我们有一个现有的 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 及更高版本。)
是的,有一个隐藏的、破坏性能的缺点!
非常感谢 Martin Smith,他的回答(见下文)为我指出了正确的分析方法。我测试了我们应用程序的用户表,它有一个定义为 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
根据我取消注释的“声明”语句,我得到一个非常不同的查询计划。 nvarchar(100) 和 nvarchar(4000) 版本都为我提供了一个关于 IX_Users_Email 的索引 seek —— 事实上,我指定的任何长度都会给我相同的计划。另一方面,nvarchar(max) 版本为我提供了对 IX_Users_Email 的索引扫描,然后是过滤器运算符以应用谓词。
这对我来说就足够了——如果有任何可能进行扫描而不是寻找,那么这种“治愈”比疾病更糟糕。
新提案
我注意到每次 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;
这似乎可以解决计划缓存污染问题,而不会像使用大小 -1 那样对查询计划产生同样的影响。但是,我还没有对此进行大量测试,我很想听听任何人对这种修改后的方法的意见。
我们不得不修改之前的版本(上面的新提案)来处理参数值超过最大值的情况。那时,您别无选择,只能将其设为 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;
}
我们已经使用这个版本大约六个月了,没有任何问题。
最佳答案
这并不理想,因为最好指定一个与所涉及列的数据类型相匹配的参数。
您需要检查您的查询计划,看看它们是否仍然合理。
尝试下面的测试
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 将计算标量添加到调用内部函数 GetRangeWithMismatchedTypes 的计划中,并且仍然设法执行索引查找 (more details on implicit conversions here)。
文章 Why Doesn’t Partition Elimination Work? 中显示了一个重要的反例.该文章中描述的行为也适用于针对在 varchar(n) 列上分区的表的 varchar(max) 参数。
关于c# - ADO.NET:为所有 VarChar 参数的 SqlParameter.Size 指定 -1 安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14342954/
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere
是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou
我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A
我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"
我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)
我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这