SqlDataAdapter.Fill 方法慢
Posted
技术标签:
【中文标题】SqlDataAdapter.Fill 方法慢【英文标题】:SqlDataAdapter.Fill method slow 【发布时间】:2010-09-20 00:37:24 【问题描述】:为什么使用此代码返回包含 9 列、89 行的表的存储过程需要 60 秒来执行(.NET 1.1),而在 SQL Server Management Studio 中运行需要
Dim command As SqlCommand = New SqlCommand(procName, CreateConnection())
command.CommandType = CommandType.StoredProcedure
command.CommandTimeout = _commandTimeOut
Try
Dim adapter As new SqlDataAdapter(command)
Dim i as Integer
For i=0 to parameters.Length-1
command.Parameters.Add(parameters(i))
Next
adapter.Fill(tableToFill)
adapter.Dispose()
Finally
command.Dispose()
End Try
输入了我的参数数组(对于这个 SQL,它只有一个参数)
parameters(0) = New SqlParameter("@UserID", SqlDbType.BigInt, 0, ParameterDirection.Input, True, 19, 0, "", DataRowVersion.Current, userID)
存储过程只是一个这样的选择语句:
ALTER PROC [dbo].[web_GetMyStuffFool]
(@UserID BIGINT)
AS
SELECT Col1, Col2, Col3, Col3, Col3, Col3, Col3, Col3, Col3
FROM [Table]
【问题讨论】:
我遇到了同样的问题并使用这篇文章解决了它:databasejournal.com/features/mssql/article.php/3841271/… 这对我有用,因为我的存储过程会根据参数进行一些分支。 这是一个老问题,很多人都发现了。在开始清理缓存和使用 ARITHABORT 设置之前,请阅读 Erland Sommarskog 的综合文章,该文章解释了可能发生的情况:Slow in the Application, Fast in SSMS? Understanding Performance Mysteries 这种行为通常是由 SQL Server 的“参数嗅探”功能引起的。跨度> 【参考方案1】:首先,确保您正确地分析了性能。例如,从 ADO.NET 运行查询两次,看看第二次是否比第一次快得多。这消除了等待应用编译和调试基础设施启动的开销。
接下来,检查 ADO.NET 和 SSMS 中的默认设置。例如,如果您在 SSMS 中运行 SET ARITHABORT OFF
,您可能会发现它现在的运行速度与使用 ADO.NET 时一样慢。
我曾经发现,SSMS 中的SET ARITHABORT OFF
导致重新编译存储的过程和/或使用不同的统计信息。突然间,SSMS 和 ADO.NET 都报告了大致相同的执行时间。请注意,ARITHABORT
本身并不是导致速度变慢的原因,而是它会导致重新编译,并且您最终会得到两个不同的计划due to parameter sniffing。参数嗅探很可能是需要解决的实际问题。
要检查这一点,请查看每次运行的执行计划,特别是 sys.dm_exec_cached_plans
表。它们可能会有所不同。
在特定存储过程上运行“sp_recompile”将从缓存中删除关联的执行计划,从而使 SQL Server 有机会在下一次执行该过程时创建一个可能更合适的计划。
最后,您可以尝试使用 SSMS 清除整个过程缓存和内存缓冲区的“nuke it from orbit”方法:
DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE
在测试查询之前这样做可以防止使用缓存的执行计划和以前的结果缓存。
【讨论】:
DBCC DROPCLEANBUFFERSDBCC FREEPROCCACHE 解决了我的问题。这需要定期运行,还是仅在对存储过程进行更改时运行? 它是重复的,因为这些是缓存的结果和计划,会逐渐积累。 很好的答案,我的 SP 执行时间从 117 秒变为 3 秒! +1 “SET ARITHABORT OFF”是一个很好的答案!现在,我在 ADO.NET 和 SSMS 之间获得了一致的体验。在我的情况下,我只需要重建一个从 SSMS IDE 完成的索引。 @Munavvar,抱歉,迟到了你的评论。是的,'sp_recompile' 将清除指定存储过程的执行计划的缓存。【参考方案2】:这是我最终做的:
我执行了以下 SQL 语句来重建数据库中所有表的索引:
EXEC <databasename>..sp_MSforeachtable @command1='DBCC DBREINDEX (''*'')', @replacechar='*'
-- Replace <databasename> with the name of your database
如果我想在 SSMS 中看到相同的行为,我可以这样运行 proc:
SET ARITHABORT OFF
EXEC [dbo].[web_GetMyStuffFool] @UserID=1
SET ARITHABORT ON
绕过此问题的另一种方法是将其添加到您的代码中:
MyConnection.Execute "SET ARITHABORT ON"
【讨论】:
【参考方案3】:我遇到了同样的问题,但是当我在 SQL 表上重建索引时,它工作正常,所以您可能需要考虑在 sql server 端重建索引
【讨论】:
【参考方案4】:为什么不让它成为 DataReader 而不是 DataAdapter,看起来你有一个单一的结果集,如果你不打算将更改推回 DB 并且不需要在 .NET 代码中应用约束,你应该'不要使用适配器。
编辑:
如果您需要它成为 DataTable,您仍然可以通过 DataReader 从 DB 中提取数据,然后在 .NET 代码中使用 DataReader 填充 DataTable。这应该仍然比依赖 DataSet 和 DataAdapter 更快
【讨论】:
我正在使用的代码只能返回此存储过程的数据表。【参考方案5】:我不知道“为什么”它本身这么慢 - 但正如 Marcus 所指出的 - 将 Mgmt Studio 与填充数据集进行比较是苹果与橙子的对比。数据集包含大量开销。我讨厌它们,如果我能帮上忙,就永远不要使用它们。
您可能会遇到旧版本的 SQL 堆栈不匹配等问题(尤其是考虑到您显然也停留在 .NET 1.1 中)该框架可能会尝试执行数据库等效的“反射”来推断架构等等等等等等
要考虑尝试使用您不幸的约束的一件事是使用数据读取器访问数据库并在代码中构建您自己的数据集。您应该可以通过 google 轻松找到示例。
【讨论】:
请使用一些 using 语句,因为您的连接实现了 IDisposable以上是关于SqlDataAdapter.Fill 方法慢的主要内容,如果未能解决你的问题,请参考以下文章
SqlDataAdapter#Fill:`SelectCommand.connection` 属性尚未初始化
SQLDataAdapter Fill 方法如何触发数据库查询?
SqlDataAdapter.Fill 方法不会给出任何错误,但也不会返回任何数据以用于长时间运行的查询 ado.net core SQL Server