使用“与临时表连接”时的性能损失与“常量值的IN子句”

时间:2016-10-29 14:13:01

标签: sql sql-server database-performance

我有一个临时表,有两个这样的记录:

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

以及相关索引:

Create nonclustered index IX_1 on #T(id)

以下查询需要 4000ms 才能运行:

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 SERVER 2014 SP2
  2. myView是一个递归CTE
  3. 将第一个查询更改为INNER JOIN模型或EXISTS模型无效
  4. IX_1 Index更改为群集索引无效
  5. 使用FORSEEK没有帮助
  6. 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)
    

2 个答案:

答案 0 :(得分:3)

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

enter image description here

当他们来自桌子时拒绝这样做。

您可以创建表格类型

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,尽管优化商会为您执行此操作。

答案 1 :(得分:1)

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

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

我怀疑这会有所帮助。

问题是SQL Server可以比表中的值更好地优化文字值。结果是执行计划发生了变化。

如果INJOIN都没有解决问题,那么您可能需要调整视图的定义以提高性能。