DataAdapter.Update() 性能

jle*_*ach 5 c# sqlite performance ado.net

我有一个相对简单的例程,它查看媒体文件的数据库条目,计算宽度、高度和文件大小,并将它们写回到数据库中。

数据库是 SQLite,使用 System.Data.SQLite 库,处理约 4000 行。我将所有行加载到 ADO 表中,使用新值更新行/列,然后运行 ​​adapter.Update(table); 在上面。

从数据库表加载数据集大约需要半秒,更新所有行的图像宽度/高度并从 FileInfo 获取文件长度可能需要 30 秒。美好的。

适配器.Update(表); 命令在附近某处运行需要 5 到 7 分钟。

这似乎太过分了。ID 是一个 PK INTEGER,因此 - 根据 SQLite 的文档,它本质上是索引的,但即便如此,我还是忍不住想,如果我为每个单独的更新运行单独的更新命令,这会完成得更快。

我曾认为 ADO/适配器的级别相对较低(无论如何与 ORM 相比),这种糟糕的性能让我感到惊讶。谁能解释一下为什么根据本地放置的 SQLite 数据库更新一批约 4000 条记录需要 5-7 分钟?

顺便说一句,是否有某种方法可以“窥视”ADO 如何处理这个问题?内部库逐步执行或...?

谢谢

public static int FillMediaSizes() {
        // returns the count of records updated

        int recordsAffected = 0;

        DataTable table = new DataTable();
        SQLiteDataAdapter adapter = new SQLiteDataAdapter();

        using (SQLiteConnection conn = new SQLiteConnection(Globals.Config.dbAppNameConnectionString))
        using (SQLiteCommand cmdSelect = new SQLiteCommand())
        using (SQLiteCommand cmdUpdate = new SQLiteCommand()) {

            cmdSelect.Connection = conn;
            cmdSelect.CommandText =
                "SELECT ID, MediaPathCurrent, MediaWidth, MediaHeight, MediaFilesizeBytes " +
                "FROM Media " +
                "WHERE MediaType = 1 AND (MediaWidth IS NULL OR MediaHeight IS NULL OR MediaFilesizeBytes IS NULL);";

            cmdUpdate.Connection = conn;
            cmdUpdate.CommandText =
                "UPDATE Media SET MediaWidth = @w, MediaHeight = @h, MediaFilesizeBytes = @b WHERE ID = @id;";

            cmdUpdate.Parameters.Add("@w", DbType.Int32, 4, "MediaWidth");
            cmdUpdate.Parameters.Add("@h", DbType.Int32, 4, "MediaHeight");
            cmdUpdate.Parameters.Add("@b", DbType.Int32, 4, "MediaFilesizeBytes");
            SQLiteParameter param = cmdUpdate.Parameters.Add("@id", DbType.Int32);
            param.SourceColumn = "ID";
            param.SourceVersion = DataRowVersion.Original;

            adapter.SelectCommand = cmdSelect;
            adapter.UpdateCommand = cmdUpdate;

            try {
                conn.Open();
                adapter.Fill(table);
                conn.Close();
            }
            catch (Exception e) {
                Core.ExceptionHandler.HandleException(e, true);
                throw new DatabaseOperationException("", e);
            }

            foreach (DataRow row in table.Rows) {

                try {

                    using (System.Drawing.Image img = System.Drawing.Image.FromFile(row["MediaPathCurrent"].ToString())) {

                        System.IO.FileInfo fi;

                        fi = new System.IO.FileInfo(row["MediaPathCurrent"].ToString());

                        if (img != null) {

                            int width = img.Width;
                            int height = img.Height;
                            long length = fi.Length;

                            row["MediaWidth"] = width;
                            row["MediaHeight"] = height;
                            row["MediaFilesizeBytes"] = (int)length;
                        }
                    }
                }
                catch (Exception e) {
                    Core.ExceptionHandler.HandleException(e);
                    DevUtil.Print(e);
                    continue;
                }
            }                


            try {
                recordsAffected = adapter.Update(table);
            }
            catch (Exception e) {
                Core.ExceptionHandler.HandleException(e);
                throw new DatabaseOperationException("", e);
            }


        }

        return recordsAffected;
    }
Run Code Online (Sandbox Code Playgroud)

Mar*_*ark 5

使用 Connection.BeginTransaction() 加速 DataAdapter 更新。

conn.Open() 'open connection
Dim myTrans As SQLiteTransaction
myTrans = conn.BeginTransaction() 
'Associate the transaction with the select command object of the DataAdapter
objDA.SelectCommand.Transaction = myTrans 

objDA.Update(objDT)

Try
    myTrans.Commit()
Catch ex As Exception
    myTrans.Rollback()
End Try
conn.Close()
Run Code Online (Sandbox Code Playgroud)

这大大加快了更新速度。


Lia*_*iam 2

从数据库表加载数据集大约需要半秒

这是单个 SQL 语句(因此速度很快)。执行 SQL SELECT,填充数据集,完成。

更新所有行的图像宽度/高度并从 FileInfo 获取文件长度可能需要 30 秒。美好的。

这是更新内存中的数据(所以速度也很快),更改数据集中的 x 行,根本不与 SQL 对话。

adapter.Update(table);命令的运行时间约为 5 到 7 分钟。

这将为每个更新的行运行 SQL 更新。这就是为什么它很慢。

但即便如此,我还是忍不住想,如果我为每个单独的更新运行单独的更新命令,这会完成得更快。

无论如何,这基本上就是它正在做的事情!


来自MSDN

更新是逐行执行的。对于每个插入、修改和删除的行,Update 方法确定对其执行的更改类型(插入、更新或删除)。根据更改的类型,执行插入、更新或删除命令模板以将修改的行传播到数据源。当应用程序调用 Update 方法时,DataAdapter 将检查 RowState 属性,并根据 DataSet 中配置的索引顺序,为每行迭代执行所需的 INSERT、UPDATE 或 DELETE 语句。


有什么方法可以“了解”ADO 是如何处理这个问题的吗?

是:在 Visual Studio 2012 中调试 .NET Framework 源代码?