选项重新编译的CTE不起作用

时间:2015-10-04 08:59:52

标签: sql sql-server tsql sql-server-2012

如果我这样做,

declare @Q varchar(10) = 'ab1';
SELECT * 
FROM   MyTable
WHERE  EXISTS(SELECT TOP 1 't' InnerTable O WHERE O.TId = P.Id)
AND (P.Name_EngLIKE @Per_Name + '%' OR P.PER_NAME_ARB LIKE @Per_Name +'%') 

然后查询变得非常慢。如果我发表评论EXISTS(SELECT TOP 1 't' InnerTable O WHERE O.TId = P.Id),那么它会很快或者如果我添加OPTION(RECOMPILE)那么它会很快。问题是我在CTE(公用表表达式)中使用这个SQL,它不允许我放OPTION(RECOMPILE)

修改 这是我的CTE,

WITH CTEPage AS
(
    SELECT  Top(@PageSize * @PageIndex)
            ROW_NUMBER() OVER (ORDER BY P.Id) AS RowNumber
    FROM    MyTable(NOLOCK) AS P
    WHERE   EXISTS(SELECT TOP 1 't' FROM OtherTable O WHERE O.PId = P.Id)
            AND (@Name IS NULL OR @Name = '' OR P.NAME_ENG LIKE @Name + '%' OR P.NAME_ARB LIKE @Name + '%')
)

SELECT  TOP(@PageSize) 
        *
FROM    CTEPage AS P

WHERE   P.[RowNumber] > (@PageIndex - 1) * @PageSize
        ORDER BY P.[RowNumber] ASC;

更新:CTE有效。问题是@Name是NVarChar而NAME_ENG和NAME_ARB是Varchar

2 个答案:

答案 0 :(得分:4)

根据Query Hints

  

查询提示只能在顶级查询中指定,而不能在   子查询。

所以你不能在那里使用它。你必须在最后写query hint,如:

WITH cte AS
(
  SELECT ...
  FROM ...
)
SELECT ...
FROM cte
OPTION(RECOMPILE)

但正如你所说,它没有帮助。您也可以尝试使用EXISTS更改INNER JOIN,如:

SELECT * 
FROM MyTable P
JOIN InnerTable O
  ON O.TId = P.Id
WHERE ...;

对于更多可能的解决方案,您应该创建SqlFiddle,以便我们重新创建您的问题。

修改

如果您使用SQL Server 2012+,请考虑使用OFFSET FETCH而不是自定义分页解决方案,例如:

<强> Demo

DECLARE @PageSize INT = 5,
        @Page INT = 2;

SELECT *
FROM tab
ORDER BY id
OFFSET @PageSize * (@Page - 1) ROWS FETCH NEXT @PageSize ROWS ONLY

答案 1 :(得分:1)

我不认为你的问题是&#34;选项重新编译&#34;但很可能是您的SQL非常复杂以至于SQL Server无法正确估计行数,或者无法为所选计划创建正确的成本估算(或者计划创建超时)。

要弄清楚出现了什么问题,你应该查看实际的执行计划和统计输出,看看CTE中的代码被调用了多少次,是否存在估计和实际行数被关闭的运算符如果索引使用得当,至少10倍或者100倍。

默认情况下,CTE的结果不会缓存在任何地方,并且计划可能会使代码被调用数百或数千次,您可以尝试先在CTE中运行代码并将结果存储到临时文件中。正确索引的表,用于连接其余数据。