Ran*_*ard 17 c# sql-server ado.net
我们有一个现有的C#代码体,可以在很多地方调用参数化的临时SQL Server查询.我们从不指定SqlParameter.Size,并且记录了在这种情况下,SqlParameter类从参数值推断出大小.我们最近才意识到这会产生SQL Server计划缓存污染问题,其中为每个不同的参数大小组合缓存单独的计划.
幸运的是,每当我们创建一个SqlParameter时,我们都是通过一个实用方法来实现的,所以我们有机会在该方法中添加几行并使这个问题消失.我们正考虑添加以下内容:
if((sqlDbType == SqlDbType.VarChar) || (sqlDbType == SqlDbType.NVarChar))
m_sqlParam.Size = -1;
Run Code Online (Sandbox Code Playgroud)
换句话说,每次传递varchar参数时,将其作为varchar(max)传递.基于一些快速测试,这很好用,我们可以看到(通过SQL Profiler和sys.dm_exec_cached_plans)每个ad-hoc查询的缓存中现在有一个计划,以及字符串参数的类型现在是varchar(max).
这似乎是一个简单的解决方案,必须有一些隐藏的,破坏性能的缺点.有人知道吗?
(请注意,我们只需要支持SQL Server 2008及更高版本.)
是的,有一个隐藏的,破坏性能的缺点!
非常感谢Martin Smith,他的回答(见下文)向我指出了正确的分析方法.我使用我们的应用程序的Users表进行了测试,该表具有定义为nvarchar(100)的Email列,并且在Email列上具有非聚集索引(IX_Users_Email).我修改了Martin的示例查询,如下所示:
declare @a nvarchar(max) = cast('a' as nvarchar(max))
--declare @a nvarchar(100) = cast('a' as nvarchar(100))
--declare @a nvarchar(4000) = cast('a' as nvarchar(4000))
select Email from Users where Email = @a
Run Code Online (Sandbox Code Playgroud)
根据我取消评论的"声明"语句,我得到了一个非常不同的查询计划.nvarchar(100)和nvarchar(4000)版本都给我一个IX_Users_Email 的索引搜索 - 实际上,我指定的任何长度都给了我相同的计划.另一方面,nvarchar(max)版本为我提供了对IX_Users_Email 的索引扫描,然后是Filter运算符以应用谓词.
这对我来说已经足够了 - 如果有可能进行扫描而不是寻求,那么这种"治愈"比疾病更糟糕.
新提案
我注意到每次SQL Server使用varchar参数参数化查询时,缓存计划只使用varchar(8000)(或nvarchar(4000))作为参数.我认为如果它对SQL Server来说足够好,那对我来说已经足够了!用我原来的问题(上面)替换C#代码:
if(sqlDbType == SqlDbType.VarChar)
m_sqlParam.Size = 8000;
else if(sqlDbType == SqlDbType.NVarChar)
m_sqlParam.Size = 4000;
Run Code Online (Sandbox Code Playgroud)
这似乎解决了计划缓存污染问题,而不会像使用-1的大小那样对查询计划产生相同的影响.但是,我没有对此进行过大量的测试,我很想听听任何人对此修订方法的意见.
我们必须修改先前版本(上面的New Proposal)来处理参数值超过最大值的情况.此时,您别无选择,只能将其设为varchar(max):
if((sqlDbType == SqlDbType.VarChar) || (sqlDbType == SqlDbType.NVarChar))
{
m_sqlParam.Size = (sqlDbType == SqlDbType.VarChar) ? 8000 : 4000;
if((value != null) && !(value is DBNull) && (value.ToString().Length > m_sqlParam.Size))
m_sqlParam.Size = -1;
}
Run Code Online (Sandbox Code Playgroud)
我们已经使用这个版本大约六个月没有问题.
这并不理想,因为最好指定与所涉及列的数据类型相匹配的参数。
\n\n您需要检查您的查询计划,看看它们看起来是否仍然合理。
\n\n尝试以下测试
\n\nCREATE TABLE #T\n(\nX VARCHAR(10) PRIMARY KEY\n)\n\n\nDECLARE @A VARCHAR(MAX) = CAST(\'A\' AS VARCHAR(MAX))\n\nSELECT *\nFROM #T \nWHERE X = @A\nRun Code Online (Sandbox Code Playgroud)\n\n给出一个像这样的计划
\n\n
SQL Server 将一个计算标量添加到调用内部函数的计划中GetRangeWithMismatchedTypes,并且仍然设法执行索引查找(有关隐式转换的更多详细信息请参见此处)。
文章《为什么\xe2\x80\x99t 分区消除有效?》中显示了一个重要的反例。。该文章中描述的行为也适用于varchar(max)针对分区表的参数varchar(n)。
| 归档时间: |
|
| 查看次数: |
3774 次 |
| 最近记录: |