Warum ist dieses Stream-Aggregat erforderlich?

12

Überprüfen Sie diese Abfrage. Es ist ziemlich einfach (siehe Ende des Beitrags für Tabellen- und Indexdefinitionen und ein Reproskript):

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

Hinweis: Mit "AND 1 = (SELECT 1)" soll nur verhindert werden, dass diese Abfrage automatisch parametrisiert wird. Ich hatte den Eindruck, dass dies das Problem verwirrt hat. Es wird jedoch derselbe Plan mit oder ohne diese Klausel erstellt

Und hier ist der Plan ( fügen Sie den Plan-Link ein) :

plan mit einem stream agg

Da es dort eine "Top 1" gibt, war ich überrascht, den Stream-Aggregat-Operator zu sehen. Es scheint mir nicht notwendig zu sein, da es garantiert nur eine Reihe gibt.

Um diese Theorie zu testen, habe ich diese logisch äquivalente Abfrage ausprobiert:

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

Hier ist der Plan für diesen ( fügen Sie den Plan-Link ein ):

plan ohne stream agg

Sicher genug, die Gruppe nach Plan kommt ohne den Stream-Aggregat-Operator aus.

Beachten Sie, dass beide Abfragen ab dem Ende des Index "Rückwärts" lesen und eine "Top 1" ausführen, um die maximale Revision zu erhalten.

Was vermisse ich hier? Funktioniert das Stream-Aggregat tatsächlich in der ersten Abfrage oder sollte es beseitigt werden können (und es ist nur eine Einschränkung des Optimierers, die es nicht ist)?

Übrigens stelle ich fest, dass dies kein unglaublich praktisches Problem ist (beide Abfragen melden 0 ms CPU und verstrichene Zeit). Ich bin nur neugierig auf die Interna / das Verhalten, das hier gezeigt wird.


Hier ist der Setup-Code, den ich ausgeführt habe, bevor ich die beiden obigen Abfragen ausgeführt habe:

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
quelle

Antworten:

16

Sie können die Rolle dieses Aggregats sehen, wenn keine Zeilen mit der WHEREKlausel übereinstimmen .

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

In diesem Fall werden keine Zeilen in das Aggregat eingefügt, es wird jedoch weiterhin eine ausgegeben, da NULLin diesem Fall die korrekte Semantik zurückgegeben werden soll .

Bildbeschreibung hier eingeben

Dies ist ein skalares Aggregat im Gegensatz zu einem Vektor.

Ihre "logisch äquivalente" Abfrage ist nicht äquivalent. Das Hinzufügen GROUP BY Idwürde es zu einem Vektoraggregat machen und dann wäre es das richtige Verhalten, keine Zeilen zurückzugeben.

Weitere Informationen finden Sie unter Spaß mit Skalar- und Vektoraggregaten .

Martin Smith
quelle