sql - 与 "join with temp table "相比,使用 "IN clause with constant values"时会出现性能损失

标签 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)

为什么会出现如此巨大的差异?如何将第一个查询更改为与第二个查询一样快?

附注

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

PS2

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

Paste the Plan中的执行计划

附注3

View 定义是:

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)

最佳答案

在您的良好计划中,它能够将文字值直接插入递归 CTE anchor 部分的索引查找中。

enter image description here

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

您可以创建一个表格类型

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

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

然后就这样调用它

DECLARE @AncestorIds INTEGERSET;

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

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

内联 TVF 与 View 非常相似,但带有

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

在递归 CTE 的 anchor 部分。

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,尽管优化器会为您完成此操作。

关于sql - 与 "join with temp table "相比,使用 "IN clause with constant values"时会出现性能损失,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40319958/

相关文章:

mysql - 如何在同一天的 mySQL 中只获取值的总和

python - 带有内连接查询的 web.py 数据库

c# - Entity Framework 正在执行太多查询

sql - 在特定单词后返回 SUBSTRING 并在第一个引号处结束

sql - 如何查找查询执行的次数?

database - DynamoDB 计算操作容量单位消耗

sql - T - 数字范围的 SQL 语句

sql - 仅当字符串长度大于 2 时才选择列

sql - 分组和划分 Netezza

mysql - MySQL IN 子句是否多次执行子查询?