1

I recently asked a question about how to insert 100,000 records in an MDB file in C#. The given answers reduced the required time from 45 secs to 10 secs. It was even possible to be reduced to 2~3 secs using Number Tables.

Now I have problem updating a similar database. I don't want to actually update 100,000 records in this case but around 10,000 records from the already created MDB file with 100,1000 records.

Here is my code:

Stopwatch sw = new Stopwatch();
sw.Start();
OleDbConnection con = new OleDbConnection();
string dbProvider = "PROVIDER=Microsoft.Jet.OLEDB.4.0;";
string dbSource = "Data Source = D:/programming/sample.mdb";
con.ConnectionString = dbProvider + dbSource;
con.Open();
string query = "SELECT * FROM tblBooks";

DataSet ds = new DataSet();
OleDbDataAdapter da = new OleDbDataAdapter(query, con);
da.Fill(ds,"Books Table");
for (int i = 0; i < 10000; i++)
{
    ds.Tables[0].Rows[i][1] = "Book" + i.ToString();    
}


OleDbCommandBuilder cb = new OleDbCommandBuilder(da);
da.UpdateCommand = cb.GetUpdateCommand();
da.Update(ds, "Books Table");

con.Close();
sw.Stop();
Console.WriteLine(String.Format("{0:0.0} seconds", sw.ElapsedMilliseconds / 1000.0));

Updating 10000 records (only one field) took around 24 secs!

I have another code that performs well:

Stopwatch sw = new Stopwatch();
sw.Start();
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;
con.Open();
cmd.CommandText = "UPDATE tblBooks SET [Title] = @title";
cmd.Parameters.AddWithValue("@title", "Book");
cmd.ExecuteNonQuery();
con.Close();
sw.Stop();
Console.WriteLine(String.Format("{0:0.0} seconds", sw.ElapsedMilliseconds / 1000.0));

I found out that when I use the above code I'm able to update the whole table (100,000 records) in less than a second (0.4 sec). But in this version, I don't know how to be selective and only update part of the table and how to assign different values to each record (Book 1, Book 2...). I mean I want to be able to update for example from record 4000 to 14000 in the table and assign Book 1, Book 2 and ... to title field.

Community
  • 1
  • 1
Vahid
  • 5,144
  • 13
  • 70
  • 146
  • Have you considered using Transactions ? See this other question to get some samples about how to use this : http://stackoverflow.com/questions/18183297/increase-performance-on-oledb-insert-into-statement-begintransaction-committran – Larry Feb 18 '14 at 16:22
  • @Remou Absolutely not, I just didn't understand the Numbers Table in the previous example. Can you please help me in the right direction? – Vahid Feb 18 '14 at 16:33
  • @Larry I saw it but there is not an example for UPDATE. – Vahid Feb 18 '14 at 16:37
  • It's exactly the same as if your command do an insert. Just enclose it using transaction object the same way as in the accepted answer of your previous question. – Larry Feb 18 '14 at 16:40
  • @Larry Will it decrease the run time to less than 1 sec, I'm starting to have doubt about my application architecture actually. My application sends queries to database per each operation (delete a line, add some shape, etc) and updates the database. So using this architecture, this shouldn't take more than a fraction of a second. Am I on the wrong route? – Vahid Feb 18 '14 at 16:44
  • 1
    I dont think you are on the wrong route. I understood you dont want to update the whole table at each operation. You realize that a single SQL query is much faster than a dataadapter which launch loads of those. So you want to be selective by using the fewest possible SQL statements and this is the right path. I suggest you to look some hints about the "WHERE" SQL statement: this is what you need. – Larry Feb 18 '14 at 20:35
  • @Larry By wrong route, I mean saving every change to database as the user works with the application. Is this normal at all in software development? – Vahid Feb 18 '14 at 21:48
  • 1
    Not all application works this way. Sometimes, the data persistence is defered to a dedicated thread, or a service, that saves the data in the background to database, files... But backing changes to the database as the user works is very common. – Larry Feb 18 '14 at 21:52
  • @Larry Thank you so much Larry, I don't have much experience in desktop development, so sometimes it bothers me that what I'm doing is the right thing or not. Thank you for the clarification. – Vahid Feb 18 '14 at 22:10
  • 1
    You're welcome. The Gord Thompson answer is definitely showing you the way. – Larry Feb 18 '14 at 22:12

1 Answers1

3

Just to give you another option to consider (as suggested by 4dmonster in a comment to your earlier question) here is the DAO Recordset way of updating an Access database. It "skips" the first 4,000 records and updates the next 10,000 as "Book 1", "Book 2", ....

In many cases DAO is still the fastest way to perform row-by-row updates on an Access database. On my machine the following code takes 2.5 seconds to execute.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DAO;

namespace daoConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var sw = new System.Diagnostics.Stopwatch();
            sw.Start();

            // This code requires the following COM reference in your project:
            //     Microsoft DAO 3.6 Object Library
            //
            var dbe = new DBEngine();
            Database db = dbe.OpenDatabase(@"C:\Users\Gord\Desktop\speed.mdb");
            Recordset rst = db.OpenRecordset(
                    "SELECT TOP 4001 ID FROM tblBooks ORDER BY ID",
                    RecordsetTypeEnum.dbOpenSnapshot);
            rst.MoveLast();
            int startID = rst.Fields["ID"].Value;
            rst.Close();
            rst = db.OpenRecordset(
                    String.Format(
                        "SELECT TOP 10000 Title FROM tblBooks WHERE ID >= {0} ORDER BY ID", 
                        startID),
                    RecordsetTypeEnum.dbOpenDynaset);
            int i = 1;
            while (!rst.EOF)
            {
                rst.Edit();
                rst.Fields["Title"].Value = String.Format("Book {0}", i++);
                rst.Update();
                rst.MoveNext();
            }
            rst.Close();

            sw.Stop();
            Console.WriteLine(String.Format("{0:0.0} seconds", sw.ElapsedMilliseconds / 1000.0));
        }
    }
}
Gord Thompson
  • 116,920
  • 32
  • 215
  • 418
  • Thank you Gord, you've really saved me today, I learned a lot from you. Just one more question, what if I use SQL Server Express, will it make any difference in speed? – Vahid Feb 18 '14 at 22:17
  • 3
    @Vahid Like so many similar questions, the answer is "It depends." For row-by-row updates, using SQL Server would probably not be significantly faster. However, T-SQL (the SQL dialect used by SQL Server) would give you several more options for *avoiding* row-by-row operations, such as using a recursive CTE (Common Table Expression) to generate a numbers table "on the fly". Certainly when dealing with large numbers of rows where speed is important I would personally prefer to work with SQL Server. – Gord Thompson Feb 18 '14 at 22:34