Perché è necessario questo aggregato di stream?


12

Dai un'occhiata a questa domanda. È piuttosto semplice (vedere la fine del post per le definizioni di tabelle e indici e uno script di repro):

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

Nota: "AND 1 = (SELEZIONA 1) serve solo a impedire che questa query venga parametrizzata automaticamente, cosa che mi sembrava confondere il problema - in realtà ottiene lo stesso piano con o senza quella clausola

Ed ecco il piano ( incolla il link del piano) :

pianificare con un flusso agg

Dato che c'è un "top 1" lì, sono stato sorpreso di vedere l'operatore di aggregazione del flusso. Non mi sembra necessario, poiché è garantito che ci sia solo una riga.

Per testare questa teoria, ho provato questa query logicamente equivalente:

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

Ecco il piano per quello ( incolla il link del piano ):

piano senza un flusso agg

Abbastanza sicuro, il gruppo per piano è in grado di cavarsela senza l'operatore aggregato di flusso.

Si noti che entrambe le query leggono "indietro" dalla fine dell'indice ed eseguono un "primo 1" per ottenere la revisione massima.

Cosa mi sto perdendo qui? L'aggregato di flusso sta effettivamente funzionando nella prima query o dovrebbe essere in grado di essere eliminato (ed è solo una limitazione dell'ottimizzatore che non lo è)?

A proposito, mi rendo conto che questo non è un problema incredibilmente pratico (entrambe le query riportano 0 ms di CPU e tempo trascorso), sono solo curioso di vedere gli interni / il comportamento qui esposti.


Ecco il codice di installazione che ho eseguito prima di eseguire le due query sopra:

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

Risposte:


16

Puoi vedere il ruolo di questo aggregato se nessuna riga corrisponde alla WHEREclausola.

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

In tal caso, zero righe entrano nell'aggregato ma ne emette comunque una poiché NULLin questo caso deve essere restituita la semantica corretta .

inserisci qui la descrizione dell'immagine

Questo è un aggregato scalare rispetto a uno vettoriale.

La tua query "logicamente equivalente" non è equivalente. L'aggiunta GROUP BY Idlo renderebbe un aggregato vettoriale e quindi il comportamento corretto sarebbe quello di non restituire righe.

Vedi Fun with Scalar and Vector Aggregates per ulteriori informazioni al riguardo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.