设置
对于这个演示,我使用的是2013 版本的 Stack Overflow 数据库和 SQL Server 2022 CTP2,但它可以追溯到 SQL Server 2017,这是我想检查的。
功能一
对于此函数,SQL Server 跟踪函数中花费的执行时间:
CREATE OR ALTER FUNCTION
dbo.ScoreStats
(
@UserId int
)
RETURNS
@out table
(
TotalScore bigint
)
WITH SCHEMABINDING
AS
BEGIN
INSERT
@out
(
TotalScore
)
SELECT
TotalScore =
SUM(x.Score)
FROM
(
SELECT
Score =
SUM(p.Score)
FROM dbo.Posts AS p
WHERE p.OwnerUserId = @UserId
UNION ALL
SELECT
Score =
SUM(c.Score)
FROM dbo.Comments AS c
WHERE c.UserId = @UserId
) AS x;
RETURN;
END;
这是查询和执行计划:
SELECT
u.DisplayName,
TotalScore =
(
SELECT
ss.TotalScore
FROM dbo.ScoreStats(u.Id) AS ss
)
FROM dbo.Users AS u
WHERE u.Reputation >= 1000000;
您可以看到,在查询计划和 Query Time Stats 属性中都准确地跟踪了时间。
功能二
这是第二个功能,它不会发生:
CREATE OR ALTER FUNCTION
dbo.VoteStats()
RETURNS
@out table
(
PostId int,
UpVotes int,
DownVotes int,
UpMultipier AS
UpVotes * 2
)
WITH SCHEMABINDING
AS
BEGIN
INSERT
@out
(
PostId,
UpVotes,
DownVotes
)
SELECT
v.PostId,
UpVotes =
SUM
(
CASE v.VoteTypeId
WHEN 2
THEN 1
ELSE 0
END
),
DownVotes =
SUM
(
CASE v.VoteTypeId
WHEN 3
THEN 1
ELSE 0
END
)
FROM dbo.Votes AS v
GROUP BY
v.PostId;
RETURN;
END;
这是查询和执行计划:
SELECT TOP (100)
p.Id,
vs.UpVotes,
vs.DownVotes
FROM dbo.VoteStats() AS vs
JOIN dbo.Posts AS p
ON vs.PostId = p.Id
WHERE vs.DownVotes > vs.UpMultipier
AND p.CommunityOwnedDate IS NULL
AND p.ClosedDate IS NULL
ORDER BY vs.UpVotes DESC;
在此查询中,时间没有在图形执行计划中准确跟踪,而是在 Query Time Stats 属性中进行跟踪。
MAXDOP 1 处的功能二
即使是强制连载,也无法准确跟踪时间:
SELECT TOP (100)
p.Id,
vs.UpVotes,
vs.DownVotes
FROM dbo.VoteStats() AS vs
JOIN dbo.Posts AS p
ON vs.PostId = p.Id
WHERE vs.DownVotes > vs.UpMultipier
AND p.CommunityOwnedDate IS NULL
AND p.ClosedDate IS NULL
ORDER BY vs.UpVotes DESC
OPTION(MAXDOP 1);
问题
回到手头的问题:为什么在一个查询计划中可以准确地跟踪时间,而在另一个查询计划中却没有?
这是使用交错 TVF 执行的结果。
您的第一个示例不符合交错执行的条件,但第二个示例可以。第二个示例计划的根节点具有以下属性:
第二个示例中的 TVF 人口节点具有:
运行带有禁用该功能提示的测试查询:
给出一个计划,包括填充 TVF 的时间:
此问题仅在第一次执行符合交错 TVF 执行条件的语句时发生。SQL Server 执行计划的 TVF 填充部分,以在查询优化期间获得准确的基数估计。在获得该信息之前,不会编译和优化计划的其余部分。
编译完成后,SQL Server 不会为第一次执行重复填充表变量的工作,因为这会重复已经完成的工作(在优化期间)。不幸的是,在运行时跳过表填充意味着表变量填充的性能信息无法以通常的方式获得。
在后续执行中(重用计划),SQL Server确实将表变量填充步骤作为常规查询执行的一部分运行,因此运行时性能数字按预期显示在 showplan 输出中。
如果再次运行第二个示例,重用缓存的计划,您将看到完整的运行时性能信息。
注意:此行为与智能查询处理的交错 TVF 执行功能特别相关。这不是 TVF 正常缓存行为的结果,正如我在自我回答的问答中解释的那样SQL Server 缓存多语句表值函数的结果吗?.
并行执行(交换运算符和子线程)中的跟踪时间并非易事,并且在当前实现中实际上并不准确。另一个计划是连续的,因此很容易准确跟踪。是什么让你确定它是关于一个函数的?任何行模式并行执行都不会被每个操作员准确跟踪(按时间)。