sql >> Base de Datos >  >> RDS >> Sqlserver

SQL Server elige valor aleatorio (o primero) con agregación

Hay un agregado no documentado llamado ANY lo cual no es una sintaxis válida pero es posible que llegue a aparecer en sus planes de ejecución. Sin embargo, esto no proporciona ninguna ventaja de rendimiento.

Asumiendo la siguiente tabla y estructura de índice

CREATE TABLE T
(
id int identity primary key,
[group] char(1) 
)

CREATE NONCLUSTERED INDEX ix ON T([group])

INSERT INTO T
SELECT TOP 1000000 CHAR( 65 + ROW_NUMBER() OVER (ORDER BY @@SPID) % 3)
FROM sys.all_objects o1, sys.all_objects o2, sys.all_objects o3

También he rellenado datos de muestra de modo que hay muchas filas por grupo.

Su consulta original

SELECT MAX(id),
       [group]
FROM   T
GROUP  BY [group]  

Da Table 'T'. Scan count 1, logical reads 1367 y el plano

  |--Stream Aggregate(GROUP BY:([[T].[group]) DEFINE:([Expr1003]=MAX([[T].[id])))
       |--Index Scan(OBJECT:([[T].[ix]), ORDERED FORWARD)

Reescrito para obtener ANY agregado...

;WITH cte AS
(
SELECT *,
        ROW_NUMBER() OVER (PARTITION BY [group] ORDER BY [group] ) AS RN
FROM T)
SELECT id,
       [group]
FROM    cte     
WHERE RN=1

Da Table 'T'. Scan count 1, logical reads 1367 y el plano

  |--Stream Aggregate(GROUP BY:([[T].[group]) DEFINE:([[T].[id]=ANY([[T].[id])))
       |--Index Scan(OBJECT:([[T].[ix]), ORDERED FORWARD)

Aunque potencialmente SQL Server podría dejar de procesar el grupo tan pronto como se encuentre el primer valor y pasar al siguiente, no lo hace. Todavía procesa todas las filas y las lecturas lógicas son las mismas.

Para este ejemplo particular con muchas filas en el grupo, una versión más eficiente sería una CTE recursiva.

WITH    RecursiveCTE
AS      (
        SELECT TOP 1 id, [group]
        FROM T
        ORDER BY [group]
        UNION   ALL
        SELECT  R.id, R.[group]
        FROM    (
                SELECT  T.*,
                        rn = ROW_NUMBER() OVER (ORDER BY (SELECT 0))
                FROM    T
                JOIN    RecursiveCTE R
                        ON  R.[group] < T.[group]
                ) R
        WHERE   R.rn = 1
        )
SELECT  *
FROM    RecursiveCTE
OPTION  (MAXRECURSION 0);

Lo que da

Table 'Worktable'. Scan count 2, logical reads 19
Table 'T'. Scan count 4, logical reads 12

Las lecturas lógicas son mucho menores ya que recupera la primera fila por grupo y luego busca en el siguiente grupo en lugar de leer una carga de registros que no contribuyen al resultado final.