When I built my .xsd, I had to choose the columns for each table, and it made a schema for the tables, right? So how can I get that Select string to use as a base Select command for new instances of dataadapters, and then just append a Where and OrderBy clause to it as needed?
That would keep me from having to keep each DataAdapter's field list (for the same table) in synch with the schema of that table in the .xsd file.
Isn't it common to have several DataAdapters that work on a certain table schema, but with different params in the Where and OrderBy clauses? Surely one does not have to maintain (or even redundently build) the field list part of the Select strings for half a dozen DataAdapters that all work off of the same table schema.
I'm envisioning something like this pseudo code:
BaseSelectString = MyTypedDataSet.JobsTable.GetSelectStringFromSchema() // Is there such a method or technique?
WhereClause = " Where SomeField = #Param1 and SomeOtherField = #Param2"
OrderByClause = " Order By Field1, Field2"
SelectString=BaseSelectString + WhereClause + OrderByClause
OleDbDataAdapter adapter = new OleDbDataAdapter(SelectString, MyConn)
Each table has a default query (The one on top with the check on it). When you dragged your tables in to the dataset to create the query, it wrote a SQL statement which it uses to schema your table. Keep that query simple, you might not actually use it in code, and you can always edit that query to update the table schema.
Every time you open the default query it connects to your datasource and allows you to select new columns that weren't in there before. If you want to update your existing columns, delete all the columns out of the table before you attempt to open the query. When you save the query, your updated columns get added back.
Make sure your connection string has permissions to view column information.
You can add multiple queries to a single TableAdapter. TableAdapters in the designer appear sectioned with a table schema at the top, and queries on the bottom. The default query will control which columns are available for output from the other queries. To add an additional query, right click on the TableAdapter and select "Add->Query" or if you are selecting the bottom part of the TableAdapter you can select "Add Query...". Any new SQL query you create will start off with the SQL from the default query. You give each new query a method name which you can use instead of the default query's "Fill" or "GetData" methods. The assumption is that each new query will have a result set that matches the default query even though they can have different "where" clause parameters.
In short
You may have a single TableAdapter for each table, just add multiple queries.
Each additional query can have different "Where" clause parameters as long as they all return the same columns.
Related
i have a question on how to work properly with related datatables and how to actualize them properly in the backend source database.
the situation is following:
I have a SQL database (SQL Server) which is the basic data source. In here the tables can have relation with each other via foreign keys.
this SQL database i use to fill datatables via SQL queries. The resulting datatables i use to visualize the information on the formular
var query = new SqlCommand(_sqlquery, connection);
var sqlAdapter = new SqlDataAdapter(query);
sqlAdapter.Fill(dataTable);
...
Here i am using sql queries to get the needed information into my datatables for example:
SELECT * FROM [order] INNER JOIN customer ON [order].customerID =
customer.customerID;
So far so good the the visualization in the datagrid is working well.
BUT the problems are rising when trying to save modifications on the visualized data in the datagrid back in the source sql database.
I searched a lot in the internet and the solution seems to be the usage of TableAdapterManager. The problem is that i don't really understand how to connect my code i have already to visualize the data with the TableAdapterManager which can actualize the data in the sql database.
Let me add another answer - it will be easy to explain step by step. I will edit/add more details when it needed.
1. start from creating new dataset and add datatables which you need.
Add new tableadapter - it will also create datatable based on your or default query. Tableadapter may have many queries - they used to fill the datatable from db.
Start from simple query:
select * from customers
or
select *, computedField1 = 'abc', computedField2 = 123
from customers
where 1=0
This "first" query is "fake" ("where 1=0" tell you that it will never be used) and declarative, from single table, no params. Usually you never retrieve ALL data w/o parameters or joins. This query "allows" the designer to create structure and update statements, recognise primary key.
Then add another queries:
FillById
select *
from customers
where custId = #custId
FillByName
select *
from customers
where custname like (#ss + '%')
FillByRecent
select cc.*
from customers cc
inner join orders oo on cc.custId = oo.custId // get only customers with orders
where oo.orderdate > #dtFrom
You will call them in code:
taCustomer.FillByName(ds.customers, "AAA"); // all started from "AAA"
Do the same for orders
2. Create relation
Click on gray space before column custId in Customer - it will select column. Drag selected and drop on custId in Orders
3. Add do form
After compile you dataset will be added to "Data Sources" window. Select your form, drag customer from "Data Sources" window to your form - it will create grig and add components to form: dataset, binding source, tableadapter.
Add orders too.
Look at the property "datasource" and "datamember" in the grids, and binding sources:
bsCustomer will refer to ds - customer
bsOrders will refer to customer - customers_orders_relation
4. Load / Update
in form_load() call
taOrders.FillBySomething(ds.orders, ...)
taCustomer.FillByAnotherSomething(ds.customers, ...);
under Save button
taOrders.Update(ds.orders); // will update only modified content
taCustomer.Update(ds.customers);
You use join. Its combine result from 2 tables which is good for view, but confusing for update (but it still possible). Your result will be as follow. What do you expect if user will do such modification:
ord1 cust1 --> cust1a
ord2 cust1 --> cust1b
ord3 cust2
ord4 cust2
usually if need updates, create 2 tables dtOrders and dtCustomers and set relation between them in c# (I like to use designer: add Dataset, add tableAdapters for orders and customers, drag customerId from one table to another to set relation). Drop on the form Dataset, Tableadapters, bindingSources, and cofigure orderDatasource to get data from customerDatasource->yourRelation. get data for each table.
Note. Using Dataset and tableadapters is more preferable for winform applications. For web usually used more lightweight constructions, to avoid recreation havy objects on each request.
customerTableAdapter.Fill(ds.dtCustomers);
ordersTableAdapter.Fill(ds.dtOrders);
Then you can display 2 grids (master-details) or one grig with using relation.
In your datatables you will not have duplicated customers do updates will not be confusing.
TableAdapterManager may be used if you need update tables in one transaction. If it is not important you may do updates without TableAdapterManager.
in other way you may use single table adapter with join and create your own update statemtes as many as you need:
ta.updateOrderMethod1(row1) // Update orders set ... where ordId = #ordId
ta.updateOrderMethod2(row1) // spUpdateOrder(ordId = #ordId)
ta.updateCustomer(row1) // Update customer set ... where customerId = #customerId
ta.updateCustomerNameOnly(row1) // Update customer set customerName=#customerName where customerId = #customerId
I have a table T1 with an arbitrary schema. I'd like to create a second table T2 with the same schema, then port some data from T1 into T2. I can create T2 (with the identical column names and types) manually easily enough, but it's not an elegant solution.
I'm new to working with ADO.net, and just discovered the SqlDataAdapter class, which I can use to extract a DataTable containing a DataColumnCollection. If possible, I'd like to somehow use this DataColumnCollection directly to define a new table, kinda this:
DataTable dt = new DataTable("Table2", originalDataColumnCollection);
sqlDataAdapter.MakeTable(dt);
Is there an API that enables this easily? My current method involves iterating over originalDataColumnCollection to build the create table Table2 (...) string, then submitting that via SqlCommand.
Update:
I just noticed that I can't get the SQL type from originalDataColumnCollection, but instead if gives me back a C# type. Maybe this method won't work at all for ensuring an identical column structure?
You can execute an empty select command and store it to a new table so a table is created with the same schema as the old table and you can do what you want later with it.
select * into new_table from old_table where 1 = 0
executing this sql command will create an empty table with the same schema as the table you want.
For a homework task I have to develop a C# application to interface with a SQL Server database file (.mdf), providing a dataGridView to browse the contents, and several buttons to execute queries.
I have been going well, found how to add queries to my table adapter, how to call them etc.
Now I am having problems making a query that returns the maximum pay in hourlyPayRate.
I have a database employee that contains the following attributes: employeeID, name, position, hourlyPayRate.
My query is
SELECT MAX(hourlyPayRate)
FROM employee
I right click employeeTableAdapter, click "Add Query...", name it Max and put in the query. When I click okay I get the following error message:
The schema returned by the new query differs from the base query.
The query executes correctly in the query builder, it is only when I click "OK" to save it that I receive the error.
Looking around SE there are no definitive answers to this question.
Thanks, Michael.
The solution has been found, for all those wondering.
The problem is that the query returns a table which has a different number of columns that the database.
Usually in most DBMS's this is not an issue, but for some reason Visual Studio was having none of that.
The solution was this query:
SELECT employeeID, name, position, hourlyPayRate
FROM employee
WHERE (hourlyPayRate =
(SELECT MAX(hourlyPayRate) AS MaxRate
FROM employee AS TempTable))
And then trimming the unneeded from the result as you like. For me this was as simple as having a label that derived it's data only from the hourlyPayRate attribute.
The actual reason for this is that the base QUERY returns more or less columns than the QUERY you are adding . Visual Studio cares about this ; and should not after all it is a new query.
NOTE: We are talking query columns not Database Table Columns. The Error regards Base Query - for example a fill, and perhaps you want to have a fill by and hide the field of a foreignID - because your base query outputs that column, and your added query does not - the new query differs from the base in that the columns are not the same. (I think this is done to ensure bound objects are properly bound; but I really do not know (think datagridview.)
So for example
Fill() Query
SELECT Id, Name, FKtblThing_ID
FROM ITEMS
Adding this query works..
FillByID() Query
SELECT Id, Name, FKtblThing_ID
FROM ITEMS
WHERE (FKtblThing_ID = #FKtbl_ThingID)
If instead you tried this - it would not work.
FillByID() Query
SELECT Id, Name
FROM ITEMS
WHERE (FKtblThing_ID = #FKtbl_ThingID)
This is the error you would receive:
The schema returned by the new query differs from the base query.
Everyone is wrong here, you don't edit the sql in the table adapter but edit the sql in the dataset.
I have a database with several tables and I am using the following query to return a record that matches a string(Name).
In the MHP table there is a Name field(primary key), Num_Sites and a few more, but these are the only ones I am concerned with.
In the MHP_Parcel_Info table there are many fields with one of them being Name(foreign key). There is a parcel_id field and in some case there may only be one parcel for one name, but there may also be many parcels for a Name.
As it is now my query will return one of the rows for instances where there are multiple parcels for a name.
What I would like to do is: if there is more than one parcel for a Name, have all the parcels put into a list(so I can display in listbox on form).
My SQL skills are limited and I don’t know how I would go about doing something like this.
SELECT MHP_Parcel_Info.*, MHP.NUM_SITES FROM MHP_Parcel_Info INNER JOIN MHP ON " +
"(MHP_Parcel_Info.MHP_NAME = MHP.MHP_NAME) WHERE MHP_Parcel_Info.MHP_NAME='" + strValue + "'"
This is not something you can do directly in SQL. There's no way to select data in a parent/child structure in a SQL query - you have to do that as a post-processing step.
Since this is tagged as C# and Winforms I'm assuming this is from inside a .Net app. You will need to execute the query as you have it above, then in C# you can use the LINQ GroupBy extension method on the result to group the results into a list of IGrouping objects which use the name as the key, and has all of the parcel info as the items in the list.
Even better, if you are using (or can use) LINQ to SQL or Entity Framework you can just write a linq query that fetches the data from the database and does the grouping all at once.
I use asp.net 4 and DataSets for accessing the database. There are two tables with one-to-one relationship in the database. It means that both tables have the same column as a primary key (say Id), and one of tables has #identity on this column set.
So in general if we want to insert, we insert first into the first table, than insert into the second table with id.table2 = id of the corresponding record in table1.
I can imagine how to achieve this using stored procedure (we would insert into the first table and have id as an out parameter and then insert into the second table using this id, btw all inside one transaction).
But is there a way to do it without using a stored procedure? May be DataSets \ DataAdapters have such functionality built in?
Would appreciate any help.
Today it is so quiet here... Ok if anybody is also looking for such a solution, I've found a way to do it.
So our main problem is to get the id of the newly created record in the first table. If we're able to do that, after that we simply supply it to the next method which creates a corresponding record in the second table.
I used a DataSet Designer in order to enjoy the code autogeneration feature of the VS. Let's call the first table TripSets. In DataSet Designer right click on the TripSetsTableAdapter, then Properties. Expand InsertCommand properties group. Here we need to do two things.
First we add a new parameter into the collection of parameters using the Parameters Collection Editor. Set ParameterName = #TripId, DbType = Int32 (or whatever you need), Direction = Output.
Second we modify the CommandText (using Query Builder for convenience). Add to the end of the command another one after a semicolon like that:
(...);
SELECT #TripId = SCOPE_IDENTITY()
So you will get something like this statement:
INSERT INTO TripSets
(Date, UserId)
VALUES
(#Date,#UserId);
SELECT #TripId = SCOPE_IDENTITY()
Perhaps you will get a parser error warning, but you can just ignore it. Having this configured now we are able to use in our Business logic code as follows:
int tripId;
int result = tripSetsTableAdapter.Insert(tripDate, userId, out tripId);
// Here comes the insert method into the second table
tripSetTripSearchTableAdapter.Insert(tripId, amountPersons);
Probably you will want to synchronize this operations somehow (e.g. using TransactionScope) but it is completely up to you.