ROW_NUMBER () LEBIH (PARTISI DENGAN B, A PESAN DENGAN C) tidak menggunakan indeks pada (A, B, C)

12

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 BYklausa 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

AB

PARTISI DENGAN B, A

BA

Kedua

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,Aitu sama dengan PARTITION BY A,Bdan mengurutkan kembali data.

Menariknya, kueri ketiga memiliki kedua varian ROW_NUMBERdi 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,Aitu 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?

Vladimir Baranov
sumber
Komentar diarsipkan .
Paul White 9

Jawaban:

2

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".

  • Sepertinya GROUP BY a,bdan GROUP BY b,amenghasilkan rencana yang sama tetapi PARTITION BYtidak dapat menggunakan transformasi yang sama

  • Ada juga optimasi lain yang hilang di mana fungsi jendela dengan spesifikasi jendela yang sama dapat memiliki operasi semacam tambahan jika dipisahkan dalam daftar pilih oleh satu dengan spesifikasi yang berbeda.

  • Ya, ini sepertinya optimasi lainnya yang terlewatkan, dan ada banyak dari itu. Pengoptimal ditulis oleh manusia dan tidak sempurna


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_NUMBERyang memiliki operator tambahan yang bisa dihindari pengoptimal.


Bagi saya hasil praktisnya adalah menjaga keunikan optimizer ini dalam pikiran. Saat menggunakan PARTITION BYdalam 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 SELECTpernyataan.

Vladimir Baranov
sumber