我正在构建一个复杂的查询,以在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查看带有一个参数的查询的执行计划:
编辑:更简化的代表性查询执行计划:
最佳答案 查看执行计划中的“估计行数”属性.使用慢速版本(带参数),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();