SqlDataAdapter和SqlDataReader的区别

144

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

我特别关注它们的优缺点,以及它们的速度和内存表现。

谢谢

5个回答

211

DataReader:

  • 需要在您完成操作前一直保持连接打开状态(不要忘记关闭它!)。
  • 通常只能迭代一次。
  • 不太适用于更新回数据库。

但是,DataReader:

  • 每次仅有一条记录存储在内存中,而不是整个结果集(这可能非常庞大)。
  • 在单次迭代中速度几乎最快。
  • 允许您尽早开始处理结果(一旦第一条记录可用)。对于一些查询类型,这也可能是非常重要的。

DataAdapter/DataSet

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

但代价是:

  • 使用更高的内存
  • 必须等待全部数据加载完毕才能使用任何数据。

因此,实际上取决于您的需求。但我倾向于在需要使用 DataSet 提供的专门支持时才使用它。SqlDataReader 对于只读网格绑定等常见数据访问情况非常适合。

有关更多信息,请参阅官方 Microsoft 文档


6
DataSet 是一个内存数据存储库,而 datareader 只是检索数据的中介。有趣的是,你可以在 DataSet 上运行 Linq 查询,但不能在 datareader 上运行。 - Partha Choudhury
实际上,只需添加一点额外的代码,您就可以在数据读取器上运行linq查询(或至少一个查询)。只需使用迭代器块,在while(reader.Read())循环内以IDataRecord类型返回DataReader即可。 - Joel Coehoorn
8
这个回答有误导性。如果您在 SqlConnection 和 SqlDataReader 对象周围使用 "using" 语句(应该这样做,因为它们是 IDisposable),连接将自动关闭。而且您可以使用一个 SqlDataReader 来填充 DataSet:只需调用 DataSet.Load(SqlDataReader)。 - RickNZ
当您迭代读取器时,您必须在此迭代期间打开另一个读取器,那么您将需要两个连接。因此,在给定时间打开的读取器数量将取决于连接数。 - bjan
5
@RickNZ 不要轻信使用语句来为您关闭事物。它们调用对象的Dispose()方法,而不是它的Close()方法,我遇到过至少一个情况,其中Dispose并没有真正地关闭对象。最好在using块内包含显式调用close方法。 - Cdaragorn
7
@Cdaragorn MSDN文档通常对Close()与Dispose()做了很清楚的区分。例如,在SqlConnection上,文档表明Close()和Dispose()在功能上是等效的。我没有反对调用Close(),但对于所有IDisposable对象,应该同时调用Dispose(),最简洁的方法是使用using语句。在您知道Dispose()不会调用Close()的情况下,如果可能的话,应该在finally块中调用Close(),而不是在using块中调用(这样即使有异常也会被调用)。 - RickNZ

18
答案可能相当广泛。
基本上,对我来说通常影响我决定使用哪种方法的主要区别是,使用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))
{
    cn.Open();
    using(var rdr = cmd.ExecuteReader())
    {
        while(rdr.Read())
        {
            //...
        }
    }
}
   

3
如果您的目标是使用数据库上的选择查询来获取数据,并且仅在不同行访问此数据,例如移至先前的行等,则可以使用SQLDatareader并使用dtable.Load(rdr)将其加载到datatable中。然后在此datatable中上下浏览。您可以使用此方法代替DataAdapter… - variable

17

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

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

此外,使用SqlDataReader时,您一次处理一条记录,并且不保存任何数据在内存中。显然,对于DataTable或DataSet,您需要分配一定的内存空间。

如果您不需要保留数据在内存中,则只需用SqlDataReader进行渲染即可。如果您想以断开连接的方式处理数据,请选择DataAdapater来填充DataSet或DataTable。


11
使用SqlDataAdapter可将数据库中的数据填充到内存中的DataSet/DataTable。您可以选择关闭/销毁连接,并在内存中传递DataTable/Set,然后可以使用DataAdapter和InsertCommand/UpdateCommand将数据持久化到数据库中。当需要快速、低内存占用的数据访问时,请使用SqlDataReader,而不需要传递业务逻辑中的数据。这对于快速获取大量数据非常优化,因为它不会一次性将所有数据加载到内存中 - 使用SqlDataAdapter方法,DataSet/DataTable将被填充所有数据,如果有很多行和列,则需要大量内存来存储。

0

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接