看来我对查询计划的理解不正确。如果我第一次运行此查询并检查查询计划缓存。我会看到一个缓存的计划。
declare @codes varchar(max)='BHVD7,BHVDE,BHVDF'
SELECT OFST010020 AS [OFST010020], OFST730010 AS [OFST730010], Identifier AS [Identifier] FROM STATIC_FUNDSHARECLASSUNIT_NEXT as f WITH(NOLOCK) Inner join STATIC_FUNDSHARECLASSUNITEXTENSION_NEXT as fex WITH(NOLOCK) on fex.Identifier2 = f.Identifier WHERE OFST900174 in (select value from string_split(@codes,','))
如果我只是更改代码参数并在同一个 SSMS 窗口中再次运行查询,例如
declare @codes varchar(max)='BHVD7,BHVDF'
SELECT OFST010020 AS [OFST010020], OFST730010 AS [OFST730010], Identifier AS [Identifier] FROM STATIC_FUNDSHARECLASSUNIT_NEXT as f WITH(NOLOCK) Inner join STATIC_FUNDSHARECLASSUNITEXTENSION_NEXT as fex WITH(NOLOCK) on fex.Identifier2 = f.Identifier WHERE OFST900174 in (select value from string_split(@codes,','))
我以为查询计划会有 1 行,useCount 为 2。但在查询计划缓存中,我实际上看到了两个计划。每个计划的 sql 文本实际上是全文(以声明 @codes 开头)。我做错了什么吗?还是 ad hoc 查询实际上并没有重用计划,即使涉及参数?
没有涉及任何参数。您使用的是局部变量,而不是参数。在日常用语中没有太大区别,但对于 SQL Server 来说,它们是完全不同的东西。
一般来说,除非批处理的整个文本(而不是批处理中的各个语句)完全匹配,否则SQL Server 不会重用临时批处理的计划。
当批处理中的语句可以通过简单或强制参数化在服务器端进行参数化时,会有一些微妙之处,但这里似乎并非如此。
无论如何,即使使用服务器端参数化,也只有参数化形式会被缓存并针对不同的参数值进行重用。原始临时文本几乎总是单独缓存,并且仅在完全匹配时才重用。
为了鼓励计划重用,您需要使用存储过程、准备好的语句或
sp_executesql
。所有这些都允许您显式地参数化语句以进行计划重用。有关参数化和计划重用的更多信息,请参阅 Microsoft 白皮书《SQL Server 2012 中的计划缓存和重新编译》。
你确定这两个查询是相同的(甚至空间位置也相同)吗?
我使用 StackOverflow 数据库做了一个小测试。
注意:在生产中不建议清理存储过程缓存:
我有以下疑问
第一次执行时,创建一个新的计划,
usecounts
值为1。在第二次执行中,该计划被重用,
usecounts
值为2。但是,当我第三次运行查询时,前面有一个空格,如下所示:
我收到了不同的执行计划。