Ich habe 3 "große" Tabellen, die auf einem Spaltenpaar (beide int
s) verbunden sind.
- Tabelle 1 enthält ~ 200 Millionen Zeilen
- Tabelle 2 enthält ~ 1,5 Millionen Zeilen
- Tabelle 3 enthält ~ 6 Millionen Zeilen
Jeder Tisch hat einen Clustered - Index auf Key1
, Key2
und dann eine weitere Spalte. Key1
hat eine geringe Kardinalität und ist sehr schief. In der WHERE
Klausel wird immer darauf verwiesen . Key2
wird in der WHERE
Klausel nie erwähnt . Jeder Join ist viele zu viele.
Das Problem liegt in der Kardinalitätsschätzung. Die Ausgabeschätzung jedes Joins wird kleiner statt größer . Dies führt zu endgültigen Schätzungen von niedrigen Hunderten, wenn das tatsächliche Ergebnis weit in die Millionen geht.
Gibt es eine Möglichkeit für mich, das CE zu besseren Schätzungen zu bewegen?
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;
Lösungen, die ich ausprobiert habe:
- Erstellen mehrspaltiger Statistiken für
Key1
,Key2
- Erstellen von Tonnen von gefilterten Statistiken auf
Key1
(Dies hilft ziemlich viel, aber ich habe am Ende Tausende von benutzerdefinierten Statistiken in der Datenbank.)
Maskierter Ausführungsplan (Entschuldigung für die schlechte Maskierung)
In dem Fall, den ich betrachte, hat das Ergebnis 9 Millionen Zeilen. Das neue CE schätzt 180 Zeilen; Das alte CE schätzt 6100 Zeilen.
Hier ist ein reproduzierbares Beispiel:
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;
make_parallel
Funktion wird verwendet, um das Problem zu lösen. Ich werde einen Blick darauf werfenmany
. Scheint ein ziemlich grobes Pflaster zu sein.