La requête fonctionne plusieurs fois plus lentement lorsque l'on compare la colonne BIT avec 0 au lieu de 1

StackOverflow https://stackoverflow.com/questions/468641

  •  19-08-2019
  •  | 
  •  

Question

J'utilise une vue basée sur une requête complexe avec 17 jointures (interne et externe gauche / droite) et des sous-requêtes. Toutes les lignes de vue sont affichées en 5 secondes environ.

SELECT * FROM a_view;

Une des colonnes de la vue a le type BIT. Et lorsque je filtre les lignes de vue en les comparant à 1, une requête fonctionne à nouveau environ 5 secondes.

SELECT * FROM a_view WHERE c = 1;

Mais lorsque je compare cette colonne BIT à 0, une requête fonctionne environ 50 secondes (10 fois plus lentement).

SELECT * FROM a_view WHERE c = 0;

Cette requête qui renvoie les mêmes lignes de résultat fonctionne comme prévu d'environ 10 secondes:

SELECT * FROM a_view 
EXCEPT
SELECT * FROM a_view WHERE c = 1;

Je me demande donc pourquoi comparer avec 0 ou 'FALSE' prend autant de temps? Des idées, s'il vous plaît.

Le tri sur ce champ BIT est rapide. Le filtrage par d'autres colonnes est également rapide.

Était-ce utile?

La solution

Généralement, il existe plusieurs façons d'exécuter une requête impliquant des jointures. Tous les SGBDR modernes recherchent différents plans de jointure en recherchant le meilleur plan en estimant leurs coûts (temps d’accès au processeur et au disque).

Le problème est que chaque jointure supplémentaire dans une requête multiplie le nombre de plans possibles par un nombre croissant, ce qui entraîne une croissance à double facteur (plus qu'exponentielle) du nombre de plans à prendre en compte. le nombre de jointures augmente. Pour cette raison, la base de données doit limiter la recherche quelque part, ce qui signifie que les plans sous-optimaux sont inévitablement choisis pour les requêtes impliquant de nombreuses jointures.

Par référence, PostgreSQL ™ arrête d’évaluer tous les plans possibles après 12 jointures par défaut. SQL Server aura certainement une limite similaire. 17 jointures prendraient (2 * 13) * (2 * 14) * (2 * 15) * (2 * 16) * (2 * 17) fois plus longtemps à évaluer - il est largement suffisant d’écraser tout SGBD ayant jamais existé, ou jamais .

Il existe également le fait de considérer que les bases de données estiment les coûts sur la base de statistiques brutes, telles que le nombre de valeurs distinctes dans une colonne et / ou une liste des 10 valeurs les plus courantes dans une colonne. Tout cela s’ajoute au fait que, à mesure que le nombre de jointes augmente, la probabilité de choisir la meilleure stratégie (voire même une stratégie raisonnable) diminue dans le sens .

Pourquoi avez-vous besoin de rejoindre 17 tables? Ne pouvez-vous pas simplifier votre schéma de base de données?

Autres conseils

le moteur SQL Server SQL place la requête SQL entière de la vue dans l'instruction SQL que vous avez écrite sur la vue, puis tente de l'optimiser.

Cela pourrait conduire à une situation où avec c = 0, les statistiques des tables utilisées montrent qu'il y a beaucoup plus de lignes correspondant à ce prédicat qu'avec c = 1. Par exemple, avec c = 1, une table contenant le champ c qui est le centre des jointures peut ne renvoyer que 5 lignes correspondantes, ce qui conduit à un plan d’exécution très différent de celui qui est obtenu si la table renvoie 1 million de lignes (exemple: la situation pour c = 0).

Examinez donc les plans d'exécution pour les deux. Examinez également les résultats du profileur de serveur pour les deux, comme avec c = 0, il se peut qu’il y ait beaucoup plus de lectures que avec c = 1 et beaucoup plus de résultats retournés que avec c = 1. Le renvoi de toutes les lignes peut prendre un certain temps, ce qui explique également le ralentissement de la requête.

Je sais que la question est très ancienne, mais si quelqu'un cherche encore une solution, essayez-le. J'ai rencontré le même problème récemment et il s'agissait d'une requête relativement simple, à savoir 3 tables dans une jointure, la plus grande d'entre elles avec un peu plus de 1000 lignes. Malheureusement, je n'avais pas le privilège de voir le plan d'exécution, j'ai donc dû improviser.

select * from subquery_x;

très rapide, fini pratiquement instantanément (retourne à peu près 500 lignes) comme il se doit

select * from subquery_x where column_x = 1  

dtto, très rapide, column_x est une colonne de bits non NULL

select * from subquery_x where column_x = 0
select * from subquery_x where column_x != 1

devrait retourner autour de 300 lignes, les deux est très, très lent, en fait cela a pris plusieurs minutes !!

solution simple (mais étrange) : convertissez la colonne de la clause where en tinyint

.
select * from subquery_x where convert(tinyint,column_x) = 0

Je sais que cela peut avoir des effets indésirables sur les performances, mais cela a fonctionné comme un charme, comparer la valeur de colonne convertie à 1 était également très rapide, je l'ai donc laissé comme ça (il a été utilisé dans un rapport avec la valeur comparée fournie en paramètre)

Si quelqu'un sait pourquoi cela se produit, dites-le-moi, je suppose que c'est un bug, mais qui sait, pourrait aussi bien être une mauvaise fonctionnalité: -)

Semblable à la solution de horcic.roman ..., jette la valeur ou la colonne sur BIT pour une augmentation importante. en vitesse.

myColumn = CAST(1 AS BIT)

CAST(myColumn AS BIT) = 1

Évidemment assez étrange étant donné que la colonne est BIT NOT NULL.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top