Est-il possible de forcer l'optimiseur à éliminer les tables non pertinentes dans cette vue partitionnée?

22

Je teste différentes architectures pour de grandes tables et une suggestion que j'ai vue est d'utiliser une vue partitionnée, par laquelle une grande table est divisée en une série de tables plus petites et "partitionnées".

1 , 2 , 3 , 4

En testant cette approche, j'ai découvert quelque chose qui n'a pas beaucoup de sens pour moi. Lorsque je filtre sur "colonne de partitionnement" sur la vue des faits, l'optimiseur ne recherche que sur les tables pertinentes. De plus, si je filtre sur cette colonne de la table de dimension, l'optimiseur élimine les tables inutiles.

Cependant, si je filtre sur un autre aspect de la dimension que l'optimiseur recherche sur le PK / CI de chaque table de base.

Voici les requêtes en question:

select 
    od.[Year], 
    AvgValue = avg(ObservationValue)
from dbo.v_Observation o 
join dbo.ObservationDates od
    on o.ObservationDateKey = od.DateKey
where o.ObservationDateKey >= 20000101
    and o.ObservationDateKey <= 20051231
group by od.[Year];

select 
    od.[Year], 
    AvgValue = avg(ObservationValue)
from dbo.v_Observation o 
join dbo.ObservationDates od
    on o.ObservationDateKey = od.DateKey
where od.DateKey >= 20000101
    and od.DateKey <= 20051231
group by od.[Year];

select 
    od.[Year], 
    AvgValue = avg(ObservationValue)
from dbo.v_Observation o 
join dbo.ObservationDates od
    on o.ObservationDateKey = od.DateKey
where od.[Year] >= 2000 and od.[Year] < 2006
group by od.[Year];

filtre de fait sur la clé

filtre faible sur la touche

filtre faible sur l'aspect

Voici un lien vers la session SQL Sentry Plan Explorer.

Je travaille sur le partitionnement de la plus grande table pour voir si j'obtiens l'élimination de la partition pour répondre de la même manière.

J'obtiens l'élimination de partition pour la requête (simple) qui filtre sur un aspect de la dimension.

En attendant, voici une copie uniquement statistique de la base de données:

https://gist.github.com/swasheck/9a22bf8a580995d3b2aa

L'estimateur de cardinalité «ancien» obtient un plan moins cher, mais c'est à cause des estimations de cardinalité plus faibles pour chacun des indices (inutiles) recherchés.

Je voudrais savoir s'il existe un moyen pour que l'optimiseur utilise la colonne clé lors du filtrage par un autre aspect de la dimension afin qu'il puisse éliminer les recherches sur les tables non pertinentes.

Version de SQL Server:

Microsoft SQL Server 2014 - 12.0.2000.8 (X64) 
    Feb 20 2014 20:04:26 
    Copyright (c) Microsoft Corporation
    Developer Edition (64-bit) on Windows NT 6.3 <X64> (Build 9600: ) (Hypervisor)
swasheck
la source
Juste un FYI .. le dernier flux de statistiques est corrompuCREATE STATISTICS [_WA_Sys_00000008_2FCF1A8A] ON [dbo].[Observation_2010]([StationStateCode]) WITH STATS_STREAM = 0x01000000010000000000000000000000D4531EDB00000000D5080000000000009508000000000000AF030000AF000000020000000000000008D000340000000007000000E65DE0007DA5000076F9780000000000867704000000000000000000ABAAAA3C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Kin Shah
Il semble que le script de la base de données uniquement statistiques soit tronqué. J'ai essayé de cliquer sur "afficher le fichier complet" et de télécharger le zip, mais de toute façon je n'ai pas de statistiques pour le ObservationDatestableau. Je ne reçois pas le même plan que Paul, même avec 4199, et je pense que c'est pourquoi.
Geoff Patterson du
@GeoffPatterson ça marche pour moi. avez-vous cliqué sur le lien vers le fichier brut? gist.githubusercontent.com/swasheck/9a22bf8a580995d3b2aa/raw/… cependant, comme Kin l'a noté, le dernier flux de statistiques est corrompu: /
swasheck
J'ai cliqué sur le lien du fichier brut. Le script fonctionne (sauf le problème noté par Kin), mais ne contient aucune logique pour créer des statistiques ObservationDates. J'ai fini par courir UPDATE STATISTICS ObservationDates WITH ROWCOUNT = 10000manuellement afin d'obtenir le plan que Paul a démontré.
Geoff Patterson
impair. en créant une nouvelle base de données et en exécutant ce script, j'ai des objets stats (enfin, ce sont des index), ObservationDatesdonc je ne suis pas sûr de ce qui se passe avec ça. De plus, je ne peux pas non plus générer le plan généré par Paul. je vais essayer la mise à jour pour voir.
swasheck

Réponses:

10

Activez l'indicateur de trace 4199.

J'ai également dû émettre:

UPDATE STATISTICS dbo.ObservationDates 
WITH ROWCOUNT = 73049;

