Saya memiliki kolom yang tetap dihitung pada tabel yang hanya terdiri kolom gabungan, misalnya
CREATE TABLE dbo.T
(
ID INT IDENTITY(1, 1) NOT NULL CONSTRAINT PK_T_ID PRIMARY KEY,
A VARCHAR(20) NOT NULL,
B VARCHAR(20) NOT NULL,
C VARCHAR(20) NOT NULL,
D DATE NULL,
E VARCHAR(20) NULL,
Comp AS A + '-' + B + '-' + C PERSISTED NOT NULL
);
Dalam hal Comp
ini tidak unik, dan D adalah valid dari tanggal setiap kombinasi A, B, C
, oleh karena itu saya menggunakan kueri berikut untuk mendapatkan tanggal akhir untuk masing-masing A, B, C
(pada dasarnya tanggal mulai berikutnya untuk nilai Comp yang sama):
SELECT t1.ID,
t1.Comp,
t1.D,
D2 = ( SELECT TOP 1 t2.D
FROM dbo.T t2
WHERE t2.Comp = t1.Comp
AND t2.D > t1.D
ORDER BY t2.D
)
FROM dbo.T t1
WHERE t1.D IS NOT NULL -- DON'T CARE ABOUT INACTIVE RECORDS
ORDER BY t1.Comp;
Saya kemudian menambahkan indeks ke kolom yang dihitung untuk membantu dalam permintaan ini (dan juga yang lain):
CREATE NONCLUSTERED INDEX IX_T_Comp_D ON dbo.T (Comp, D) WHERE D IS NOT NULL;
Namun rencana kueri mengejutkan saya. Saya akan berpikir bahwa karena saya memiliki klausa di mana menyatakan itu D IS NOT NULL
dan saya menyortir Comp
, dan tidak merujuk kolom di luar indeks bahwa indeks pada kolom dihitung dapat digunakan untuk memindai t1 dan t2, tetapi saya melihat indeks berkerumun memindai.
Jadi saya memaksakan penggunaan indeks ini untuk melihat apakah itu menghasilkan rencana yang lebih baik:
SELECT t1.ID,
t1.Comp,
t1.D,
D2 = ( SELECT TOP 1 t2.D
FROM dbo.T t2
WHERE t2.Comp = t1.Comp
AND t2.D > t1.D
ORDER BY t2.D
)
FROM dbo.T t1 WITH (INDEX (IX_T_Comp_D))
WHERE t1.D IS NOT NULL
ORDER BY t1.Comp;
Yang memberi rencana ini
Ini menunjukkan bahwa pencarian kunci sedang digunakan, rinciannya adalah:
Sekarang, menurut dokumentasi SQL-Server:
Anda dapat membuat indeks pada kolom yang dihitung yang didefinisikan dengan ekspresi deterministik, tetapi tidak tepat, jika kolom ditandai PERSISTED dalam pernyataan CREATE TABLE atau ALTER TABLE. Ini berarti bahwa Database Engine menyimpan nilai yang dihitung dalam tabel, dan memutakhirkannya ketika kolom lain yang bergantung pada kolom yang dihitung diperbarui. Mesin Database menggunakan nilai-nilai tetap ini ketika itu membuat indeks pada kolom, dan ketika indeks direferensikan dalam kueri. Opsi ini memungkinkan Anda untuk membuat indeks pada kolom yang dihitung ketika Database Engine tidak dapat membuktikan dengan akurat apakah suatu fungsi yang mengembalikan ekspresi kolom yang dihitung, khususnya fungsi CLR yang dibuat dalam .NET Framework, keduanya deterministik dan tepat.
Jadi jika, seperti dokumen mengatakan "Mesin Basis Data menyimpan nilai yang dihitung dalam tabel" , dan nilainya juga disimpan dalam indeks saya, mengapa Pencarian Kunci diperlukan untuk mendapatkan A, B dan C ketika mereka tidak dirujuk dalam pertanyaannya sama sekali? Saya berasumsi mereka sedang digunakan untuk menghitung Comp, tetapi mengapa? Juga, mengapa kueri dapat menggunakan indeks aktif t2
, tetapi tidak aktif t1
?
NB Saya telah menandai SQL Server 2008 karena ini adalah versi yang menjadi masalah utama saya, tetapi saya juga mendapatkan perilaku yang sama pada tahun 2012.
FOJNtoLSJNandLASJN
) yang menghasilkan hal-hal yang tidak berfungsi seperti yang kita harapkan, dan meninggalkan sampah (BaseRow / Checksums) yang berguna dalam beberapa jenis paket (mis. Kursor) tetapi tidak diperlukan di sini.Chk
itu checksum! Terima kasih, saya tidak yakin tentang itu. Awalnya saya berpikir mungkin ada hubungannya dengan kendala pemeriksaan.Meskipun ini mungkin sedikit kebetulan karena sifat buatan dari data pengujian Anda, karena seperti yang Anda sebutkan SQL 2012 saya mencoba menulis ulang:
Ini menghasilkan rencana berbiaya rendah yang bagus menggunakan indeks Anda dan dengan bacaan yang jauh lebih rendah daripada opsi lain (dan hasil yang sama untuk data pengujian Anda).
Saya menduga data Anda yang sebenarnya lebih rumit sehingga mungkin ada beberapa skenario di mana kueri ini berperilaku semantik berbeda dengan Anda, tetapi kadang-kadang menunjukkan fitur baru dapat membuat perbedaan nyata.
Saya melakukan percobaan dengan beberapa data yang lebih bervariasi dan menemukan beberapa skenario untuk dicocokkan dan beberapa tidak:
sumber
comp
bukan kolom yang dihitung Anda tidak melihat jenisnya.LEAD
fungsinya bekerja persis seperti yang saya inginkan pada contoh lokal saya tahun 2012 express. Sayangnya, ketidaknyamanan kecil ini bagi saya belum dianggap sebagai alasan yang cukup baik untuk meningkatkan server produksi ...Ketika saya mencoba melakukan tindakan yang sama, saya mendapat hasil yang lain. Pertama, rencana eksekusi saya untuk tabel tanpa indeks terlihat sebagai berikut:
Seperti yang dapat kita lihat dari Pemindaian Indeks Berkelompok (t2), predikat digunakan untuk menentukan baris yang diperlukan untuk dikembalikan (karena kondisi):
Ketika indeks ditambahkan, tidak masalah apakah itu didefinisikan oleh operator DENGAN atau tidak, rencana eksekusi menjadi sebagai berikut:
Seperti yang dapat kita lihat, Pemindaian Indeks Berkelompok digantikan oleh Pemindaian Indeks. Seperti yang kita lihat di atas, SQL Server menggunakan kolom sumber dari kolom yang dihitung untuk melakukan pencocokan kueri bersarang. Selama pemindaian indeks berkerumun semua nilai ini dapat diperoleh dalam waktu yang sama (tidak diperlukan operasi tambahan). Ketika indeks ditambahkan, pemfilteran baris yang diperlukan dari tabel (di pilih utama) berkinerja sesuai dengan indeks, tetapi nilai-nilai kolom sumber untuk kolom yang dihitung
comp
masih perlu diperoleh (operasi terakhir Nested Loop) .Karena ini operasi Pencarian Kunci digunakan - untuk mendapatkan data dari kolom sumber yang dihitung.
PS Tampak seperti bug di SQL Server.
sumber