使用 "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.
- SQL 服务器 2014 SP2
- myView 是一个递归 CTE
- 将第一个查询更改为
INNER JOIN
模型或 EXISTS
模型没有帮助
- 将
IX_1 Index
更改为聚簇索引没有帮助
- 使用
FORSEEK
没有帮助
P.S.2
两者的执行计划可以在这里下载:https://www.dropbox.com/s/pas1ovyamqojhba/Query-With-In.sqlplan?dl=0
中的执行计划
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 中的值更好地优化文字值。结果就是执行计划变了
如果 IN
和 JOIN
都无法解决问题,那么您可能必须 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
,尽管优化器会为您完成。
我有一个临时 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.
- SQL 服务器 2014 SP2
- myView 是一个递归 CTE
- 将第一个查询更改为
INNER JOIN
模型或EXISTS
模型没有帮助 - 将
IX_1 Index
更改为聚簇索引没有帮助 - 使用
FORSEEK
没有帮助
P.S.2
两者的执行计划可以在这里下载:https://www.dropbox.com/s/pas1ovyamqojhba/Query-With-In.sqlplan?dl=0
中的执行计划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 中的值更好地优化文字值。结果就是执行计划变了
如果 IN
和 JOIN
都无法解决问题,那么您可能必须 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
,尽管优化器会为您完成。