sql >> Database >  >> RDS >> Sqlserver

SQL Server kiest willekeurige (of eerste) waarde met aggregatie

Er is een ongedocumenteerd aggregaat genaamd ANY wat geen geldige syntaxis is, maar wel in uw uitvoeringsplannen kan verschijnen. Dit levert echter geen prestatievoordeel op.

Uitgaande van de volgende tabel en indexstructuur

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

Ik heb ook voorbeeldgegevens ingevuld zodat er veel rijen per groep zijn.

Uw oorspronkelijke vraag

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

Geeft Table 'T'. Scan count 1, logical reads 1367 en het plan

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

Herschreven om de ANY . te krijgen aggregaat...

;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

Geeft Table 'T'. Scan count 1, logical reads 1367 en het plan

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

Ook al kan SQL Server mogelijk stoppen met het verwerken van de groep zodra de eerste waarde is gevonden en naar de volgende gaan, maar dat gebeurt niet. Het verwerkt nog steeds alle rijen en de logische uitlezingen zijn hetzelfde.

Voor dit specifieke voorbeeld met veel rijen in de groep zou een meer efficiënte versie een recursieve CTE zijn.

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);

Wat geeft

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

De logische uitlezingen zijn veel minder omdat het de eerste rij per groep ophaalt en vervolgens naar de volgende groep zoekt in plaats van een lading records te lezen die niet bijdragen aan het uiteindelijke resultaat.




  1. Hoe POSITION() werkt in PostgreSQL

  2. Dubbele rijen verwijderen in SQL Server

  3. Voeg continu alle unieke combinaties van drie ID's in

  4. Beste manier om terugkerende taken/kalender te ontwikkelen/beheren/ontwerpen