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.
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 am using a SQLite database. Since drop/rename/reorder columns are not supported in SQLite (using alter table commands), I am writing customized methods for the following tasks: (taking backup of existing table, then creating a new table with matching requirements and so on..) which is described in several other threads.
The following are the DB operations:
DROP columns
RENAME (columns and datatype)
ADD columns
REORDER columns.
I am wondering in what order should these operations be done? My confusion is mainly around whether drop should come before rename columns or the other way?
Also I need some pointers on how to rename columns including datatype and moving data around?
Any thoughts?
First, create a backup of your database in case something goes wrong during the following instructions.
The order of operations should be CREATE newtable, INSERT INTO newtable, DROP oldtable.
Create a new table with the correct column names and datatypes for each column. Then just do something like this:
INSERT INTO new_table(columns, ...)
(SELECT columns, ...
FROM old_table)
You may need to perform casts on different datatypes if the new datatype isn't directly compatible with the old datatype.
You will need to make sure that the columns you select from your old table are in the same order as defined by the columns in your new table in your INSERT INTO statement.
After data is inserted into your new table, you can then verify that all data has been inserted correctly. Be sure to update any foreign key references in your other tables to avoid any issues with foreign key constraints.
Then you can drop the old table and rename the new table to the name of your old table:
DROP old_table;
ALTER TABLE new_table_name RENAME TO old_table_name;
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.
We have an application that creates a number of Visual Foxpro (DBF) tables. Each of those tables have a different schema, but they all contain a known date field.
I've been asked to create another application (in C#) that will copy the last week's worth of data from each table to a new table (in a different folder to the source tables). The distinct tables will remain (e.g. if there are three source tables, there will be three destination tables).
Over time the tables may change (e.g. new fields added), so I can't make assumptions about table structure (apart from the existence of the aforementioned date field).
What's the easiest/best way to take the data from one table and create a new table with the same structure?
I know how to query the tables to extract the data (e.g. fill a DataSet with the last week's records). However, I'm thinking there must be a better way of creating a new table and filling it with the results than manually parsing all the field information in the schema and using that to recreate the the destination table.
Working with FoxPro seems to be different enough from SQL Server to give me a headache at each turn, so I need some guidance on my approach.
The production machine has the VFP 9 OLEDB driver installed on it. If possible, we'd prefer not to have to install much else.
To get an exact copy of the data, table, and records, you can do via a single SQL-Select via
OleDbConnection oConn = new OleDbConnection("Provider=VFPOLEDB.1;Data Source=C:\\SomePath");
OleDbCommand oCmd = new OleDbCommand();
oCmd.Connection = oConn;
oCmd.Connection.Open();
oCmd.CommandText = "select * from SomeTable where someCondition into table YourNewTable";
oCmd.ExecuteNonQuery();
oConn.Close();
Your where clause could be almost anything, and the Into TABLE clause tells the VFP engine to create the result set AS A NEW TABLE, so no need to explicitly declare types, columns, etc, query data from one and push into another...
One issue of consideration... Verify the user access to obviously be able to create, read, write wherever you are trying to create the new table. You can even specify a fully qualified path, such as C:\SomeOtherPath\Monthly\MyTable1 if need be...
Try something like this (note written in VB.NET and converted use www.developerfusion.co.uk/tools ):
using System.Data.OleDb;
using System.IO;
static class Module1
{
public static void Main()
{
OleDbConnection oConn = new OleDbConnection("Provider=VFPOLEDB.1;Data Source=C:\\");
OleDbCommand oCmd = new OleDbCommand();
{
oCmd.Connection = oConn;
oCmd.Connection.Open();
// Create a sample FoxPro table
oCmd.CommandText = "CREATE TABLE Table1 (FldOne c(10))";
oCmd.CommandType = CommandType.Text;
oCmd.ExecuteNonQuery();
}
oConn.Close();
oConn.Dispose();
oCmd.Dispose();
}
}
You can simply do a:
select * from myTable into table newTable [database dbName]
as DRapp showed. However you may want to get indexes as well (if any) (BTW creating indexes via VFPOLEDB is not supported directly but you can do so using ExecScript() function). Then the easiest would be to copy the table's DBF, CDX (and FPT) files. VFP is file based.
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.