Pertimbangkan dua fungsi ini:
ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C)
ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C)
Sejauh yang saya mengerti, mereka menghasilkan hasil yang persis sama. Dengan kata lain, urutan daftar kolom dalam PARTITION BY
klausa tidak masalah.
Jika ada indeks pada (A,B,C)
saya harapkan pengoptimal untuk menggunakan indeks ini di kedua varian.
Tetapi, yang mengejutkan, optimizer memutuskan untuk melakukan Sort ekstra eksplisit pada varian kedua.
Saya pernah melihatnya di SQL Server 2008 Standard dan SQL Server 2014 Express.
Ini adalah skrip lengkap yang saya gunakan untuk memperbanyaknya.
Mencoba di Microsoft SQL Server 2014 - 12.0.2000.8 (X64) 20 Februari 2014 20:04:26 Hak cipta (c) Microsoft Corporation Express Edition (64-bit) pada Windows NT 6.1 (Build 7601: Paket Layanan 1)
dan Microsoft SQL Server 2014 (SP1-CU7) (KB3162659) - 12.0.4459.0 (X64) 27 Mei 2016 15:33:17 Hak cipta (c) Edisi Microsoft Corporation Express (64-bit) pada Windows NT 6.1 (Build 7601: Layanan Paket 1)
dengan Estimator Kardinalitas lama dan baru dengan menggunakan OPTION (QUERYTRACEON 9481)
dan OPTION (QUERYTRACEON 2312)
.
Siapkan tabel, indeks, data sampel
CREATE TABLE [dbo].[T](
[ID] [int] IDENTITY(1,1) NOT NULL,
[A] [int] NOT NULL,
[B] [int] NOT NULL,
[C] [int] NOT NULL,
CONSTRAINT [PK_T] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_ABC] ON [dbo].[T]
(
[A] ASC,
[B] ASC,
[C] ASC
)WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF,
DROP_EXISTING = OFF,
ONLINE = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON)
GO
INSERT INTO [dbo].[T] ([A],[B],[C]) VALUES
(10, 20, 30),
(10, 21, 31),
(10, 21, 32),
(10, 21, 33),
(11, 20, 34),
(11, 21, 35),
(11, 21, 36),
(12, 20, 37),
(12, 21, 38),
(13, 21, 39);
Pertanyaan
SELECT -- AB
ID,A,B,C
,ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C) AS rnAB
FROM T
ORDER BY C
OPTION(RECOMPILE);
SELECT -- BA
ID,A,B,C
,ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C) AS rnBA
FROM T
ORDER BY C
OPTION(RECOMPILE);
SELECT -- both
ID,A,B,C
,ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C) AS rnAB
,ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C) AS rnBA
FROM T
ORDER BY C
OPTION(RECOMPILE);
Rencana eksekusi
PARTISI DENGAN A, B
PARTISI DENGAN B, A
Kedua
Seperti yang Anda lihat, paket kedua memiliki Sort tambahan. Ini dipesan oleh B, A, C. Pengoptimal, tampaknya, tidak cukup pintar untuk menyadari bahwa PARTITION BY B,A
itu sama dengan PARTITION BY A,B
dan mengurutkan kembali data.
Menariknya, kueri ketiga memiliki kedua varian ROW_NUMBER
di dalamnya dan tidak ada Sort tambahan! Rencananya sama dengan permintaan pertama. (Proyek Urutan memiliki ekspresi ekstra dalam Daftar Output untuk kolom tambahan, tetapi tidak ada Urutkan ekstra). Jadi, dalam kasus yang lebih rumit ini, pengoptimal tampak cukup pintar untuk menyadari bahwa PARTITION BY B,A
itu sama denganPARTITION BY A,B
.
Dalam kueri pertama dan ketiga, operator Pindai Indeks memiliki properti Dipesan: Benar, dalam kueri kedua itu salah.
Yang lebih menarik, jika saya menulis ulang kueri ketiga seperti ini (tukar dua kolom):
SELECT -- both
ID,A,B,C
,ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C) AS rnBA
,ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C) AS rnAB
FROM T
ORDER BY C
OPTION(RECOMPILE);
maka Sort tambahan muncul lagi!
Bisakah seseorang menjelaskan? Apa yang terjadi di optimizer di sini?