Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

SQL Server memilih nilai acak (atau pertama) dengan agregasi

Ada agregat tidak berdokumen disebut ANY yang bukan sintaks yang valid tetapi dimungkinkan untuk muncul dalam rencana eksekusi Anda. Namun, ini tidak memberikan keuntungan kinerja apa pun.

Dengan asumsi tabel dan struktur indeks berikut

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

Saya juga telah mengisi data sampel sehingga ada banyak baris per grup.

Permintaan asli Anda

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

Memberikan Table 'T'. Scan count 1, logical reads 1367 dan rencananya

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

Ditulis ulang untuk mendapatkan ANY agregat...

;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

Memberikan Table 'T'. Scan count 1, logical reads 1367 dan rencananya

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

Meskipun berpotensi SQL Server dapat berhenti memproses grup segera setelah nilai pertama ditemukan dan melompat ke yang berikutnya tidak. Itu masih memproses semua baris dan pembacaan logisnya sama.

Untuk contoh khusus ini dengan banyak baris dalam grup, versi yang lebih efisien adalah CTE rekursif.

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

Yang memberi

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

Pembacaan logis jauh lebih sedikit karena mengambil baris pertama per grup kemudian mencari ke grup berikutnya daripada membaca banyak catatan yang tidak berkontribusi pada hasil akhir.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Daftar Format Tanggal Tersedia dengan CONVERT() di SQL Server

  2. Hasil aktif tidak berisi bidang menggunakan PDO dengan MS SQL

  3. Periksa apakah suatu parameter nol atau kosong dalam prosedur tersimpan

  4. Tentukan apakah Parameter SP memiliki Nilai Default di T-SQL

  5. Bagaimana cara mendapatkan hasil float dengan membagi dua nilai integer menggunakan T-SQL?