SQL服务器拒绝缓存固定长度参数化IN子句的计划

时间:2012-05-01 03:40:52

标签: sql-server ado.net

使用.NET 4.0,我已经定义了以下sqlcommand。当我连续多次执行sqlcommand而不进行任何更改时,SQL Server拒绝缓存查询计划。

string[] colors = new string[] { "red", "blue", "yellow", "green" };
string cmdText = "SELECT * FROM ColoredProducts WHERE Color IN ({0})";

string[] paramNames = tags.Select(
    (s, i) => "@color" + i.ToString()
).ToArray();

string inClause = string.Join(",", paramNames);
using (SqlCommand cmd = new SqlCommand(string.Format(cmdText, inClause))) {
    for(int i = 0; i < paramNames.Length; i++) {
       cmd.Parameters.AddWithValue(paramNames[i], tags[i]);
    }
    //Execute query here
}

我知道它拒绝缓存计划,因为以下查询在连续运行后的一小部分时间内运行:

string[] colors = new string[] { "red", "blue", "yellow", "green" };
string cmdText = "SELECT * FROM ColoredProducts WHERE Color IN ({0})";

string inClause = string.Join(",", colors);
using (SqlCommand cmd = new SqlCommand(string.Format(cmdText, inClause))) {
       //Execute query here
}

在我的实际测试用例中,参数列表固定为2000的大小。我试图优化的场景是从一个非常大的表中选择一组特定的2000条记录。我希望查询尽可能快,所以我真的希望它能够缓存。

困了帖子编辑: 问题是,为什么这个计划不会被缓存?是的,我已经使用sys.dm_exec_cached_plans和sys.dm_exec_sql_test确认查询不在缓存中。

1 个答案:

答案 0 :(得分:1)

这是使用表值参数的想法。如果这种方法比你的巨大字符串表现更好,请告诉我们。还有其他一些想法,但这是最接近将数组作为数组处理。

在SQL Server中:

CREATE TYPE dbo.Colors AS TABLE
(
  Color VARCHAR(32) -- be precise here! Match ColoredProducts.Color
     PRIMARY KEY
);
GO

CREATE PROCEDURE dbo.MatchColors
  @colors AS dbo.Colors READONLY
AS
BEGIN
  SET NOCOUNT ON;

  SELECT cp.* -- use actual column names please!
  FROM dbo.ColoredProducts AS cp -- always use schema prefix
  INNER JOIN @colors AS c
  ON cp.Color = c.Color; 
END
GO

现在在C#中:

DataTable tvp = new DataTable();
tvp.Columns.Add(new DataColumn("Color"));

tvp.Rows.Add("red"); 
tvp.Rows.Add("blue"); 
tvp.Rows.Add("yellow"); 
tvp.Rows.Add("green"); 
// ...

using (connectionObject)
{
    SqlCommand cmd = new SqlCommand("dbo.MatchColors", connectionObject);
    cmd.CommandType = CommandType.StoredProcedure;
    SqlParameter tvparam = cmd.Parameters.AddWithValue("@colors", tvp);
    tvparam.SqlDbType = SqlDbType.Structured;
    // execute query here
}

我几乎可以保证,与具有大量参数的IN列表相比,它的性能会更好,无论C#代码中实际字符串的长度如何。