2009-04-02 7 views
2
SELECT user_id, 
    SUM(COALESCE(point_points, 0)) AS total_points, 
    SUM(
     CASE 
      WHEN point_date > '$this_month' 
      THEN point_points 
      ELSE 0 
     END)    AS month_points, 
    COUNT(DISTINCT c_id) AS num_comments, 
    COUNT(DISTINCT rant_id) AS live_submissions 
FROM users 
    LEFT JOIN points 
    ON users.user_id = points.point_userid 
    LEFT JOIN comments 
    ON 
     (
      c_userid = user_id 
     ) 
    LEFT JOIN rants 
    ON 
     (
      rant_poster = user_id 
     AND rant_status = 1 
     ) 
WHERE user_id = $id 
GROUP BY user_id 

Fondamentalement live_submissions et num_comments affichage variable des résultats corrects, alors que l'affichage total_points et month_points un produit de month_points/total_points, live_submissions et num_comments. Une idée de pourquoi cela se passe?Pourquoi les résultats de cette requête MySQL sont-ils multipliés les uns par les autres?

Répondre

9

Ceci est appelé Cartesian Product. Lorsque vous joignez les tables ensemble, le résultat par défaut est chaque permutation des lignes pour lesquelles les conditions de jointure sont vraies. Vous utilisez les conditions JOIN pour limiter ces permutations.

Mais puisque vous joignez plusieurs tables à users, le résultat inclut chaque permutation de chaque table correspondante. Par exemple, chaque ligne correspondante dans points est répétée par ligne correspondante dans comments, et chacune de celles-ci est à nouveau multipliée, en répétant par ligne correspondante en rants.

Vous pouvez partiellement compenser cela avec COUNT(DISTINCT c_id) comme vous le faites, mais le DISTINCT est nécessaire uniquement parce que vous avez plusieurs lignes par c_id. Et cela ne fonctionne pas, sauf si vous l'appliquez à des valeurs uniques. Ce remède ne fonctionne pas pour les expressions SUM().

Fondamentalement, vous essayez de faire trop de calculs dans une requête. Vous devez le diviser en requêtes distinctes pour qu'il soit fiable. Et puis vous pouvez vous débarrasser des modificateurs DISTINCT, aussi.

SELECT u.user_id, SUM(COALESCE(p.point_points, 0)) AS total_points, 
    SUM(CASE WHEN p.point_date > '$this_month' THEN p.point_points ELSE 0 END) AS month_points 
FROM users u LEFT JOIN points p 
    ON u.user_id = p.point_userid 
WHERE u.user_id = $id 
GROUP BY u.user_id; 

SELECT user_id, COUNT(c.c_id) as num_comments, 
FROM users u LEFT JOIN comments c 
    ON (c.c_userid = u.user_id) 
WHERE u.user_id = $id 
GROUP BY u.user_id; 

SELECT u.user_id, COUNT(r.rant_id) as live_submissions 
FROM users u LEFT JOIN rants r 
    ON (r.rant_poster = u.user_id AND r.rant_status = 1) 
WHERE u.user_id = $id 
GROUP BY u.user_id; 

Vous ne devriez pas essayer de faire tous les trois dans une seule requête.

+0

bonne réponse facture. – nickf

0

Pouvez-vous fournir un exemple de sortie?

Je pense que cela a quelque chose à voir avec l'ajout de commentaires et de commentaires dans les points. Pouvez-vous essayer de supprimer les tables de commentaires et de commentaires?

+0

si je supprime des rancunes et des commentaires, il le résume parfaitement. Si j'ai simplement la somme + les commentaires, cela ne fait que doubler ces 2, si j'ajoute des rançons, cela se multiplie aussi. –

-1

Si vous regardez la sortie de la requête avant le regroupement, vous verrez le problème. Plusieurs lignes seront renvoyées pour un utilisateur si elles ont plus d'un enregistrement dans l'une des tables jointes. Donc, si un utilisateur a 2 enregistrements de commentaires, alors 2 enregistrements de points seront également retournés.

Comme exemple simplifié ...

utilisateur Tableau

nom userId

1 Fred

table point

Points userId

Commentaires Table

userId Commentaire

1 Voici

1 Il

Sélection * à partir de ces tableaux entraînera

Points userId Commentaire

1 10 Ici

1 10 Il

Je ne suis pas tout à fait sûr de la syntaxe MYSQL mais vous voulez quelque chose comme

SELECT UserId, C.num_comments, P.total_points 
FROM users 
LEFT JOIN 
    (SELECT c_userId, COUNT(DISTINCT c_id) as num_comments 
    FROM Comments 
    GROUP BY c_userId) 
    AS C 
    ON UserId = c_userid 
LEFT JOIN 
    (SELECT point_userId, sum(COALESCE(point_points, 0)) as total_points 
    FROM Points 
    GROUP BY point_userId) 
    AS P 
    ON UserId = point_userid 
+0

si je ne groupe pas, requête ne s'exécutera pas. –

+0

J'essayais de démontrer pourquoi vous obtenez des doublons. Je ne suis pas sûr de la syntaxe MYSQL mais dans SQL Server, vous retourneriez des points, des commentaires et des commentaires de sous-requêtes séparées. –

Questions connexes