Quoi de plus efficace, une clause where ou une jointure avec plus d'un million de tables de lignes?
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(...)
?
Pour les SGBDR modernes, il n'y a aucune 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 le traitement logique pas le 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.
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 originale, 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 .
@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, je vais essayer 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.
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 est celle à laquelle vous pensez 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é que 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 DE TABLE a toujours donné les meilleurs résultats.