¿Por qué es necesario este agregado de flujo?


12

Mira esta consulta. Es bastante simple (vea el final de la publicación para las definiciones de tabla e índice, y un script de repro):

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

Nota: el "Y 1 = (SELECCIONAR 1) es solo para evitar que esta consulta se parametrice automáticamente, lo que sentí que confundía el problema, aunque en realidad obtiene el mismo plan con o sin esa cláusula

Y aquí está el plan ( pegue el enlace del plan) :

planificar con un flujo agg

Como hay un "top 1" allí, me sorprendió ver el operador agregado de flujo. No me parece necesario, ya que se garantiza que solo habrá una fila.

Para probar esa teoría, probé esta consulta lógicamente equivalente:

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

Aquí está el plan para ese ( pegue el enlace del plan ):

planificar sin un flujo agg

Efectivamente, el grupo por plan puede sobrevivir sin el operador agregado de flujo.

Observe que ambas consultas leen "hacia atrás" desde el final del índice y hacen un "top 1" para obtener la revisión máxima.

¿Que me estoy perdiendo aqui? ¿El agregado de flujo realmente está funcionando en la primera consulta, o debería poder eliminarse (y es solo una limitación del optimizador que no lo es)?

Por cierto, me doy cuenta de que este no es un problema increíblemente práctico (ambas consultas informan 0 ms de CPU y tiempo transcurrido), solo tengo curiosidad acerca de los aspectos internos / comportamiento que se exhiben aquí.


Aquí está el código de configuración que ejecuté antes de ejecutar las dos consultas anteriores:

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

Respuestas:


16

Puede ver el papel de este agregado si ninguna fila coincide con la WHEREcláusula.

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

En ese caso, las filas cero entran en el agregado pero todavía emite una, ya que la semántica correcta debe regresar NULLen este caso.

ingrese la descripción de la imagen aquí

Este es un agregado escalar en lugar de uno vectorial.

Su consulta "lógicamente equivalente" no es equivalente. Agregar GROUP BY Idlo convertiría en un agregado vectorial y luego el comportamiento correcto sería no devolver filas.

Vea Diversión con agregados escalares y vectoriales para obtener más información al respecto.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.