Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/319.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# ADO.NET:可以安全地为SqlParameter指定-1。所有VarChar参数的大小?_C#_Sql Server_Ado.net - Fatal编程技术网

C# ADO.NET:可以安全地为SqlParameter指定-1。所有VarChar参数的大小?

C# ADO.NET:可以安全地为SqlParameter指定-1。所有VarChar参数的大小?,c#,sql-server,ado.net,C#,Sql Server,Ado.net,我们有一个现有的C#代码体,在许多地方调用参数化的即席SQL Server查询。我们从不指定SqlParameter.Size,并且有文档记录,在这种情况下,SqlParameter类根据参数值推断大小。我们最近才意识到这会造成SQL Server计划缓存污染问题,即为每个不同的参数大小组合缓存一个单独的计划 幸运的是,每当我们创建一个SqlParameter时,我们都是通过一个实用程序方法来实现的,因此我们有机会在该方法中添加几行代码,从而解决这个问题。我们正在考虑添加以下内容: if((sq

我们有一个现有的C#代码体,在许多地方调用参数化的即席SQL Server查询。我们从不指定SqlParameter.Size,并且有文档记录,在这种情况下,SqlParameter类根据参数值推断大小。我们最近才意识到这会造成SQL Server计划缓存污染问题,即为每个不同的参数大小组合缓存一个单独的计划

幸运的是,每当我们创建一个SqlParameter时,我们都是通过一个实用程序方法来实现的,因此我们有机会在该方法中添加几行代码,从而解决这个问题。我们正在考虑添加以下内容:

if((sqlDbType == SqlDbType.VarChar) || (sqlDbType == SqlDbType.NVarChar))
    m_sqlParam.Size = -1;
换句话说,每次传递varchar参数时,都将其作为varchar(max)传递。根据一些快速测试,这可以正常工作,并且我们可以看到(通过SQL Profiler和sys.dm_exec_cached_plans),现在缓存中每个临时查询都有一个单独的计划,字符串参数的类型现在是varchar(max)

这似乎是一个非常简单的解决方案,肯定存在一些隐藏的、破坏性能的缺点。有人知道吗

(请注意,我们只需要支持SQL Server 2008及更高版本。)

更新(1月16日) 是的,有一个隐藏的、破坏性能的缺点

非常感谢马丁·史密斯,他的回答(见下文)为我指明了正确的分析方法。我使用应用程序的用户表进行了测试,该表的电子邮件列定义为nvarchar(100),电子邮件列上有一个非聚集索引(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
根据我取消注释的“declare”语句,我得到了一个非常不同的查询计划。nvarchar(100)和nvarchar(4000)版本在IX_用户_电子邮件上都给了我一个索引seek——事实上,我指定的任何长度都给了我相同的计划。另一方面,nvarchar(max)版本在IX_Users_电子邮件上为我提供了一个索引扫描,然后是一个过滤器操作符来应用谓词

这对我来说已经足够了——如果有可能进行扫描而不是搜索,那么这种“治愈”比疾病更糟糕

新提案

我注意到,每次SQLServer使用varchar参数参数化查询时,缓存的计划都只是使用varchar(8000)(或nvarchar(4000))作为参数。我想如果它对SQL Server足够好,对我也足够好!将我原来问题(上文)中的C代码替换为:

这似乎解决了计划缓存污染问题,而不会像使用-1大小那样对查询计划产生影响。然而,我还没有用这个做过很多测试,我非常有兴趣听到任何人对这个修改过的方法的意见

更新(9月24日) 我们必须修改以前的版本(上面的新方案),以处理参数值大于最大值的情况。此时,您别无选择,只能将其设置为varchar(max):


我们已经使用这个版本大约六个月了,没有任何问题。

这并不理想,因为最好指定一个与所涉及列的数据类型匹配的参数

您需要检查您的查询计划,看看它们是否仍然合理

尝试下面的测试

CREATE TABLE #T
(
X VARCHAR(10) PRIMARY KEY
)


DECLARE @A VARCHAR(MAX) = CAST('A' AS VARCHAR(MAX))

SELECT *
FROM #T 
WHERE X = @A
给出一个像这样的计划

SQL Server向计划中添加了一个计算标量,该计划调用内部函数
getRangeWithMismatchdTypes
,但仍设法执行索引查找()


文章中给出了一个反例,说明了这一点。这篇文章中描述的行为也适用于
varchar(max)
参数,该参数针对
varchar(n)
列上分区的表。

我们是出于某种原因喜欢创造复杂性的生物。你的解决方案是合理的。这与在表中包含合法的
VARCHAR(MAX)
字段没有什么不同。您可以放心地相信,Microsoft已经对该数据类型进行了彻底的测试。我已经阅读了有关此问题的文章,但认为它在很久以前就已经为Linq2Sql和实体框架解决了。我没有考虑过特别的sql。或者更确切地说,在我看来,这似乎是SQLServer应该更好地处理的事情。很好的问题,我投你一票。很想了解更多信息。sys.dm_exec_cached_计划是否会如此简单,只报告将要发生的事情,并且Sql Server内部的食物链会进一步优化?对我来说,这似乎是一个明显的优化,我很难相信Sybase背后所有版本的Sql Server都没有考虑到这一点。您是否测量过使用和不使用此大小=-1时的性能?如果这很重要的话,它肯定会出现在每本关于使用Sql Server的教科书中。我想知道不设置大小匹配的数据类型对性能有多大影响?我们曾考虑过类似的方法,但我们没有单一的实用工具来创建
SqlParameter
。我们正在寻找一些全局拦截器,我们有第二种方法来创建Roslyn Analyzer,它将在编译过程中出现错误,所以开发人员将被迫设置大小
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;
}
CREATE TABLE #T
(
X VARCHAR(10) PRIMARY KEY
)


DECLARE @A VARCHAR(MAX) = CAST('A' AS VARCHAR(MAX))

SELECT *
FROM #T 
WHERE X = @A