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?
sumber
Jawaban:
Tampaknya tidak ada "jawaban" definitif yang baik untuk pertanyaan "apa yang terjadi di optimiser", kecuali jika Anda adalah pengembangnya dan mengetahui internalnya.
Saya akan mengumpulkan komentar di sini.
Secara keseluruhan, tampaknya terlalu sulit untuk menyebutnya bug, karena hasil akhir dari kueri itu benar. Dalam beberapa kasus, rencana eksekusi tidak optimal. ypercubeᵀᴹ , Martin Smith dan Aaron Bertrand menyebutnya "optimasi yang terlewatkan".
Ada artikel yang agak terkait Indeks Penurun. Urutan indeks, paralelisme, dan perhitungan peringkat oleh Itzik Ben-Gan. Di sana Itzik membahas indeks yang menurun dan juga memberikan contoh bagaimana arah definisi indeks mempengaruhi fungsi jendela dengan partisi. Dia menunjukkan contoh pertanyaan dan membuat rencana dengan
ROW_NUMBER
yang memiliki operator tambahan yang bisa dihindari pengoptimal.Bagi saya hasil praktisnya adalah menjaga keunikan optimizer ini dalam pikiran. Saat menggunakan
PARTITION BY
dalam fungsi jendela selalu mencoba untuk mencocokkan urutan di mana Anda mencantumkan kolom diPARTITION BY
dengan urutan di mana mereka terdaftar dalam indeks. Meskipun itu seharusnya tidak masalah.Sisi lain dari tindakan pencegahan ini adalah ketika Anda meninjau indeks Anda dan memutuskan untuk menukar beberapa kolom di sekitar dalam definisi indeks. Ketahuilah bahwa Anda dapat secara tidak sengaja memengaruhi beberapa kueri yang ada yang tampaknya tidak akan terpengaruh. Inilah sebenarnya bagaimana saya memperhatikan keunikan pengoptimal ini.
Jika tidak, pengoptimal mungkin tidak dapat menggunakan indeks secara maksimal. Bahkan jika optimiser memang memilih rencana optimal, rencana tersebut dapat berubah menjadi kurang optimal dengan sedikit perubahan tidak bersalah pada kueri, seperti mengubah urutan kolom dalam
SELECT
pernyataan.sumber