我有一个临时表,其中有两条记录,如下所示:
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)
为什么会出现如此巨大的差异?如何将第一个查询更改为与第二个查询一样快?
附注
- SQL Server 2014 SP2
- myView 是一个递归 CTE
- 将第一个查询更改为
INNER JOIN
模型或EXISTS
模型没有帮助 - 将
IX_1 索引
更改为簇索引没有帮助 - 使用
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 部分的索引查找中。
当它们来自 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/