【问题标题】:Is datareader quicker than dataset when populating a datatable?填充数据表时,datareader 比 dataset 快吗?
【发布时间】:2010-09-24 23:32:44
【问题描述】:

这样会更快。

1) 循环数据读取器并创建基于自定义行和列的填充数据表

2) 或者创建一个 dataAdapter 对象并只是(.Fill)一个数据表。

在动态创建数据表时,数据读取器的性能是否仍然适用?

【问题讨论】:

    标签: .net performance datatable datareader dataadapter


    【解决方案1】:

    DataAdapter 在后台使用 DataReader,因此您的体验可能是相同的。

    DataAdapter 的好处是您可以省去大量需要维护的代码。

    这场辩论有点宗教问题,所以一定要环顾四周,决定什么最适合你的情况:

    【讨论】:

      【解决方案2】:

      假设您确实希望从数据库中返回所有数据,那么在数据库和网络上花费的时间几乎肯定会使进程内在内存中填充数据结构所花费的时间相形见绌。

      是的,在某些情况下,您可能会通过使用 DataReader 节省少量费用 - 特别是如果您想流式传输数据,它可能很有用 - 但如果您确实需要它,我会坚持使用最简单的代码.如果您认为 DataSet 人口正在导致严重的性能问题,请对其进行分析并然后尝试改进它。

      【讨论】:

        【解决方案3】:

        您的选项 #1 会更慢。但是,有一种比手动添加自定义行更好的方法将数据读取器转换为数据表:

        DataTable dt = new DataTable();
        
        using (SqlConnection conn = GetOpenSqlConnection())
        using (SqlCommand cmd = new SqlCommand("SQL Query here", conn)
        using (IDataReader rdr = cmd.ExecuteReader())
        {
            dt.Load(rdr);
        }
        

        我无法评论这与使用 .Fill() 之间的区别。

        【讨论】:

        • 谢谢!我正在寻找如何从数据读取器加载数据表,因为我有一个返回多个表的存储过程,但我只需要从一个输出表中“填充”一个数据表。
        【解决方案4】:

        我不能说填充数据表本身,但使用数据读取器是最有效的读取方法。

        【讨论】:

        • 我一直想知道这是否取决于我们如何处理数据。由于DataReader依赖于数据库服务器来缓冲信息,所以对于一个大的结果集,如果我们的计算很复杂,比如构建一个网络图,每增加一个新节点就变得更难,它会阻塞数据库。是吗?跨度>
        【解决方案5】:

        数据读取器更快。如果您使用的是 2.0+,您可能甚至不必使用数据表。您可以使用对象的通用列表。

        【讨论】:

        • 您 2008 年的声明在 2015 年底对我有帮助 :) SqlDataAdapter 和 SqlDataReader 需要 6.x 分钟来加载 DataTable,但 Linq 只需 1.7 秒来加载列表(56460 行)。
        • @PalaniKumar:你能告诉我,你将如何使用数据阅读器直接加载带有列表的存储过程结果??
        • @Learning,我使用 EntityFramework 将存储过程作为对象列表。如果您想将 datareader 转换为列表,请在此处查看 stackoverflow.com/questions/1464883/…
        【解决方案6】:

        当您需要显示加载数据的进度时,使用 DataReader 非常好。在 DataSet 中,您不能在加载数据的过程中做某事。

        另一方面,DataSet 是一体式对象。所以 DataSet 慢得多。 DataReader 可以在代码中数据操作非常缓慢的地方为您提供额外的提升。在这些地方将其从 DataSet 更改为 DataReader。 DataReader 占用的内存空间也更少。

        当然,编写好的 DataReader 需要更多时间,但这是值得的。例如,当您播放从数据库中获取的图像或音乐时。

        More on this topic in MSDN Magazine

        【讨论】:

          【解决方案7】:

          对于许多这样的问题,答案是:取决于。

          如果您事先不知道数据的结构并且正在动态创建 TableAdapter,那么动态 DataTable 会更有效。创建 TableAdapter 涉及大量代码生成。

          但是,如果您事先知道数据的结构,那么问题就变成了,我需要多少功能?

          如果您需要一个完整的 CRUD 实现,那么使用 TableAdapter 可以获得一些效率,而不是自己编写所有的 CRUD 代码。此外,TableAdapter 实现还可以(不是很好)。如果您需要更高效的东西,那么您最好使用 nHibernate 或其他一些 ORM。

          如果您不需要完整的 CRUD 实现(即,这是一个只读解决方案)并且预先知道您的数据结构,那么您必须针对一个 TableAdapter 只读实现来测试其效率动态生成的数据表。如果我是一个赌徒,我会把钱放在 TableAdapter 实现上,因为您绑定数据一次并多次读取它。

          【讨论】:

            【解决方案8】:

            使用DataReaderRead,这是一种只进、一次一行的方法,它按顺序读取数据,以便在连接时一读取记录就可以获取记录,将是最好的内存和性能。

            也就是说,在这两种方法之间,我发现IDataAdapter.FillDataTable.Load 快得多。当然,这取决于实现。这是我在here 发布的两者之间的基准:

            public DataTable Read1<T>(string query) where T : IDbConnection, new()
            {
                using (var conn = new T())
                {
                    using (var cmd = conn.CreateCommand())
                    {
                        cmd.CommandText = query;
                        cmd.Connection.ConnectionString = _connectionString;
                        cmd.Connection.Open();
                        var table = new DataTable();
                        table.Load(cmd.ExecuteReader());
                        return table;
                    }
                }
            }
            
            public DataTable Read2<S, T>(string query) where S : IDbConnection, new() 
                                                       where T : IDbDataAdapter, IDisposable, new()
            {
                using (var conn = new S())
                {
                    using (var da = new T())
                    {
                        using (da.SelectCommand = conn.CreateCommand())
                        {
                            da.SelectCommand.CommandText = query;
                            da.SelectCommand.Connection.ConnectionString = _connectionString;
                            DataSet ds = new DataSet(); //conn is opened by dataadapter
                            da.Fill(ds);
                            return ds.Tables[0];
                        }
                    }
                }
            }
            

            第二种方法总是优于第一种。

            Stopwatch sw = Stopwatch.StartNew();
            DataTable dt = null;
            for (int i = 0; i < 100; i++)
            {
                dt = Read1<MySqlConnection>(query); // ~9800ms
                dt = Read2<MySqlConnection, MySqlDataAdapter>(query); // ~2300ms
            
                dt = Read1<SQLiteConnection>(query); // ~4000ms
                dt = Read2<SQLiteConnection, SQLiteDataAdapter>(query); // ~2000ms
            
                dt = Read1<SqlCeConnection>(query); // ~5700ms
                dt = Read2<SqlCeConnection, SqlCeDataAdapter>(query); // ~5700ms
            
                dt = Read1<SqlConnection>(query); // ~850ms
                dt = Read2<SqlConnection, SqlDataAdapter>(query); // ~600ms
            
                dt = Read1<VistaDBConnection>(query); // ~3900ms
                dt = Read2<VistaDBConnection, VistaDBDataAdapter>(query); // ~3700ms
            }
            sw.Stop();
            MessageBox.Show(sw.Elapsed.TotalMilliseconds.ToString());
            

            Read1 看起来更好,但数据适配器性能更好(不要混淆一个数据库优于另一个,查询都是不同的)。两者之间的差异取决于查询。原因可能是Load 需要在添加行时逐行检查各种约束from the documentation(它是DataTable 上的一种方法),而Fill 位于专门为此而设计的DataAdapters 上——快速创建DataTables .

            【讨论】:

              【解决方案9】:

              视情况而定。如果您希望一次获取所有记录,DataAdapter 可以快 2 到 25 倍。当您只需要一列或两列并希望一次更改一个时,数据读取器非常好,但是执行时间如预期的那样很慢。 DA 最大的问题是缺乏异步方法——然而,MS 没有计划使 DA 异步。虽然 DA 在后台使用 DR,但它已经被磨练到完美,并且在 C# 中使用 DR 编写自己的循环肯定会更慢。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2019-12-25
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2010-11-12
                • 1970-01-01
                • 2012-07-25
                相关资源
                最近更新 更多