Question

J'ai cinq tables dans ma base de données. Membres, articles, commentaires, votes et pays. Je veux obtenir 10 articles. Je veux obtenir le nombre de commentaires et de votes pour chaque élément. Je veux aussi le membre qui a soumis chaque élément, et le pays dans lequel ils sont de.

Après avoir affiché ici et d'ailleurs, je commencé à utiliser des sous-requêtes pour obtenir les comptes, mais cette requête est de prendre plus de 10 secondes!

SELECT `items_2`.*, 
   (SELECT COUNT(*) 
   FROM `comments` 
   WHERE (comments.Script = items_2.Id) 
   AND (comments.Active = 1)) 
  AS `Comments`, 
   (SELECT COUNT(votes.Member) 
   FROM `votes` 
   WHERE (votes.Script = items_2.Id) 
   AND (votes.Active = 1)) 
  AS `votes`, 
  `countrys`.`Name` AS `Country` 
FROM `items` AS `items_2` 
INNER JOIN `members` ON items_2.Member=members.Id AND members.Active = 1 
INNER JOIN `members` AS `members_2` ON items_2.Member=members.Id 
LEFT JOIN `countrys` ON countrys.Id = members.Country 
GROUP BY `items_2`.`Id` 
ORDER BY `Created` DESC 
LIMIT 10

Ma question est de savoir si c'est la bonne façon de le faire, s'il y a une meilleure façon d'écrire cette déclaration ou s'il y a une approche totalement différente qui sera mieux. Dois-je exécuter les sous-requêtes séparément et agréger les informations?

Était-ce utile?

La solution

Oui, vous pouvez réécrire les sous-requêtes comme agrégat des jointures (voir ci-dessous), mais je suis presque certain que la lenteur est due à indices manquants plutôt que de la requête elle-même. Utilisez EXPLAIN pour voir ce que les indices que vous pouvez ajouter à rendre votre exécution de la requête en une fraction de seconde.

Pour mémoire, voici l'ensemble se joindre équivalent.

SELECT `items_2`.*,
  c.cnt AS `Comments`,
  v.cnt AS `votes`,
  `countrys`.`Name` AS `Country` 
FROM `items` AS `items_2` 
INNER JOIN `members` ON items_2.Member=members.Id AND members.Active = 1 
INNER JOIN `members` AS `members_2` ON items_2.Member=members.Id 
LEFT JOIN (
  SELECT Script, COUNT(*) AS cnt 
   FROM `comments` 
   WHERE Active = 1
   GROUP BY Script
) AS c
ON c.Script = items_2.Id 
LEFT JOIN ( 
  SELECT votes.Script, COUNT(*) AS cnt 
   FROM `votes` 
   WHERE Active = 1
   GROUP BY Script
) AS v
ON v.Script = items_2.Id 
LEFT JOIN `countrys` ON countrys.Id = members.Country 
GROUP BY `items_2`.`Id` 
ORDER BY `Created` DESC 
LIMIT 10

Cependant , parce que vous utilisez LIMIT 10, vous êtes presque certainement aussi bien nantis (ou mieux) avec les sous-requêtes que vous avez actuellement qu'avec l'ensemble jointure équivalente I fourni ci-dessus pour référence.

En effet, un mauvais optimiseur (et MySQL de est loin d'être stellaire) pourrait, dans le cas de l'agrégat requête de jointure, finissent par effectuer le travail d'agrégation de COUNT(*) pour le contenu intégral de la table Comments et Votes tout avant de lancer gaspillée, mais 10 valeurs (votre LIMIT) à l'écart, alors que dans le cas de votre requête initiale, il sera, dès le départ, ne regardez le strict minimum pour autant que les tables de Comments et Votes sont concernés.

Plus précisément, en utilisant les sous-requêtes de la manière que votre requête d'origine ne produit généralement dans ce qu'on appelle boucles imbriquées avec des consultations d'index. L'utilisation globale des jointures se traduit généralement par fusion ou jointures de hachage avec balayage d'index ou des analyses de table. Les anciens (boucles imbriquées) sont plus efficaces que ceux-ci (fusion et jointures de hachage) lorsque le nombre de boucles est faible (10 dans votre cas.) Ce dernier, toutefois, obtenir plus efficace lorsque l'ancien entraînerait trop de boucles ( des dizaines / centaines de milliers ou plus), en particulier sur les systèmes avec des disques lents mais beaucoup de mémoire.

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