使用 "join with temp table " 而不是 "IN clause with constant values" 时的性能损失

performance penalty when using "join with temp table " in contrast of "IN clause with constant values"

我有一个临时 table 有两条这样的记录:

select * into #Tbl from (select 1 id union select 2) tbl

还有相关索引:

Create nonclustered index IX_1 on #T(id)

以下查询需要 4000 毫秒 到 运行:

SELECT   AncestorId
FROM     myView 
WHERE    AncestorId =ANY(select id from #t)

但是等效的查询(使用 IN 和文字值)只需要 3ms 到 运行!:

SELECT  ProjectStructureId
FROM    myView
WHERE   AncestorId in (1,2)

为什么会有这么大的差异?我怎样才能使第一个查询与第二个查询一样快?

P.S.

  1. SQL 服务器 2014 SP2
  2. myView 是一个递归 CTE
  3. 将第一个查询更改为 INNER JOIN 模型或 EXISTS 模型没有帮助
  4. IX_1 Index 更改为聚簇索引没有帮助
  5. 使用 FORSEEK 没有帮助

P.S.2

两者的执行计划可以在这里下载:https://www.dropbox.com/s/pas1ovyamqojhba/Query-With-In.sqlplan?dl=0

Paste the Plan

中的执行计划

P.S。 3

视图定义是:

ALTER VIEW [dbo].[myView] 
AS
WITH parents AS (SELECT        main.Id, main.NodeTypeCode, main.ParentProjectStructureId AS DirectParentId, parentInfo.Id AS AncestorId, parentInfo.ParentProjectStructureId AS AncestorParentId, CASE WHEN main.NodeTypeCode <> IsNull(parentInfo.NodeTypeCode, 0) 
                                                   THEN 1 ELSE 0 END AS AncestorTypeDiffLevel
                          FROM            dbo.ProjectStructures AS main LEFT OUTER JOIN
                                                   dbo.ProjectStructures AS parentInfo ON main.ParentProjectStructureId = parentInfo.Id
                          UNION ALL
                          SELECT        m.Id, m.NodeTypeCode, m.ParentProjectStructureId, parents.AncestorId, parents.AncestorParentId,
                                                   CASE WHEN m.NodeTypeCode <> parents.NodeTypeCode THEN AncestorTypeDiffLevel + 1 ELSE AncestorTypeDiffLevel END AS AncestorTypeDiffLevel

                          FROM            dbo.ProjectStructures AS m INNER JOIN
                                                   parents ON m.ParentProjectStructureId = parents.Id)
    SELECT          ISNULL(Id, - 1) AS ProjectStructureId, 
                    ISNULL(NodeTypeCode,-1) NodeTypeCode,
                    DirectParentId, 
                    ISNULL(AncestorId, - 1) AS AncestorId, 
                    AncestorParentId, 
                    AncestorTypeDiffLevel
    FROM            parents
    WHERE        (AncestorId IS NOT NULL)

我只想说我会把查询写成:

SELECT AncestorId
FROM myView 
WHERE AncestorId IN (select id from #t);

我怀疑这是否有帮助。

问题是 SQL 服务器可以比 table 中的值更好地优化文字值。结果就是执行计划变了

如果 INJOIN 都无法解决问题,那么您可能必须 fiddle 定义视图以提高性能。

在你的好计划中,它能够将文字值直接推入递归 CTE 的锚点部分的索引查找中。

当它们来自 table 时,它拒绝这样做。

您可以创建一个 table 类型

CREATE TYPE IntegerSet AS TABLE 
( 
Integer int PRIMARY KEY WITH (IGNORE_DUP_KEY = ON)
);

然后将其传递给内联 TVF,以便直接在锚定部分使用它。

然后就像

那样称呼它
DECLARE @AncestorIds INTEGERSET;

INSERT INTO @AncestorIds
VALUES      (1),
            (2);

SELECT *
FROM   [dbo].[myFn](@AncestorIds); 

内联 TVF 与视图大致相同,但

 WHERE parentInfo.Id IN (SELECT Integer FROM @AncestorIds)

在递归 CTE 的锚点部分。

CREATE FUNCTION [dbo].[myFn]
(
@AncestorIds IntegerSet READONLY
)
RETURNS TABLE
AS
RETURN 
  WITH parents
       AS (SELECT  /*omitted for clarity*/
           WHERE parentInfo.Id IN (SELECT Integer FROM @AncestorIds)
           UNION ALL
           SELECT/* Rest omitted for clarity*/

您也可以将 LEFT JOIN 更改为 INNER JOIN,尽管优化器会为您完成。