2010-01-11 8 views
7

Je lance une requête MySQL pour classer les utilisateurs de mon site en fonction du nombre de critiques de livres et de critiques de recettes qu'ils ont contribué. Après les problèmes initiaux avec une requête JOIN multiple, je suis passé à une série de sous-requêtes, ce qui est beaucoup, beaucoup plus rapide. Cependant, bien que je puisse extraire le nombre de critiques de chaque membre, je ne peux pas trouver comment les ajouter ensemble afin que je puisse trier par le nombre total.Comment ajouter ensemble les résultats de plusieurs sous-requêtes?

est ici la requête en cours:

SELECT users.*, 
    (SELECT count(*) FROM bookshelf WHERE bookshelf.user_id = users.ID) as titles, 
    (SELECT count(*) FROM book_reviews WHERE book_reviews.user_id = users.ID) as bookreviews, 
    (SELECT count(*) FROM recipe_reviews WHERE recipe_reviews.user_id = users.ID) as recipereviews 
FROM users 

Je dois ajouter ensemble bookreviews et recipereviews pour obtenir 'reviewtotals'. MySQL ne vous permettra pas d'utiliser une syntaxe simple pour faire des calculs sur les alias, mais je suppose qu'il y a une autre façon de faire cela?

+0

@mandel, je suis curieux de savoir vos problèmes avec 'JOIN' en général, je résoudre ce genre de problème avec un' JOIN' - voir ma réponse pour référence. D'après mon expérience, MySQL est généralement beaucoup plus rapide que plusieurs sous-requêtes corrélées comme vous l'avez fait. Je serais intéressé d'entendre si dans votre cas la solution de sous-requête est plus rapide que le JOIN. J'apprécierais beaucoup si vous pouviez me le faire savoir. –

+0

Mes problèmes avec JOIN sont venus de l'inexpérience plutôt que de la philosophie. Mon précédent JOIN a abouti à une requête terriblement lente - 9 secondes; avéré qu'il était en train de créer un produit cartésien. J'ai demandé à ce sujet sur SO ici: http://stackoverflow.com/questions/2030032/is-performing-a-count-calculation-slowing-down-my-mysql-query. Quand je me suis tourné pour faire des sous-requêtes, la vitesse s'est considérablement améliorée, ET je pouvais comprendre ce que je faisais! – mandel

+0

Voir ma réponse à votre message pour une comparaison de vitesse - votre requête JOIN est un peu plus rapide, mais seulement 4/100ème de seconde à la taille de la base de données actuelle. – mandel

Répondre

9

envelopper dans une sous-requête:

SELECT *, 
     bookreviews + recipereviews AS totalreviews 
FROM (
     SELECT users.*, 
       (SELECT count(*) FROM bookshelf WHERE bookshelf.user_id = users.ID) as titles, 
       (SELECT count(*) FROM book_reviews WHERE book_reviews.user_id = users.ID) as bookreviews, 
       (SELECT count(*) FROM recipe_reviews WHERE recipe_reviews.user_id = users.ID) as recipereviews 
     FROM users 
     ) q 
0

Vous avez essayé ce qui suit?

SELECT users.*, 
    (SELECT count(*) FROM bookshelf WHERE bookshelf.user_id = users.ID) as titles, 
    ((SELECT count(*) FROM book_reviews WHERE book_reviews.user_id = users.ID) + 
    (SELECT count(*) FROM recipe_reviews WHERE recipe_reviews.user_id = users.ID)) as reviewtotal 
FROM users 
+0

Merci - c'est presque le cas, mais j'ai encore besoin de générer des totaux à partir de bookreviews et recipereviews ainsi que la somme calculée. – mandel

1

Deux options:

Option 1: laid est l'enfer, et probablement lent (en fonction du cache de requêtes):

SELECT users.*, 
    (SELECT count(*) FROM bookshelf WHERE bookshelf.user_id = users.ID) as titles, 
    (SELECT count(*) FROM book_reviews WHERE book_reviews.user_id = users.ID) as bookreviews, 
    (SELECT count(*) FROM recipe_reviews WHERE recipe_reviews.user_id = users.ID) as recipereviews 
    (SELECT count(*) FROM book_reviews WHERE book_reviews.user_id = users.ID) + (SELECT count(*) FROM recipe_reviews WHERE recipe_reviews.user_id = users.ID) as reviewtotals 
