I'm trying to perform a simple task, however, all the examples I'm finding do not work.
I have a very simple case:
I have a dataTable that belongs to a dataSet. It is populated from some text file that I parsed (not CSV).
I just want to save that dataTable into SQLite DB, where I have an empty table with the same schema (same number of columns and format of columns).
I have this method:
public static void UpdateTable(string dbpath, DataSet dataSet, string tableName)
{
using (SQLiteConnection db = new SQLiteConnection($"URI=file:{dbpath}"))
{
db.Open();
dataSet.AcceptChanges();
SQLiteDataAdapter DataAdapter = new SQLiteDataAdapter("select * from " + tableName, db);
DataAdapter.AcceptChangesDuringUpdate = true;
SQLiteCommandBuilder commandBuilder = new SQLiteCommandBuilder(DataAdapter);
DataAdapter.UpdateCommand = commandBuilder.GetUpdateCommand();
DataAdapter.Update(dataSet, tableName);
}
}
This is the last way I tried. All I'm getting is the same empty table in SQLite DB.
All I want to do is just save a table into a DB. I guess I can do that row by row, but I know there is a way to do that in one command.
Could you please help me write a functioning method?
I have data coming from an Excel spreadsheet, I cannot change how this comes in. Is there a solution for adding IList<IList<Object>> values to a SQL Server database instead of looping as I am reaching limit with currently 5k rows.
I was also informed that I shouldn't use injection, so any alternatives are welcomed.
public static void Load_Table()
{
// This function on_click populates the datagrid named JumpTable
// this.JumpTable.ItemsSource = null; // Clears the current datagrid before getting new data
// Pulls in the 2d table from the client sheet
IList<IList<Object>> client_sheet = Get(SetCredentials(), "$placeholder", "Client!A2:AY");
DBFunctions db = new DBFunctions();
db.postDBTable("DELETE FROM Client");
foreach (var row in client_sheet)
{
string exe = "INSERT INTO Client ([Tracker ID],[Request ID]) VALUES('" + row[0].ToString() + "','" + row[1].ToString() + "')";
db.postDBTable(exe);
}
}
Database functions
public SqlConnection getDBConnection()
{
// --------------< Function: Opens the connection to our database >-------------- \\
string connectionString = Properties.Settings.Default.connection_string; // Gets the connection source from properties
SqlConnection dbConnection = new SqlConnection(connectionString); // Creates the connection based off our source
if (dbConnection.State != ConnectionState.Open) dbConnection.Open(); // If it's not already open then open the connection
return dbConnection;
}
public DataTable getDBTable(string sqlText)
{
// --------------< Function: Gets the table from our database >-------------- \\
SqlConnection dbConnection = getDBConnection();
DataTable table = new DataTable();
SqlDataAdapter adapter = new SqlDataAdapter(sqlText, dbConnection);adapter.Fill(table);
return table;
}
public void postDBTable(string sqlText)
{
// --------------< Function: Post data to our database >-------------- \\
SqlConnection dbConnection = getDBConnection();
SqlCommand cmd = new SqlCommand(sqlText, dbConnection);cmd.ExecuteNonQuery();
}
I have worked with lots of bulk data loads in the past. There are two main ways to avoid individual inserts with SQL Server, a list of values inserted at one time or using bulk insert.
the first option to use a list of values is like this:
INSERT INTO Foo (Bar,Baz)
VALUES ('bar','baz'),
('anotherbar','anotherbaz')
In c# you would loop through your list and build the values content, however doing this with out a sql injection vulnerability is difficult.
The second option is to use bulk insert with SQL Bulk copy and a datatable. Before getting to the code below you would build a DataTable that holds all your data then use SqlBulkCopy to insert rows using Sql functionality that is optimized for inserting large amounts of data.
using (var bulk = new SqlBulkCopy(con)
{
//bulk mapping is a SqlBulkCopyColumnMapping[] and is not necessaraly needed if the DataTable matches your destination table exactly
foreach (var i in bulkMapping)
{
bulk.ColumnMappings.Add(i);
}
bulk.DestinationTableName = "MyTable";
bulk.BulkCopyTimeout = 600;
bulk.BatchSize = 5000;
bulk.WriteToServer(someDataTable);
}
These are the two framework included methods. There are other libraries that can help. Dapper is one but I am not sure how it handles inserts on the back end. Entity framework is another but it does single inserts so it is just moving the problem from your code to some one else's.
I have acquired data from my Oracle server and stored it in a DataTable (u). I have verified that the correct data has been acquired and stored.
I also have a local SQL database that has multiple tables, each with a column that carries a unique identifier.
What I would like to be able to do is compare the Oracle data stored in DataTable (u) with these various local SQL database tables, and then show the values(s) within the local SQL database tables that are identical to the values within the Oracle DataTable (u).
How would I perform this comparison while being able to tell what the matches are?
My current unfinished code:
using (OracleDataAdapter b = new OracleDataAdapter(sql2, conn))
{
conn.Open();
OracleCommand cmd2 = new OracleCommand(sql2, conn) { CommandType = CommandType.Text };
cmd2.BindByName = true;
cmd2.Parameters.Add(":user_name", OracleDbType.Varchar2).Value = cboUserName.SelectedValue;
var u = new DataTable();
b.Fill(u);
lstFunctions.DisplayMember = "Function_Name";
lstFunctions.ValueMember = "Function_Name";
lstFunctions.DataSource = u;
SqlConnection sodconnstring = new SqlConnection(#"***\SODGROUPS.sdf");
sodconnstring.Open();
SqlCommand sodcommand = new SqlCommand("SELECT * FROM tbl1, tbl2", sodconnstring);
SqlDataAdapter sodAdapter = new SqlDataAdapter(sodcommand);
var sodGroupData = new DataTable();
sodAdapter.Fill(sodGroupData);
conn.Close();
sodconnstring.Close();
}
Please let me know if you require any additional input.
Thanks.
Unless you share the schema of the tables (both oracle and SQL), it would be hard to guess a solution.
This line would return the join of tbl1 and tbl2 and I'm sure to compare the values, you would n't need a join .
SqlCommand sodcommand = new SqlCommand("SELECT * FROM tbl1, tbl2", sodconnstring)
I have a four column table in a SQL Server database. The info for the first three columns is supplied by another source. Column 4 is set to null by default.
I then have a win form with a datatable that populates with the information from the SQL Server database using the following code:
public DataTable populateFormList()
{
SqlConnection con = new SqlConnection(Properties.Settings.Default.sqlConnectionString);
SqlCommand cmd = new SqlCommand("SELECT * FROM of_formlist_raw", con);
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
DataTable dt = new DataTable();
dt.Load(reader);
return dt;
}
datagridview2.DataSource = populateFormList();
datagridview2.Refresh();
Now that works fine in obtaining my data.
The user can then make changes to the null values in column 4.
How can I easily write these changes from the datatable back into the SQL Server table?
In other words, once the on screen datatable has additional values, how can I then store the updated information back in the SQL Server database from which it was originally obtained from?
Thanks.
Try something like this and just pass (DataTable)datagridview2.DataSource as the data table:
private static void BulkInsertToSQL(DataTable dt, string tableName)
{
using (SqlConnection con = new SqlConnection(_DB))
{
SqlBulkCopy sbc = new SqlBulkCopy(con);
sbc.DestinationTableName = tableName;
//if your DB col names don’t match your data table column names 100%
//then relate the source data table column names with the destination DB cols
sbc.ColumnMappings.Add("DBAttributeName1", "DTColumnName1");
sbc.ColumnMappings.Add("DBAttributeName2", "DTColumnName2");
sbc.ColumnMappings.Add("DBAttributeName3", "DTColumnName3");
sbc.ColumnMappings.Add("DBAttributeName4", "DTColumnName4");
con.Open();
sbc.WriteToServer(dt);
con.Close();
}
}
2 options, with or without TableAdapter.
I would recommend to read this in MSDN for TableAdapter
They're using BindingSources too, which are excellent components, easy-to-use.
Without TableAdapter, read this, the "Update Records Using Command Objects" part.
I have a DataGridView that shows list of records and when I hit a insert button, a form should add a new record, edit its values and save it.
I have a BindingSource bound to a DataGridView. I pass is as a parameter to a NEW RECORD form so
// When the form opens it add a new row and de DataGridView display this new record at this time
DataRowView currentRow;
currentRow = (DataRowView) myBindindSource.AddNew();
when user confirm to save it I do a
myBindindSource.EndEdit(); // inside the form
and after the form is disposed the new row is saved and the bindingsorce position is updated to the new row
DataRowView drv = myForm.CurrentRow;
avaliadoTableAdapter.Update(drv.Row);
avaliadoBindingSource.Position = avaliadoBindingSource.Find("ID", drv.Row.ItemArray[0]);
The problem is that this table has a AUTOINCREMENT field and the value saved may not correspond the the value the bindingSource gives in EDIT TIME.
So, when I close and open the DataGridView again the new rowd give its ID based on the available slot in the undelying DB at the momment is was saved and it just ignores the value the BindingSource generated ad EDIT TIME,
Since the value given by the binding source should be used by another table as a foreingKey it make the reference insconsistent.
There's a way to get the real ID was saved to the database?
I come up with this solution
First added a GetNextID() method directly to the table model:
SELECT autoinc_next
FROM information_schema.columns
WHERE (table_name = 'Estagio') AND (column_name = 'ID')
and whener I need a new row to be added I do
EstagioTableAdapter ta = new EstagioTableAdapter ();
nextID = ta.GetNextID();
row = (DataRowView)source.AddNew();
row.Row["ID"] = nextID;
(...)
source.EndEdit();
The same thing happens with Access databases. There is a great article (with solution) here. Basically, the TableAdapter normally sends 2 queries in a batch when you save the data. The first one saves the data and the second one asks for the new ID. Unfortunately, neither Access nor SQL CE support batch statements.
The solution is to add an event handler for RowUpdated that queries the DB for the new ID.
based on my answer on concurrency violation, use da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord.
note: just change SQLiteConnection and SQLiteDataAdapter to MSSQL ones, and change the LAST_INSERT_ROWID() to SCOPE_IDENTITY()
const string devMachine = #"Data Source=C:\_DEVELOPMENT\__.NET\dotNetSnippets\Mine\TestSqlite\test.s3db";
SQLiteConnection c = new SQLiteConnection(devMachine);
SQLiteDataAdapter da = new SQLiteDataAdapter();
DataTable dt = new DataTable();
public Form1()
{
InitializeComponent();
da = new SQLiteDataAdapter("select product_id, product_name, abbrev from product", c);
var b = new SQLiteCommandBuilder(da);
da.InsertCommand = new SQLiteCommand(
#"insert into product(product_id, product_name, abbrev) values(:_product_id, :_product_name, :_abbrev);
select product_id /* include rowversion field here if you need */
from product where product_id = LAST_INSERT_ROWID();", c);
da.InsertCommand.Parameters.Add("_product_id", DbType.Int32,0,"product_id");
da.InsertCommand.Parameters.Add("_product_name", DbType.String, 0, "product_name");
da.InsertCommand.Parameters.Add("_abbrev", DbType.String, 0, "abbrev");
da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;
da.UpdateCommand = b.GetUpdateCommand();
da.DeleteCommand = b.GetDeleteCommand();
da.Fill(dt);
bds.DataSource = dt;
grd.DataSource = bds;
}
private void uxUpdate_Click(object sender, EventArgs e)
{
da.Update(dt);
}
here's the sample table on SQLite:
CREATE TABLE [product] (
[product_id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[product_name] TEXT NOT NULL,
[abbrev] TEXT NOT NULL
)
[EDIT Nov 19, 2009 12:58 PM CN] Hmm... I guess my answer cannot be used, SQLCE does not allow multiple statements.
anyway, just use my answer when you use server-based MSSQL or if you use SQLite. or perhaps, encapsulate the two statements to a function that returns scope_identity(integer):
da.InsertCommand = new SQLiteCommand(
#"select insert_to_product(:_product_id, :_product_name, :_abbrev) as product_id", c);
da.InsertCommand.Parameters.Add("_product_id", DbType.Int32,0,"product_id");
da.InsertCommand.Parameters.Add("_product_name", DbType.String, 0, "product_name");
da.InsertCommand.Parameters.Add("_abbrev", DbType.String, 0, "abbrev");
da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;
note: just change SQLiteConnection and SQLiteDataAdapter to MSSQL ones, and change the LAST_INSERT_ROWID() to SCOPE_IDENTITY()
use RowUpdated (shall work on SQLCE and RDBMS that doesn't support multi-statements):
const string devMachine = #"Data Source=C:\_DEVELOPMENT\__.NET\dotNetSnippets\Mine\TestSqlite\test.s3db";
SQLiteConnection c = new SQLiteConnection(devMachine);
SQLiteDataAdapter da = new SQLiteDataAdapter();
DataTable dt = new DataTable();
public Form1()
{
InitializeComponent();
da = new SQLiteDataAdapter("select product_id, product_name, abbrev from product", c);
var b = new SQLiteCommandBuilder(da);
da.InsertCommand = b.GetInsertCommand();
da.UpdateCommand = b.GetUpdateCommand();
da.DeleteCommand = b.GetDeleteCommand();
da.Fill(dt);
da.RowUpdated += da_RowUpdated;
bds.DataSource = dt;
grd.DataSource = bds;
}
void da_RowUpdated(object sender, System.Data.Common.RowUpdatedEventArgs e)
{
if (e.StatementType == StatementType.Insert)
{
int ident = (int)(long) new SQLiteCommand("select last_insert_rowid()", c).ExecuteScalar();
e.Row["product_id"] = ident;
}
}
private void uxUpdate_Click(object sender, EventArgs e)
{
da.Update(dt);
}
I haven't had a chance to use SQLiteConnection class but I do used SQLConnection and SQLCommand class. SqlCommand has a method ExecuteScalar that return the value of the first row and first column of your t-sql statement. You can use it to return the Auto-Identity column. Also, in SQL Server 2005 there is a keyword named OUTPUT you may also check it too.
I've come across this: all you need to do is set your autoincrement seed to -1 and have it "increment" by -1 too. This way all your datarows will have unique ids that DON'T map to anything in the real database. If you're saving your data with a DataAdapter, then after the save your datarow and any other rows with a datarelation pointing to that id will be updated