SqlDataAdapter与SqlDataReader


130

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

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

谢谢

Answers:


193

SqlDataReader:

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

另一方面,它:

  • 一次只在内存中记录一个记录,而不是整个结果集(可以是HUGE
  • 在那一次迭代中速度最快
  • 使您可以更快地开始处理结果(一旦第一条记录可用)。对于某些查询类型,这也可能是很大的事情。

SqlDataAdapter / DataSet

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

以:

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

因此,实际上这取决于您在做什么,但是在需要仅由数据集支持的内容之前,我倾向于使用DataReader。SqlDataReader非常适合绑定到只读网格的常见数据访问情况。

有关更多信息,请参阅正式的Microsoft文档


5
DataSet是内存中的数据存储,而datareader只是检索数据的媒介。简而言之,您可以在DataSet上运行Linq查询,但不能在datareader上运行。
Partha Choudhury

实际上,只需一点额外的代码,您当然可以在数据读取器上运行linq查询(或至少一个查询)。只需使用迭代器块就可以在while (reader.Read())循环内返回作为IDataRecord的DataReader强制返回。
Joel Coehoorn

7
这个答案是误导的。如果用“ using”语句包装SqlConnection和SqlDataReader对象(无论如何,因为它们是IDisposable的,所以应该这样做),连接将自动关闭。您可以将DataSet与SqlDataReader一起使用:只需调用DataSet.Load(SqlDataReader)。
RickNZ 2011年

4
@RickNZ不要太快相信使用语句为您关闭事物。他们调用对象的Dispose()方法,而不是Close()方法,而且我遇到了至少一种情况,其中Dispose并未真正为我关闭对象。始终最好在using块内包含对close方法的显式调用。
Cdaragorn 2012年

5
@Cdaragorn MSDN文档通常对Close()与Dispose()相当清楚。以SqlConnection为例,文档说Close()和Dispose()在功能上是等效的。我对调用Close()没有异议,但是对所有IDisposables都应该有对Dispose()的调用-做到这一点最干净的方法是使用using语句。如果您知道Dispose()没有调用Close(),那么您应该在finally块内调用Close(),而不是using块内调用(因此,如果有异常,它仍然会被调用)。
RickNZ

17

答案可能非常广泛。

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

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

警告:如果您使用的是SQLDataReader,则始终要确保编写正确的代码以关闭连接,因为要使连接与SQLDataReader保持打开状态。如果不这样做,或者在处理结果过程中发生错误时进行适当的错误处理以关闭连接,将使您的应用程序崩溃并出现连接泄漏。

请原谅我的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上的选择查询来获取数据,并且仅在不同的行上访问该数据,然后转到rpevious行等,则可以使用SQLDatareader并使用dtable.Load(rdr)将其加载到数据表中。然后在此数据表中上下浏览。您可以使用此方法代替DataAdapter ...
变量

14

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

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

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

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


10

想要从数据库中填充内存中的DataSet / DataTable时,请使用SqlDataAdapter。然后,您可以灵活地关闭/释放连接,在内存中传递数据表/设置。然后,您可以使用数据适配器结合InsertCommand / UpdateCommand来操纵数据并将其持久化回数据库中。

想要快速,低内存占用空间的数据访问而无需灵活性,例如在业务逻辑中传递数据时,请使用SqlDataReader。对于大型数据量的快速,低内存使用率检索,这是最佳选择,因为它不会一次性将所有数据全部加载到内存中-使用SqlDataAdapter方法,DataSet / DataTable将充满所有数据,因此如果有很多行和列,这将需要很多内存。


0

Fill函数在内部使用DataReader。如果您考虑的是“哪一种效率更高?”,则在紧密循环中使用DataReader填充每个记录的收集,可能对系统造成的负担与使用DataAdapter.Fill相同。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.