FROM users 

Option 2: enregistrer les résultats à une table temporaire et puis interroger ce tableau

peut-être que cela fonctionnera (ont pas essayé)

SELECT *, bookreviews+recipereviews as reviewtotals FROM 
(SELECT users.*, 
    (SELECT count(*) FROM bookshelf WHERE bookshelf.user_id = users.ID) as titles, 
    (SELECT count(*) FROM book_reviews WHERE book_reviews.user_id = users.ID) as bookreviews, 
    (SELECT count(*) FROM recipe_reviews WHERE recipe_reviews.user_id = users.ID) as recipereviews 
FROM users) u 
+0

Le numéro deux fonctionne presque, mais vous devez ajouter un alias à la première sous-requête - alors, il est identique à la solution de @ Quassnoi, qui était dans une minute plus tôt. Merci! – mandel

+0

J'ai vu qu'il y avait une autre réponse en tapant ... –

1

Si vous voulez être sûr et rapide, le faire comme ceci:

SELECT users.* 
,  titles.num       titles 
,  book_reviews.num      book_reviews 
,  recipe_reviews.num     recipe_reviews 
,  book_reviews.num + recipe_reviews.num total_reviews 
FROM  users 
LEFT JOIN (
      SELECT user_ID, count(*) AS num 
      FROM  bookshelf 
      GROUP BY user_ID 
     ) as titles 
ON  users.ID = titles.user_ID 
LEFT JOIN (
      SELECT user_ID, count(*) AS num 
      FROM  book_reviews 
      GROUP BY user_ID 
     ) as book_reviews 
ON  users.ID = reviews.user_ID 
LEFT JOIN (
      SELECT user_ID, count(*) AS num 
      FROM  recipe_reviews 
      GROUP BY user_ID 
     ) as recipe_reviews 
ON  users.ID = recipes.user_ID 

Si vous voulez coller les sous-requêtes dans la liste SELECT, et que vous voulez être en sécurité, jetez un oeil à la solution de Quassnoi.

Si vous aimez vivre un peu dangereusement et que vous voulez toujours un résultat rapide, vous pouvez utiliser des variables définies par l'utilisateur. Je prédis qu'il sera en sécurité dans ce cas très particulier:

SELECT users.*, 
     (SELECT count(*) FROM bookshelf WHERE bookshelf.user_id = users.ID) as titles, 
     @bookreviews:=(
      SELECT count(*) 
      FROM book_reviews 
      WHERE book_reviews.user_id = users.ID 
     ) as bookreviews, 
     @recipereviews:=(
      SELECT count(*) 
      FROM recipe_reviews 
      WHERE recipe_reviews.user_id = users.ID 
     ) as recipereviews, 
     @bookreviews + @recipereviews as total_reviews 
FROM users 
+0

Intéressant: L'option 1 est un peu plus rapide que la solution de Quassnoi (0.02 sec vs 0.024 sec), tandis que l'option 2 est de 0,88 sec, donc comparativement un peu plus lent. Je trouve Quassnoi beaucoup plus simple en termes de lisibilité (et ma compréhension de ce qui se passe). Y a-t-il des avantages à utiliser la méthode JOIN dans votre premier exemple? – mandel

+0

mandel: oui, les avantages deviennent rapidement évidents lorsque les jeux auxquels vous faites affaire prennent de l'ampleur. le JOIN est beaucoup mieux évolutif. Cependant, à ces heures, je ne pense pas que nous puissions dire quelque chose de concluant. Je ne tiendrai pas compte d'une si petite différence. Mais vous devriez toujours voir des différences importantes lorsque vous obtenez plus de données à traiter. La solution de sous-requête exécute chaque sous-requête pour chaque ligne de la requête externe. Une jointure de boucle imbriquée est généralement beaucoup plus efficace. –

Questions connexes