web-dev-qa-db-fra.com

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(...)?

20
Jeremy Boyd

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.

20
gbn

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 .

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, 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.

6
RolandoMySQLDBA

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.

1
Timothy Oleary