3

I know this questions is rather general, but I have searched the whole day and I haven't been able to find the proper way to do this.

Here is my code to insert some 100 000 dummy records into an MDB file using C#.

OleDbConnection con = new OleDbConnection();
string dbProvider = "PROVIDER=Microsoft.Jet.OLEDB.4.0;";
string dbSource = "Data Source = D:/programming/sample.mdb";
con.ConnectionString = dbProvider + dbSource;
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = con;
cmd.CommandText = "INSERT INTO tblBooks (Title, Price, Tag, Author) VALUES (@title, @price, @tag, @author)";
cmd.Parameters.AddWithValue("@title", "Dummy Text 1");
cmd.Parameters.AddWithValue("@price", 10);
cmd.Parameters.AddWithValue("@tag", "Dummy Text 2");
cmd.Parameters.AddWithValue("@author", "Dummy Text 3");
con.Open();

for (int i = 0; i < 100000; i++)
{
    cmd.ExecuteNonQuery();    
}

 con.Close();

This code takes around a minute to run. Is this normal? What is the proper way to do this faster?

Gord Thompson
  • 116,920
  • 32
  • 215
  • 418
Vahid
  • 5,144
  • 13
  • 70
  • 146

3 Answers3

5

If you happen to already have a "numbers table" available (with at least 100,000 rows) then Remou's answer will almost certainly get the job done fastest. I tried a quick test in VBA and the query

Dim t0 As Single
t0 = Timer
CurrentDb.Execute _
        "INSERT INTO tblBooks (Title, Price, Tag, Author) " & _
        "SELECT 'Dummy Text 1', 10, 'Dummy Text 2', 'Dummy Text 3' FROM Numbers", _
        dbFailOnError
Debug.Print Format(Timer - t0, "0.0") & " seconds"

created the 100,000 rows in less than 2 seconds.

However, if you don't already have a numbers table then you would need to create that table first, so if this is a one-time requirement then you might be better off just optimizing your code.

The code as posted in your question took 45 seconds on my machine. Two enhancements that significantly reduced the execution time were:

  1. Use .Prepare(): that alone reduced the elapsed time to 16 seconds

  2. Use an OleDbTransaction: Wrapping the inserts in a transaction (in addition to using .Prepare()) further reduced the elapsed time to 10 seconds.

The modified code looks like this:

var sw = new System.Diagnostics.Stopwatch();
sw.Start();
OleDbConnection con = new OleDbConnection();
string dbProvider = "PROVIDER=Microsoft.Jet.OLEDB.4.0;";
string dbSource = "Data Source = C:/Users/Gord/Desktop/speed.mdb";
con.ConnectionString = dbProvider + dbSource;
con.Open();
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = con;
cmd.CommandText = "INSERT INTO tblBooks (Title, Price, Tag, Author) VALUES (?,?,?,?)";
cmd.Parameters.Add("?", OleDbType.VarWChar, 255);
cmd.Parameters.Add("?", OleDbType.Currency);
cmd.Parameters.Add("?", OleDbType.VarWChar, 255);
cmd.Parameters.Add("?", OleDbType.VarWChar, 255);
cmd.Prepare();
cmd.Parameters[0].Value = "Dummy Text 1";
cmd.Parameters[1].Value = 10;
cmd.Parameters[2].Value = "Dummy Text 2";
cmd.Parameters[3].Value = "Dummy Text 3";
OleDbTransaction trn = con.BeginTransaction();
cmd.Transaction = trn;
for (int i = 0; i < 100000; i++)
{
    cmd.ExecuteNonQuery();
}
trn.Commit();
con.Close();
sw.Stop();
Console.WriteLine(String.Format("{0:0.0} seconds", sw.ElapsedMilliseconds / 1000.0));
Gord Thompson
  • 116,920
  • 32
  • 215
  • 418
  • 1
    Thank you so much, this really helped me big time. I managed to decrease the time to 10.1 secs, I'll look into the Numbers Table next. – Vahid Feb 18 '14 at 12:17
3

(Bonus answer:)

In case anyone is wondering whether an OleDbDataAdapter can insert the rows faster, it seems not. The following code does create the 100,000 records...

var da = new OleDbDataAdapter("SELECT [ID], [Title], [Price], [Tag], [Author] FROM [tblBooks] WHERE False", con);
var cb = new OleDbCommandBuilder(da);
cb.QuotePrefix = "["; cb.QuoteSuffix = "]";
var dt = new System.Data.DataTable();
da.Fill(dt);
for (int i = 0; i < 100000; i++)
{
    System.Data.DataRow dr = dt.NewRow();
    dr["Title"] = "Dummy Text 1";
    dr["Price"] = 10;
    dr["Tag"] = "Dummy Text 2";
    dr["Author"] = "Dummy Text 3";
    dt.Rows.Add(dr);
}
da.Update(dt);

...but it takes about 30% longer to run than the original code in the question.

Gord Thompson
  • 116,920
  • 32
  • 215
  • 418
  • Thanks Gord for all the knowledge you shared with us. What about updates? I have raised another question regarding this. http://stackoverflow.com/questions/21859395/how-to-update-thousands-of-records-in-an-mdb-file-in-c-sharp – Vahid Feb 18 '14 at 16:20
1

You can use a numbers table to add multiple identical rows, for example:

INSERT INTO aTable ( aText, aNumber )
SELECT @param1 , @param2 
FROM Numbers
WHERE Numbers.Number<1000

The Numbers table is:

Number
0
1
2
<...>
Fionnuala
  • 90,370
  • 7
  • 114
  • 152
  • You mean, first I need to create the blank records and populate them with integer numbers and after that I can UPDATE the other fields with the proper values? – Vahid Feb 18 '14 at 11:12
  • 1
    I mean you need a numbers table (http://stackoverflow.com/questions/1393951/what-is-the-best-way-to-create-and-populate-a-numbers-table). It is very useful. In the example above, the numbers table is simply used to get the required number of rows. The data in the numbers table is not used other than as a count. You can see that the select data includes two parameters, but not the single field that is held in the numbers table. – Fionnuala Feb 18 '14 at 11:15
  • Sorry but I'm having trouble understanding this :( I need to create an MDB file that has 100 000 rows and several fields, and I need to populate them with data, How can I implement your method in my code. Sorry if I'm missing the obvious. – Vahid Feb 18 '14 at 11:21
  • First, create or import a numbers table, use the numbers table in your query. The two tables every database should have are a numbers table and a date table. All the numbers table does in the example is eliminate the need for `for (int i = 0; i < 100000; i++)` You can have as many fields as you like in the insert table, but all fields will be identical from row to row. A numbers table is reusable. – Fionnuala Feb 18 '14 at 11:24