Configurar
Para esta demonstração, estou usando a versão 2013 do banco de dados Stack Overflow e o SQL Server 2022 CTP2, mas é válido voltar ao SQL Server 2017, que é o mais antigo que eu gostaria de verificar.
Função Um
Para esta função, o SQL Server rastreia o tempo de execução gasto na função:
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;
Aqui está o plano de consulta e execução:
SELECT
u.DisplayName,
TotalScore =
(
SELECT
ss.TotalScore
FROM dbo.ScoreStats(u.Id) AS ss
)
FROM dbo.Users AS u
WHERE u.Reputation >= 1000000;
Você pode ver que tanto no plano de consulta quanto na propriedade Query Time Stats, o tempo é rastreado com precisão.
Função Dois
Aqui está a segunda função, onde isso não acontece:
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;
Aqui está o plano de consulta e execução:
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;
Nesta consulta, o tempo não é rastreado com precisão no plano de execução gráfico, mas é rastreado na propriedade Query Time Stats.
Função Dois em MAXDOP 1
Mesmo quando forçado serial, o tempo não é rastreado com precisão:
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);
El Problema
De volta à questão em questão: Por que o tempo é rastreado em um plano de consulta com precisão, mas não no outro?
Isso é uma consequência do uso da execução de TVF intercalada .
Seu primeiro exemplo não se qualifica para execução intercalada, mas o segundo sim. O nó raiz do segundo plano de exemplo tem a propriedade:
O nó de população TVF no segundo exemplo tem:
Executando uma consulta de teste com uma dica para desativar esse recurso:
Fornece um plano incluindo o tempo para preencher o TVF:
Esse problema ocorre apenas na primeira execução de uma instrução qualificada para execução de TVF intercalada. O SQL Server executa a parte de população TVF do plano para obter uma estimativa precisa de cardinalidade durante a otimização da consulta . O restante do plano não é compilado e otimizado até que essas informações sejam obtidas.
Após a conclusão da compilação, o SQL Server não repete o trabalho de preencher a variável da tabela para a primeira execução , pois isso duplicaria o trabalho já feito (durante a otimização). Infelizmente, pular a população da tabela em tempo de execução significa que as informações de desempenho para a população da variável da tabela não estão disponíveis da maneira usual.
Em execuções subsequentes (onde o plano é reutilizado), o SQL Server executa a etapa de preenchimento de variável de tabela como parte da execução de consulta regular, portanto, os números de desempenho do tempo de execução aparecem conforme o esperado na saída do plano de exibição.
Se você executar seu segundo exemplo novamente, reutilizando o plano armazenado em cache, verá informações completas sobre o desempenho do tempo de execução.
Observação: esse comportamento está relacionado especificamente ao recurso de execução de TVF intercalado do Intelligent Query Processing. Não é uma consequência do comportamento normal de armazenamento em cache dos TVFs, conforme explico em minhas perguntas e respostas auto-respondidas. O SQL Server armazena em cache o resultado de uma função com valor de tabela de várias instruções? .
O tempo de rastreamento em execuções paralelas (operadores de câmbio e threads filho) não é trivial e não pode ser realmente preciso com a implementação atual. O outro plano é serial, tão fácil de rastrear com precisão. O que faz você ter certeza de que é sobre uma função? Qualquer execução paralela de modo de linha não será rastreada com precisão por operador (em termos de tempo).