Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

SQL Server wybiera losową (lub pierwszą) wartość za pomocą agregacji

Istnieje nieudokumentowany agregat o nazwie ANY który nie jest poprawną składnią, ale może pojawić się w twoich planach wykonania. Nie zapewnia to jednak żadnej przewagi wydajności.

Zakładając poniższą tabelę i strukturę indeksu

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

Wypełniłem również przykładowymi danymi, tak że jest wiele wierszy na grupę.

Twoje pierwotne zapytanie

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

Daje Table 'T'. Scan count 1, logical reads 1367 i plan

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

Przepisany, aby uzyskać ANY agregacja...

;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

Daje Table 'T'. Scan count 1, logical reads 1367 i plan

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

Mimo że potencjalnie SQL Server może przestać przetwarzać grupę zaraz po znalezieniu pierwszej wartości i przejść do następnej, czego nie robi. Nadal przetwarza wszystkie wiersze, a odczyty logiczne są takie same.

W tym konkretnym przykładzie z wieloma wierszami w grupie bardziej wydajną wersją byłoby rekurencyjne 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 daje

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

Odczyty logiczne są znacznie mniejsze, ponieważ pobiera pierwszy wiersz na grupę, a następnie przeszukuje następną grupę, zamiast odczytywać ładunek rekordów, które nie mają wpływu na końcowy wynik.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. 5 sposobów na zliczanie liczby tabel zdefiniowanych przez użytkownika w bazie danych SQL Server

  2. Jak mogę ustrukturyzować zapytanie, aby dać mi tylko wiersze, które pasują do WSZYSTKICH wartości na liście identyfikatorów CSV w T-SQL

  3. LINQ to SQL w każdym n-tym wierszu z tabeli

  4. Procedura oczekuje parametru, którego nie podano

  5. Ile rozmiaru ma wartość Null w SQL Server