SqlDataAdapter 与 SqlDataReader

Posted

技术标签:

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

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

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

谢谢

【问题讨论】:

【参考方案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。)

【讨论】:

以上是关于SqlDataAdapter 与 SqlDataReader的主要内容,如果未能解决你的问题,请参考以下文章

SqlDataAdapter 与 SqlDataReader

C# SqlDataAdapter 与来自多个数据库的表的 JOIN

C# SqlDataAdapter 与来自多个数据库的表的 JOIN

SqlDataReader 与 SqlDataAdapter:返回 DataTable 的性能哪个更好?

使用“SQlDataAdapter”时的最佳实践

与 SqlDataAdapter.Fill() 相比,DataTable.Load() 花费了太多时间