sql >> Databáze >  >> RDS >> Sqlserver

SQL Server vybere náhodnou (nebo první) hodnotu s agregací

Existuje nezdokumentovaný agregát s názvem ANY což není platná syntaxe, ale je možné, aby se objevila ve vašich prováděcích plánech. To však neposkytuje žádnou výkonnostní výhodu.

Za předpokladu následující tabulky a struktury indexu

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

Také jsem naplnil ukázková data, takže existuje mnoho řádků na skupinu.

Váš původní dotaz

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

Dává Table 'T'. Scan count 1, logical reads 1367 a plán

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

Přepsáno, abyste získali ANY agregát...

;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

Dává Table 'T'. Scan count 1, logical reads 1367 a plán

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

I když by potenciálně SQL Server mohl zastavit zpracování skupiny, jakmile je nalezena první hodnota, a přeskočit na další, ne. Stále zpracovává všechny řádky a logické čtení je stejné.

Pro tento konkrétní příklad s mnoha řádky ve skupině by efektivnější verzí byla rekurzivní CTE.

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

Což dává

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

Logická čtení jsou mnohem menší, protože načte první řádek na skupinu a poté hledá další skupinu, místo aby četl hromadu záznamů, které nepřispívají ke konečnému výsledku.




  1. Stránkování pro zobrazení maximální hodnoty a omezení zbytku

  2. Kontejner MySQL Docker neukládá data do nového obrazu

  3. Kdy Postgres kontroluje jedinečná omezení?

  4. Oracle + dbunit získává výjimku AmbiguousTableNameException