Mag*_*ier 16 sql-server execution-plan sql-server-2008-r2 prepared-statement plan-cache
我遇到了开发人员代码,其中 SqlCommand.Prepare() (参见 MSDN)方法在执行 SQL 查询之前被广泛使用。我想知道这样做有什么好处?
样本:
command.Prepare();
command.ExecuteNonQuery();
//...
command.Parameters[0].Value = 20;
command.ExecuteNonQuery();
Run Code Online (Sandbox Code Playgroud)
我玩了一点并进行了追踪。调用Prepare()
方法后执行Command,使Sql Server执行如下语句:
declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,N'@id int,@desc text',N'INSERT INTO dbo.testtable (id) VALUES (@id)',@id=20'
select @p1
Run Code Online (Sandbox Code Playgroud)
之后,当 Parameter 获取它的值并被SqlCommand.ExecuteNonQuery()
调用时,将在 Sql-Server 上执行以下操作:
exec sp_execute 1,@id=20
Run Code Online (Sandbox Code Playgroud)
对我来说,这看起来就像语句在Prepare()
执行时被编译。我想知道这样做有什么好处?这是否意味着它被放入计划缓存中,并且可以在使用所需参数值执行最终查询后立即重新使用?
我发现(并在另一个问题中记录了它)使用 SqlParameters 执行的 SqlCommands 总是包含在sp_executesql
过程调用中。这使得 Sql Server 能够独立于参数值存储和重用计划。
关于这一点,我想知道该prepare()
方法是无用的还是过时的,或者我在这里遗漏了什么?
Sol*_*zky 22
准备 SQL 批处理与执行准备好的 SQL 批处理分开是一种有效的构造**考虑到执行计划是如何缓存的,对 SQL Server 没用。分离准备步骤(解析、绑定任何参数和编译)和执行只有在没有缓存时才有意义。目的是通过重用现有计划来节省解析和编译所花费的时间,这就是内部计划缓存所做的。但并不是所有的 RDBMS 都做这种级别的缓存(显然从这些函数的存在来看),因此由客户端代码来请求“缓存”计划,因此保存该缓存 ID,然后重新使用它。这对客户端代码(以及程序员要记住这样做并使其正确)造成额外的负担,这是不必要的。事实上,IDbCommand.Prepare()方法的 MSDN 页面指出:
服务器根据需要自动缓存计划以供重用;因此,无需在客户端应用程序中直接调用此方法。
应当指出的是,据我的测试显示(这符合你的问题显示什么),调用SqlCommand.Prepare() ,并没有做“准备” -只操作:调用sp_prepexec其准备和执行SQL ; 它不调用仅解析和编译的sp_prepare(并且不接受任何参数值,只接受它们的名称和数据类型)。因此,调用不会有任何“预编译”的好处,SqlCommand.Prepare
因为它会立即执行(假设目标是推迟执行)。然而,调用 有一个有趣的潜在小好处SqlCommand.Prepare()
,即使它确实调用sp_prepexec
而不是sp_prepare
. 请参阅注释(** ) 在底部了解详细信息。
我的测试表明,即使SqlCommand.Prepare()
是所谓的(并且请注意,此调用并不会发出SQL Server上的任何命令)中,ExecuteNonQuery
或ExecuteReader
遵循完全执行,如果是的ExecuteReader,它返回行。尽管如此,SQL Server Profiler 确实显示SqlCommand.Execute______()
调用后的第一个调用SqlCommand.Prepare()
注册为“Prepare SQL”事件,随后的.Execute___()
调用注册为“Exec Prepared SQL”事件。
测试代码
它已上传到 PasteBin:http ://pastebin.com/Yc2Tfvup 。该代码创建了一个 .NET/C# 控制台应用程序,该应用程序旨在在 SQL Server Profiler 跟踪运行(或扩展事件会话)时运行。它在每一步后都会暂停,以便清楚哪些语句具有特定效果。
更新
找到了更多信息,以及避免调用SqlCommand.Prepare()
. 我在测试中注意到的一件事,以及运行该测试控制台应用程序的任何人都应该注意的事情:从来没有明确调用 made sp_unprepare
。我做了一些挖掘,并在 MSDN 论坛中找到了以下帖子:
接受的答案包含一堆信息,但要点是:
我还发现了 SQLCAT 团队的以下帖子,这似乎是相关的,但可能只是特定于 ODBC 的问题,而 SqlClient 在处理 SqlConnection 时确实会正确清理。很难说,因为 SQLCAT 帖子没有提到任何有助于证明这是一个原因的额外测试,例如清除连接池等。
结论
鉴于:
SqlCommand.Prepare()
似乎是您不需要再次通过网络提交查询文本,sp_prepare
并将其sp_prepexec
存储为连接内存的一部分(即 SQL Server 内存)我建议不要调用,SqlCommand.Prepare()
因为唯一的潜在好处是节省网络数据包,而缺点是占用更多服务器内存。虽然在大多数情况下消耗的内存量可能非常小,但网络带宽很少成为问题,因为大多数数据库服务器直接连接到应用服务器的最低 10 兆位(这些天更可能是 100 兆位或千兆位)连接(有些甚至在同一个盒子里;-)。那和内存几乎总是比网络带宽更稀缺的资源。
我想,对于任何编写可以连接到多个数据库的软件的人来说,标准界面的便利性可能会改变这种成本/收益分析。但即使在那种情况下,我也必须相信,抽象出一个允许不同提供者(因此它们之间存在差异,例如不需要调用Prepare()
)的“标准”数据库接口仍然足够容易,因为这样做是基本的 Object您可能已经在您的应用程序代码中进行了定向编程;-)。
关于这一点,我想知道 prepare() 方法是否有点无用或过时,或者我是否在这里遗漏了什么?
我会说 Prepare 方法在 SqlCommand 世界中的价值有限,并不是说它完全没用。一个好处是 Prepare 是 SqlCommand 实现的 IDbCommand 接口的一部分。这允许针对其他 DBMS 提供程序(可能需要 Prepare)运行相同的代码,而无需使用条件逻辑来确定是否应调用 Prepare。
除了这个用例 AFAIK 之外,Prepare 对于 SQL Server 的 .NET Provider 没有任何价值。
归档时间: |
|
查看次数: |
5350 次 |
最近记录: |