Esiste un aggregato non documentato
chiamato ANY
che non è una sintassi valida ma è possibile apparire nei tuoi piani di esecuzione. Tuttavia, ciò non fornisce alcun vantaggio in termini di prestazioni.
Assumendo la seguente tabella e struttura dell'indice
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
Ho anche popolato con dati di esempio in modo tale che ci siano molte righe per gruppo.
La tua domanda originale
SELECT MAX(id),
[group]
FROM T
GROUP BY [group]
Fornisce Table 'T'. Scan count 1, logical reads 1367
e il piano
|--Stream Aggregate(GROUP BY:([[T].[group]) DEFINE:([Expr1003]=MAX([[T].[id])))
|--Index Scan(OBJECT:([[T].[ix]), ORDERED FORWARD)
Riscritto per ottenere ANY
aggregato...
;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
Fornisce Table 'T'. Scan count 1, logical reads 1367
e il piano
|--Stream Aggregate(GROUP BY:([[T].[group]) DEFINE:([[T].[id]=ANY([[T].[id])))
|--Index Scan(OBJECT:([[T].[ix]), ORDERED FORWARD)
Anche se potenzialmente SQL Server potrebbe interrompere l'elaborazione del gruppo non appena viene trovato il primo valore e passare a quello successivo, non è così. Elabora ancora tutte le righe e le letture logiche sono le stesse.
Per questo esempio particolare con molte righe nel gruppo, una versione più efficiente sarebbe un CTE ricorsivo.
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);
Che dà
Table 'Worktable'. Scan count 2, logical reads 19
Table 'T'. Scan count 4, logical reads 12
Le letture logiche sono molto inferiori in quanto recupera la prima riga per gruppo, quindi cerca nel gruppo successivo anziché leggere un carico di record che non contribuiscono al risultato finale.