【问题标题】:SqlDataAdapter vs SqlDataReaderSqlDataAdapter 与 SqlDataReader
【发布时间】:2009-11-04 21:30:06
【问题描述】:

使用 SqlDataAdapter 与 SqlDataReader 从数据库获取数据有什么区别?

我正在专门研究它们的优缺点以及它们的速度和内存性能。

谢谢

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    数据读取器:

    • 需要保持连接打开直到完成(不要忘记关闭它!)。
    • 通常只能迭代一次
    • 对于更新回数据库没有那么有用

    另一方面,它:

    • 内存中一次只有一条记录,而不是整个结果集(这可能巨大
    • 在一次迭代中几乎可以达到最快速度
    • 允许您更快地开始处理结果(一旦第一条记录可用)。对于某些查询类型,这也可能非常重要。

    数据适配器/数据集

    • 让您在加载数据完成后立即关闭连接,甚至可以自动为您关闭它
    • 所有结果都在内存中可用
    • 您可以根据需要对其进行多次迭代,甚至可以按索引查找特定记录
    • 具有一些用于更新回数据库的内置功能

    代价是:

    • 更多更高的内存使用率
    • 您要等到所有数据都加载完毕后再使用任何数据

    所以这真的取决于你在做什么,但我倾向于更喜欢 DataReader,直到我需要仅由数据集支持的东西。 SqlDataReader 非常适合绑定到只读网格的常见数据访问案例。

    有关详细信息,请参阅the official Microsoft documentation

    【讨论】:

    • DataSet 是内存中的数据存储,而 datareader 只是检索数据的媒介。顺便说一句,您可以在 DataSet 上运行 Linq 查询,但不能在 datareader 上运行。
    • 实际上,通过一些额外的代码,您当然可以在数据读取器上运行 linq 查询(或至少一个查询)。只需使用迭代器块在 while (reader.Read()) 循环中将 DataReader 强制转换为 IDataRecord 即可。
    • 这个答案具有误导性。如果您使用“使用”语句包装 SqlConnection 和 SqlDataReader 对象(无论如何您都应该这样做,因为它们是 IDisposable),连接将自动关闭。您可以将 DataSet 与 SqlDataReader 一起使用:只需调用 DataSet.Load(SqlDataReader)。
    • @RickNZ 不要太快相信使用语句为您关闭事情。他们调用对象的 Dispose() 方法,而不是 Close() 方法,并且我遇到了至少一种情况,即 Dispose 并没有真正为我关闭对象。最好在 using 块中包含对 close 方法的显式调用。
    • @Cdaragorn MSDN 文档通常非常清楚 Close() 与 Dispose()。例如,在 SqlConnection 的情况下,文档说 Close() 和 Dispose() 在功能上是等效的。我对调用 Close() 没有任何异议,但对所有 IDisposables 也应该调用 Dispose() —— 最简洁的方法是使用 using 语句。如果你知道 Dispose() 不会调用 Close(),那么你应该尽可能在 finally 块中调用 Close(),而不是在 using 块中(所以如果出现异常,它仍然会被调用)。跨度>
    【解决方案2】:

    这个问题的答案可能相当广泛。

    本质上,对我而言,通常会影响我使用哪个决定的主要区别在于,使用 SQLDataReader,您可以从数据库中“流式传输”数据。使用 SQLDataAdapter,您可以将数据库中的数据提取到一个对象中,该对象本身可以进一步查询,并对其执行 CRUD 操作。

    显然,对于数据流,SQLDataReader 的速度要快得多,但您一次只能处理一条记录。使用 SQLDataAdapter,您可以从数据库中获得与查询匹配的行的完整集合,以使用/传递您的代码。

    警告:如果您使用的是 SQLDataReader,请始终、始终、始终确保编写正确的代码来关闭连接,因为您使用 SQLDataReader 保持连接打开。如果不这样做,或在处理结果时发生错误时关闭连接的正确错误处理将CRIPPLE您的应用程序连接泄漏。

    请原谅我的 VB,但这是您在使用 SqlDataReader 时应该拥有的最少代码量:

    Using cn As New SqlConnection("..."), _
          cmd As New SqlCommand("...", cn)
    
        cn.Open()
        Using rdr As SqlDataReader = cmd.ExecuteReader()
            While rdr.Read()
                ''# ...
            End While
        End Using
    End Using     
    

    等效的 C#:

    using (var cn = new SqlConnection("..."))
    using (var cmd = new SqlCommand("..."))
    {
        cn.Open();
        using(var rdr = cmd.ExecuteReader())
        {
            while(rdr.Read())
            {
                //...
            }
        }
    }
    

    【讨论】:

    • 如果您的目标是在 db 上使用 select 查询获取数据,并且只在不同的行访问这些数据,转到 rpevious 行等,那么您可以使用 SQLDatareader 并将其加载到数据表中使用 dtable.Load(rdr)。然后在此数据表中上下浏览。您可以使用此方法代替 DataAdapter...
    【解决方案3】:

    SqlDataAdapter 通常用于填充 DataSet 或 DataTable,因此您可以在连接关闭后访问数据(断开访问)。

    SqlDataReader 是一个快速只进且连接的游标,通常比填充 DataSet/DataTable 更快。

    此外,使用 SqlDataReader,您可以一次处理一条数据,并且不会在内存中保存任何数据。显然,对于 DataTable 或 DataSet,您确实有内存分配开销。

    如果您不需要将数据保存在内存中,那么仅用于渲染内容,请使用 SqlDataReader。如果您想以断开连接的方式处理数据,请选择 DataAdapter 来填充 DataSet 或 DataTable。

    【讨论】:

      【解决方案4】:

      想要从数据库中填充内存中的 DataSet/DataTable 时,请使用 SqlDataAdapter。然后,您可以灵活地关闭/处理连接,在内存中传递数据表/集。然后,您可以使用数据适配器以及 InsertCommand/UpdateCommand 操作数据并将其持久化回 DB。

      当需要快速、低内存占用的数据访问而不需要灵活性时使用 SqlDataReader,例如围绕业务逻辑传递数据。这对于大数据量的快速、低内存使用检索更为优化,因为它不会一次性将所有数据加载到内存中 - 使用 SqlDataAdapter 方法,DataSet/DataTable 将填充所有数据,所以如果有很多行和列,需要大量内存来保存。

      【讨论】:

        【解决方案5】:

        Fill 函数在内部使用 DataReader。如果您的考虑是“哪个更有效?”,那么在逐条记录填充集合的紧密循环中使用 DataReader 可能与使用 DataAdapter.Fill 对系统的负载相同。

        (System.Data.dll、System.Data.Common.DbDataAdapter、FillInternal。)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-02-06
          • 1970-01-01
          • 1970-01-01
          • 2013-10-12
          • 1970-01-01
          • 2015-10-05
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多