与执行准备好的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
or 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()
。我在测试中注意到的一件事,以及运行该测试控制台应用程序的任何人应该注意的一点:从来没有进行过明确的调用sp_unprepare
。我进行了一些挖掘,并在MSDN论坛中找到了以下文章:
SqlCommand-准备还是不准备?
接受的答案包含大量信息,但要点是:
- **准备实际上为您节省的时间主要是通过网络传输查询字符串所花费的时间。
- 准备好的查询不能保证已保存缓存的计划,并且一旦到达服务器就不会比临时查询快。
- RPC的(CommandType.StoredProcedure)从准备中得不到任何好处。
- 在服务器上,准备好的句柄基本上是指向包含要执行的TSQL的内存映射的索引。
- 该映射是基于每个连接存储的,无法使用交叉连接。
- 客户端重新使用池中的连接时发送的重置清除了地图。
我还从SQLCAT小组找到了以下帖子,该帖子似乎相关,但可能只是特定于ODBC的问题,而SqlClient在处理SqlConnection时确实可以正确清理。很难说,因为SQLCAT帖子中没有提及任何其他有助于证明这是原因的测试,例如清除连接池等。
小心那些准备好的SQL语句
结论
鉴于:
- 调用的唯一真正好处
SqlCommand.Prepare()
似乎是您不需要通过网络再次提交查询文本,
- 调用
sp_prepare
并将sp_prepexec
查询文本存储为连接内存(即SQL Server内存)的一部分
我建议不要拨打电话,SqlCommand.Prepare()
因为唯一的潜在好处是节省了网络数据包,而缺点是占用了更多的服务器内存。尽管在大多数情况下消耗的内存量可能很小,但是网络带宽几乎不会成为问题,因为大多数数据库服务器直接以至少10兆位(如今更可能是100兆位或千兆位)的连接直接连接到应用程序服务器(有些甚至在同一盒子上;-)。与网络带宽相比,内存和内存几乎总是更稀缺。
我想,对于任何编写可以连接到多个数据库的软件的人来说,标准接口的便利性可能会改变成本/收益分析。但是即使在那种情况下,我也必须相信,提取一个“标准”数据库接口仍然足够容易,因为该接口是基本的对象,所以该接口允许使用不同的提供程序(因此它们之间也存在差异,例如不需要调用Prepare()
)。您可能已经在应用程序代码中进行过的面向对象的编程;-)。