sql-server-2005 – 如何避免从C#构建的Sql Server 2005参数化查询减速

我正在构建一个复杂的查询,以在Web视图中显示一些统计结果.视图可以有多个不同的过滤器,具体取决于用户的选择.此外,还可以使用通配符.

我正在使用SqlParameters在c#中以编程方式构建此查询.所以查询看起来像这样:

sc.CommandText = "SELECT * FROM table 
                  WHERE field1 = @filter1 
                  AND field2 LIKE @filter2"; //...and more parameters

sc.SqlParameters.Add(
   new SqlParameter("@filter1", SqlDbType.Int, 32) { Value = 1});

sc.SqlParameters.Add(
   new SqlParameter("@filter2", SqlDbType.VarChar, 446) { Value = "whatever%"});

这是一个非常简化的版本,但查询本身并不重要.请记住,它可以有不同的可选参数(我认为这是一种非常常见的情况).

当我在Sql Manager中运行此查询时,我意识到使用参数时存在巨大的减速.因此,以下两个查询应该相同,它们使用不同的执行计划,使参数化运行速度慢得多:

DECLARE @filter1 INT
DECLARE @filter2 VARCHAR 446
SET @filter1 = 1
SET @filter2 = "whatever%"

SELECT * FROM table WHERE field1 = @filter1 AND field2 LIKE @filter2

快速版:

SELECT * FROM table WHERE field1 = 1 AND field2 LIKE 'whatever%'

这是另一个有同样问题的人的例子:

Why does a parameterized query produces vastly slower query plan vs non-parameterized query

似乎有一些叫做parameter sniffing的东西,可能会使参数化查询运行得更慢,但它不适用于我的情况,因为这不是一个存储过程.

提出的solutions之一是使用OPTION(RECOMPILE)或OPTION(OPTIMIZE FOR).我不能这样做,因为我有大约10个可选参数,可能在过滤器中,或者没有,并且使用LIKE时此选项不起作用.

所以,我觉得我已陷入死胡同,我正在考虑摆脱参数并在代码上构建动态文字查询.但随后Sql Injection出现在游戏中.

那么,您对如何解决这个问题有任何其他建议吗?或者你知道逃避参数的安全方法吗?

编辑:在这里,您可以使用LIKE查看带有一个参数的查询的执行计划:

> Execution Plan

编辑:更简化的代表性查询执行计划:

> Simplified execution plan

最佳答案 查看执行计划中的“估计行数”属性.使用慢速版本(带参数),SQL Server无法很好地估计查询将返回的行,因为它不会在编译时评估变量的实际值.它将仅使用统计信息来估计您用作过滤器的字段的基数,并根据它创建执行计划.

我对这个问题的解决方案是创建一个存储过程,其参数与所需的过滤器一样多:

CREATE PROCEDURE your_sp @filter1 INT, @filter2 VARCHAR(446) AS
 SELECT * FROM table 
 WHERE field1 = @filter1 
 AND field2 LIKE @filter2
sc.CommandText = "your_sp";
sc.CommandType = CommandType.StoredProcedure;

sc.SqlParameters.Add(new SqlParameter("@filter1", SqlDbType.Int, 32) { Value = 1});

sc.SqlParameters.Add(new SqlParameter("@filter2", SqlDbType.VarChar, 446) { Value = "whatever%"});

connection.Open();
SqlDataReader reader = command.ExecuteReader();
点赞