Por que esse fluxo agregado é necessário?

12

Confira esta consulta. É bem simples (consulte o final da postagem para obter definições de tabela e índice e um script de reprodução):

SELECT MAX(Revision)
FROM dbo.TheOneders
WHERE Id = 1 AND 1 = (SELECT 1);

Nota: o "AND 1 = (SELECT 1) é apenas para impedir que esta consulta seja parametrizada automaticamente, o que eu senti como se estivesse confundindo o problema - na verdade, ele obtém o mesmo plano com ou sem essa cláusula

E aqui está o plano ( cole o link do plano) :

planejar com um stream agg

Como existe um "top 1" lá, fiquei surpreso ao ver o operador agregado de fluxo. Não me parece necessário, pois é garantido que haja apenas uma linha.

Para testar essa teoria, tentei essa consulta logicamente equivalente:

SELECT MAX(Revision)
FROM dbo.TheOneders
WHERE Id = 1
GROUP BY Id;

Aqui está o plano para esse ( cole o link do plano ):

planejar sem um fluxo agg

Com certeza, o grupo por plano é capaz de sobreviver sem o operador agregado de fluxo.

Observe que ambas as consultas são lidas "para trás" no final do índice e são "1 principais" para obter a revisão máxima.

O que estou perdendo aqui? O fluxo agregado está realmente funcionando na primeira consulta ou deve ser eliminado (e é apenas uma limitação do otimizador que não está)?

By the way, eu percebo que este não é um problema incrivelmente prático (ambas as consultas relatam 0 ms de CPU e tempo decorrido), só estou curioso sobre os internos / comportamento que estão sendo exibidos aqui.


Aqui está o código de instalação que eu executei antes de executar as duas consultas acima:

DROP TABLE IF EXISTS dbo.TheOneders;
GO

CREATE TABLE dbo.TheOneders
(
    Id INT NOT NULL,
    Revision SMALLINT NOT NULL,
    Something NVARCHAR(23),

    CONSTRAINT PK_TheOneders PRIMARY KEY NONCLUSTERED (Id, Revision)
);
GO

INSERT INTO dbo.TheOneders
    (Id, Revision, Something)
SELECT DISTINCT TOP 1000 
    1, m.message_id, 'Do...'
FROM sys.messages m
ORDER BY m.message_id
OPTION (MAXDOP 1);

INSERT INTO dbo.TheOneders
    (Id, Revision, Something)
SELECT DISTINCT TOP 100 
    2, m.message_id, 'Do that thing you do...'
FROM sys.messages m
ORDER BY m.message_id
OPTION (MAXDOP 1);
GO
Josh Darnell
fonte

Respostas:

16

Você pode ver o papel desse agregado se nenhuma linha corresponder à WHEREcláusula.

SELECT MAX(Revision)
FROM   dbo.TheOneders
WHERE  Id = 1
       AND 1 = 1 /*To avoid auto parameterisation*/
       AND Id%3 = 4  /*always false*/

Nesse caso, zero linhas são agregadas, mas ainda assim emitem uma, pois a semântica correta deve retornar NULLnesse caso.

insira a descrição da imagem aqui

Este é um agregado escalar em oposição a um vetor.

Sua consulta "logicamente equivalente" não é equivalente. A adição GROUP BY Idtornaria um vetor agregado e, em seguida, o comportamento correto seria retornar nenhuma linha.

Consulte Diversão com agregados escalares e vetoriais para saber mais sobre isso.

Martin Smith
fonte