sql >> Database teknologi >  >> RDS >> Sqlserver

SQL Server vælg tilfældig (eller første) værdi med aggregering

Der er en udokumenteret aggregat kaldet ANY som ikke er gyldig syntaks, men det er muligt at få vist i dine eksekveringsplaner. Dette giver dog ingen ydeevnefordele.

Forudsat følgende tabel- og indeksstruktur

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
 

Jeg har også udfyldt med eksempeldata, så der er mange rækker pr. gruppe.

Din oprindelige forespørgsel

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

Giver Table 'T'. Scan count 1, logical reads 1367 og planen

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

Omskrevet for at få ANY samlet...

;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
 

Giver Table 'T'. Scan count 1, logical reads 1367 og planen

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

Selvom SQL Server potentielt kunne stoppe med at behandle gruppen, så snart den første værdi er fundet og springe til den næste, gør den det ikke. Den behandler stadig alle rækker, og de logiske læsninger er de samme.

For dette særlige eksempel med mange rækker i gruppen ville en mere effektiv version være en rekursiv 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);
 

Hvilket giver

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

De logiske læsninger er meget mindre, da den henter den første række pr. gruppe og derefter søger ind i den næste gruppe i stedet for at læse en mængde poster, der ikke bidrager til det endelige resultat.




  1. Relationelle vs. ikke-relationelle databaser – Del 1

  2. Brug af markører til at søge i PostgreSQL

  3. Oracle skelner ikke mellem nul og tomme strenge?

  4. Hvordan man læser og gemmer XML-data i databasen