Quoi de plus efficace, une clause where ou une jointure avec plus d'un million de tables de lignes?

17

Nous exécutons un site Web qui a 250MM de lignes dans une table et dans une autre table à laquelle nous le joignons pour la plupart des requêtes a un peu moins de 15MM de lignes.

Exemples de structures:

MasterTable (Id, UserId, Created, Updated...) -- 15MM Rows
DetailsTable (Id, MasterId, SomeColumn...) -- 250MM Rows
UserTable (Id, Role, Created, UserName...) -- 12K Rows

Nous devons régulièrement faire quelques requêtes sur toutes ces tables. L'une consiste à saisir des statistiques pour les utilisateurs gratuits (~ 10 000 utilisateurs gratuits).

Select Count(1) from DetailsTable dt 
join MasterTable mt on mt.Id = dt.MasterId 
join UserTable ut on ut.Id = mt.UserId 
where ut.Role is null and mt.created between @date1 and @date2

Le problème est que cette requête s'exécutera parfois très longtemps en raison du fait que les jointures se produisent bien avant le où.

Dans ce cas, serait-il plus judicieux d'utiliser où au lieu de jointures ou éventuellement where column in(...)?

Jeremy Boyd
la source
1
Quelle base de données et quelle version?
Leigh Riffel
1
avez-vous essayé dans les deux sens?
gbn
S'il s'agissait d'Oracle, je créerais un index basé sur les fonctions pour UserTable sur NVL2 (Role, NULL, ID), mais cela ressemble à une autre base de données.
Leigh Riffel

Réponses:

20

Pour les SGBDR modernes, il n'y a pas de différence entre "JOIN explicite" et "JOIN-in-the-WHERE" (si tous les JOINS sont INNER) en ce qui concerne les performances et le plan de requête.

La syntaxe JOIN explicite est plus claire et moins ambiguë (voir les liens ci-dessous)

Maintenant, le JOIN-before-WHERE est un traitement logique et non un traitement réel et les optimiseurs modernes sont assez intelligents pour le réaliser.

Votre problème ici est probablement l'indexation.

Veuillez nous montrer tous les index et clés de ces tables. Et les plans de requête

Remarque: cette question aurait été proche sur StackOverflow pour être un doublon maintenant ... COUNT (1) vs COUNT (*) est un autre mythe éclaté aussi.

gbn
la source
2
Il n'est PAS TOUJOURS VRAI qu'il n'y ait pas de différence entre joinet whereclause. J'optimise en permanence les requêtes longues et parfois les requêtes utilisant la whereclause fonctionnent mieux que celles utilisant joinun facteur jusqu'à 70x. Si c'était aussi simple et direct, la vie serait constituée d'arcs-en-ciel et de licornes. Et il ne s'agit pas d'un ancien moteur obscur - en ce moment, je regarde 70x l'avantage de la whereclause dans SQL 2012.
ajeh
Plus loin encore, j'observe souvent les mêmes plans exacts des deux approches et j'isole que les requêtes fonctionnent exactement de la même manière, mais lorsque la whererequête de clause s'exécute dans le grand lot dont elle est censée faire partie, elle surpasse la joinrequête d'une énorme marge. Les requêtes SQL ne s'exécutent pas dans le vide - elles sont affectées par le reste de la charge utile du serveur, et souvent les whererequêtes de clause s'en sortent assez bien, ce qui est gênant car la joinsyntaxe est en effet beaucoup plus propre.
ajeh
3
@ajeh: Je dirais que votre expérience est très atypique. Vous avez de plus gros problèmes avec les requêtes si vous avez des différences x70: c'est aussi simple que cela
gbn
5

Vous devez refactoriser complètement la requête

Essayez d'exécuter les clauses WHERE plus tôt et les JOIN plus tard

Select Count(1) from DetailsTable dt
join (Select UserId,Id FROM MasterTable where
created between @date1 and @date2) mt on mt.Id = dt.MasterId 
join (Select Id FROM UserTable WHERE Role is NULL) ut
on ut.Id = mt.UserId;

Même si vous exécutez un plan EXPLAIN sur cette requête refactorisée et qu'elle semble pire que votre version d'origine, essayez-la quand même. Les tables temporaires créées en interne effectueront des jointures cartésiennes, mais ces tables sont plus petites pour fonctionner.

J'ai eu cette idée dans cette vidéo YouTube .

J'ai essayé les principes de la vidéo dans une question très complexe dans StackOverflow et j'ai obtenu une prime de 200 points.

@gbn a mentionné avoir vérifié que les bons index étaient en place. Dans ce cas, veuillez indexer la colonne créée dans MasterTable.

Essaie !!!

MISE À JOUR 2011-06-24 22:31 EDT

Vous devez exécuter ces requêtes:

SELECT COUNT(1) AllRoles FROM UserTable;
SELECT COUNT(1) NullRoles FROM UserTable WHERE Role is NULL;

Si NullRoles X 20 <AllRoles (en d'autres termes, si NullRoles est inférieur à 5% des lignes du tableau), vous devez créer un index non unique du rôle dans UserTable. Sinon, une table complète de UserTable suffirait car l'Optimiseur de requête peut éventuellement exclure l'utilisation d'un index.

MISE À JOUR 2011-06-25 12:40 EDT

Étant donné que je suis un administrateur de base de données MySQL, ma méthode de travail nécessite de ne pas faire confiance à MySQL Query Optimizer par un pessimisme positif et d'être conservateur. Ainsi, j'essaierai de refactoriser une requête ou de créer les index de couverture nécessaires pour devancer les mauvaises habitudes cachées de MySQL Query Optimizer. La réponse de @ gbn semble plus complète dans la mesure où SQL Server peut avoir plus de "bon sens" évaluant les requêtes.

RolandoMySQLDBA
la source
0

Nous avions un tableau [Détail] d'environ 75 millions de lignes; une table [Master] d'environ 400K lignes et une table [Item] associée qui avait 7 lignes - toujours et pour toujours. Il stockait le petit ensemble de «numéros d'article» (1-7) et modélisait un formulaire papier, dont des millions étaient imprimés et distribués chaque mois. La requête la plus rapide était celle à laquelle vous pensiez le moins probablement en premier, impliquant l'utilisation d'une jointure cartésienne. IIRC, c'était quelque chose comme:

SELECT m.order_id, i.line_nr, d.Item_amt
FROM Master m, Item i 
INNER JOIN Detail d ON m.order_id = d.order_id

Même s'il existe un lien «id» logique entre [Item] et [Detail], CROSS JOIN a mieux fonctionné qu'INNER JOIN.

Le RDBMS était Teradata avec sa technologie MPP, et IDR quel était le schéma d'indexation. La table à 7 lignes n'avait pas d'index, car SCAN DES TABLEAUX donnait toujours les meilleurs résultats.

Timothy Oleary
la source