J'ai 3 "grandes" tables qui se rejoignent sur une paire de colonnes (les deux int
s).
- Le tableau 1 compte environ 200 millions de lignes
- Le tableau 2 compte environ 1,5 million de lignes
- Table3 a environ 6 millions de lignes
Chaque table possède un index clusterisé sur Key1
, Key2
puis une autre colonne. Key1
a une faible cardinalité et est très asymétrique. Il est toujours référencé dans la WHERE
clause. Key2
n'est jamais mentionné dans la WHERE
clause. Chaque jointure est plusieurs-à-plusieurs.
Le problème est lié à l'estimation de la cardinalité. L'estimation de sortie de chaque jointure devient plus petite au lieu de plus grande . Il en résulte des estimations finales de centaines faibles lorsque le résultat réel est bien en millions.
Existe-t-il un moyen pour moi d'indiquer au CE de faire de meilleures estimations?
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;
Solutions que j'ai essayées:
- Création de statistiques multi-colonnes sur
Key1
,Key2
- Créer des tonnes de statistiques filtrées sur
Key1
(Cela aide beaucoup, mais je me retrouve avec des milliers de statistiques créées par les utilisateurs dans la base de données.)
Plan d'exécution masqué (désolé pour le mauvais masquage)
Dans le cas que je regarde, le résultat a 9 millions de lignes. Le nouveau CE estime 180 lignes; l'héritage CE estime 6100 lignes.
Voici un exemple reproductible:
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
fonction d'Adam est utilisée pour aider à atténuer le problème. Je vais voirmany
. On dirait un pansement assez grossier.