Saya memiliki 3 "besar" tabel yang bergabung pada sepasang kolom (keduanya int
).
- Table1 memiliki ~ 200 juta baris
- Table2 memiliki ~ 1,5 juta baris
- Table3 memiliki ~ 6 juta baris
Setiap tabel memiliki indeks berkerumun di Key1
, Key2
, dan kemudian satu kolom lagi. Key1
memiliki kardinalitas rendah dan sangat miring. Itu selalu dirujuk dalam WHERE
klausa. Key2
tidak pernah disebutkan dalam WHERE
klausa. Setiap bergabung adalah banyak-ke-banyak.
Masalahnya adalah dengan estimasi kardinalitas. Estimasi output masing-masing bergabung menjadi lebih kecil, bukan lebih besar . Ini menghasilkan estimasi akhir dari ratusan yang rendah ketika hasil aktual mencapai jutaan.
Apakah ada cara bagi saya untuk memberi petunjuk kepada CE agar membuat perkiraan yang lebih baik?
SELECT 1
FROM Table1 t1
JOIN Table2 t2
ON t1.Key1 = t2.Key1
AND t1.Key2 = t2.Key2
JOIN Table3 t3
ON t1.Key1 = t3.Key1
AND t1.Key2 = t3.Key2
WHERE t1.Key1 = 1;
Solusi yang saya coba:
- Membuat statistik multi-kolom pada
Key1
,Key2
- Membuat banyak statistik yang difilter aktif
Key1
(Ini membantu sedikit, tapi saya berakhir dengan ribuan statistik yang dibuat pengguna dalam database.)
Rencana eksekusi bertopeng (maaf atas masking buruk)
Dalam kasus yang saya lihat, hasilnya memiliki 9 juta baris. CE yang baru memperkirakan 180 baris; legacy CE memperkirakan 6100 baris.
Berikut ini contoh yang dapat direproduksi:
DROP TABLE IF EXISTS #Table1, #Table2, #Table3;
CREATE TABLE #Table1 (Key1 INT NOT NULL, Key2 INT NOT NULL, T1Key3 INT NOT NULL, CONSTRAINT pk_t1 PRIMARY KEY CLUSTERED (Key1, Key2, T1Key3));
CREATE TABLE #Table2 (Key1 INT NOT NULL, Key2 INT NOT NULL, T2Key3 INT NOT NULL, CONSTRAINT pk_t2 PRIMARY KEY CLUSTERED (Key1, Key2, T2Key3));
CREATE TABLE #Table3 (Key1 INT NOT NULL, Key2 INT NOT NULL, T3Key3 INT NOT NULL, CONSTRAINT pk_t3 PRIMARY KEY CLUSTERED (Key1, Key2, T3Key3));
-- Table1
WITH Numbers
AS (SELECT TOP (1000000) Number = ROW_NUMBER() OVER(ORDER BY t1.number)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2),
DataSize (Key1, NumberOfRows)
AS (SELECT 1, 2000 UNION
SELECT 2, 10000 UNION
SELECT 3, 25000 UNION
SELECT 4, 50000 UNION
SELECT 5, 200000)
INSERT INTO #Table1
SELECT Key1
, Key2 = ROW_NUMBER() OVER (PARTITION BY Key1, T1Key3 ORDER BY Number)
, T1Key3
FROM DataSize
CROSS APPLY (SELECT TOP(NumberOfRows)
Number
, T1Key3 = Number%(Key1*Key1) + 1
FROM Numbers
ORDER BY Number) size;
-- Table2 (same Key1, Key2 values; smaller number of distinct third Key)
WITH Numbers
AS (SELECT TOP (1000000) Number = ROW_NUMBER() OVER(ORDER BY t1.number)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2)
INSERT INTO #Table2
SELECT DISTINCT
Key1
, Key2
, T2Key3
FROM #Table1
CROSS APPLY (SELECT TOP (Key1*10)
T2Key3 = Number
FROM Numbers
ORDER BY Number) size;
-- Table2 (same Key1, Key2 values; smallest number of distinct third Key)
WITH Numbers
AS (SELECT TOP (1000000) Number = ROW_NUMBER() OVER(ORDER BY t1.number)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2)
INSERT INTO #Table3
SELECT DISTINCT
Key1
, Key2
, T3Key3
FROM #Table1
CROSS APPLY (SELECT TOP (Key1)
T3Key3 = Number
FROM Numbers
ORDER BY Number) size;
DROP TABLE IF EXISTS #a;
SELECT col = 1
INTO #a
FROM #Table1 t1
JOIN #Table2 t2
ON t1.Key1 = t2.Key1
AND t1.Key2 = t2.Key2
WHERE t1.Key1 = 1;
DROP TABLE IF EXISTS #b;
SELECT col = 1
INTO #b
FROM #Table1 t1
JOIN #Table2 t2
ON t1.Key1 = t2.Key1
AND t1.Key2 = t2.Key2
JOIN #Table3 t3
ON t1.Key1 = t3.Key1
AND t1.Key2 = t3.Key2
WHERE t1.Key1 = 1;
sumber
make_parallel
Fungsi Adam digunakan untuk membantu mengurangi masalah. Saya akan melihatmany
. Sepertinya bantuan band yang cukup kotor.Statistik SQL Server hanya berisi histogram untuk kolom terkemuka dari objek statistik. Oleh karena itu, Anda bisa membuat statistik yang difilter yang memberikan histogram nilai untuk
Key2
, tetapi hanya di antara baris denganKey1 = 1
. Membuat statistik yang difilter ini pada setiap tabel memperbaiki perkiraan dan mengarah ke perilaku yang Anda harapkan untuk kueri pengujian: setiap gabungan baru tidak memengaruhi perkiraan kardinalitas akhir (dikonfirmasi di SQL 2016 SP1 dan SQL 2017).Tanpa statistik yang difilter ini, SQL Server akan mengambil pendekatan berbasis heuristik untuk memperkirakan kardinalitas bergabung Anda. Papan tulis berikut berisi deskripsi tingkat tinggi yang baik dari beberapa heuristik yang digunakan SQL Server: Mengoptimalkan Rencana Kueri Anda dengan Penaksir Kardinalitas SQL Server 2014 .
Misalnya, menambahkan
USE HINT('ASSUME_JOIN_PREDICATE_DEPENDS_ON_FILTERS')
petunjuk ke kueri Anda akan mengubah heuristik kontainment join untuk mengasumsikan beberapa korelasi (daripada independensi) antaraKey1
predikat danKey2
predikat join, yang mungkin bermanfaat untuk kueri Anda. Untuk kueri pengujian akhir, petunjuk ini meningkatkan perkiraan kardinalitas dari1,175
menjadi7,551
, tetapi masih agak malu dengan20,000
perkiraan baris yang benar yang dihasilkan dengan statistik yang difilter.Pendekatan lain yang kami gunakan dalam situasi yang serupa adalah mengekstraksi subset data yang relevan ke tabel #temp. Apalagi sekarang versi SQL Server yang lebih baru tidak lagi bersemangat menulis tabel #temp ke disk , kami sudah mendapatkan hasil yang baik dengan pendekatan ini. Deskripsi Anda tentang banyak-ke-banyak Anda bergabung menyiratkan bahwa setiap tabel #temp individu dalam kasus Anda akan relatif kecil (atau setidaknya lebih kecil dari hasil akhir yang ditetapkan), sehingga pendekatan ini mungkin patut dicoba.
sumber
Key1
nilai pada setiap tabel. Kami sekarang memiliki ribuan dari mereka.Sebuah jangkauan. Tidak ada dasar nyata selain mencoba.
sumber