pour obtenir les plans indiqués ci-dessous. Les statistiques de ce tableau étaient manquantes lors du téléchargement. Le chiffre 73 049 provient des informations de cardinalité de la table dans la pièce jointe Plan Explorer. J'ai utilisé SQL Server 2014 SP1 CU4 (build 12.0.4436) avec deux processeurs logiques, une mémoire maximale définie sur 2048 Mo et aucun indicateur de trace en dehors de 4199.

Vous devriez ensuite obtenir un plan d'exécution qui comprend l'élimination dynamique des partitions:

select 
    od.[Year], 
    AvgValue = avg(ObservationValue)
from dbo.v_Observation o 
join dbo.ObservationDates od
    on o.ObservationDateKey = od.DateKey
where 
    od.[Year] >= 2000 and od.[Year] < 2006
group by 
    od.[Year]
option (querytraceon 4199);

Fragment de plan:

Fragment de plan

Cela peut sembler pire, mais les filtres sont tous des filtres de démarrage . Un exemple de prédicat est:

Propriétés du filtre

Par itération de la boucle, le prédicat de démarrage est testé, et ce n'est que s'il renvoie vrai que la recherche d'index cluster en dessous est exécutée. Par conséquent, élimination de partition dynamique.

Ceci est peut - être pas tout à fait aussi efficace que l' élimination statique, en particulier si le plan est parallèle.

Vous devrez peut-être essayer des astuces comme MAXDOP 1, FAST 1ou FORCESEEKsur la vue pour obtenir le même plan. Les choix de coût de l'optimiseur avec des vues partitionnées (comme les tables partitionnées) peuvent être délicats.

Le fait est que vous avez besoin d'un plan qui comporte des filtres de démarrage pour obtenir l'élimination dynamique des partitions avec des vues partitionnées.


Requêtes avec des USE PLANconseils intégrés : (via gist.github.com):

Paul White dit GoFundMonica
la source
1
Grande info, merci Paul! Je me demandais après avoir écrit ma réponse pourquoi SQL Server ne pouvait pas faire ce type d'élimination. Il s'avère que je ne l'avais jamais vu auparavant!
Geoff Patterson
6

Mon observation a toujours été que vous devez spécifier explicitement la valeur (ou la plage de valeurs) pour la colonne de partition dans la requête afin d'obtenir "l'élimination de la table" dans une vue partitionnée. Ceci est basé sur l'expérience d'utilisation des vues partitionnées dans la production de SQL Server 2000 à SQL Server 2014.

SQL Server n'a pas de concept d'opérateur de jointure de boucle dans lequel le moteur peut viser dynamiquement la recherche directement sur la table appropriée sur le côté intérieur de la boucle en fonction de la valeur de la ligne sur le côté extérieur de la boucle. Cependant, comme l'explique la réponse de Paul , il existe la possibilité d'un plan avec des filtres de démarrage afin de sauter dynamiquement des tables non pertinentes sur le côté intérieur de la boucle en temps constant (par opposition à logarithmique en effectuant réellement la recherche).

Notez que pour les tables partitionnées, ce type de recherche (vers une partition spécifique) est pris en charge.

Si vous êtes déterminé à utiliser des vues partitionnées, une autre option consiste à diviser votre requête en plusieurs requêtes, telles que:

-- Gather than the min/max values for the partition column
DECLARE @minDateKey INT,
        @maxDateKey INT
SELECT @minDateKey = MIN(DateKey),
        @maxDateKey = MAX(DateKey)
FROM dbo.ObservationDates od
WHERE od.[Year] >= 2000 and od.[Year] < 2006

-- Since I have a stats-only copy of the database, simulate having run the query above
-- (You can comment this out since you have the actual data.)
SELECT @minDateKey = 20000101, @maxDateKey = 20051231

-- Adjust the query to use the min/max values of the partition column
-- rather than filtering on a different column in the dimension table
select 
    od.[Year], 
    AvgValue = avg(ObservationValue)
from dbo.v_Observation o 
join dbo.ObservationDates od
    on o.ObservationDateKey = od.DateKey
WHERE od.DateKey >= @minDateKey AND od.DateKey <= @maxDateKey
group by od.[Year]
-- Must use OPTION RECOMPILE; otherwise the plan will touch all tables because it
-- must do so in order to be valid for all values of the parameters!
OPTION (RECOMPILE)

Cela donne le plan suivant. Il y a maintenant une requête supplémentaire qui frappe la table de dimension, mais la requête sur la table de faits (probablement beaucoup plus grande) est optimisée.

entrez la description de l'image ici

Geoff Patterson
la source
Le même effet serait-il obtenu si vous intégriez la première requête à la seconde sans recourir à des variables?
Andriy M
@AndriyM Si je vous comprends bien, la réponse est non, le même effet ne sera pas obtenu et le plan de requête touchera toutes les tables de la vue partitionnée si vous essayez de combiner les deux requêtes. Si vous deviez exécuter la première requête, puis coller les valeurs 20000101et 20051231au lieu des variables (ou faire quelque chose de similaire via deux requêtes distinctes dans votre application), alors oui, le même effet serait obtenu sans utiliser les variables.
Geoff Patterson