2010-11-17 5 views
1

J'essaie de faire une requête plutôt complexe (pour moi, au moins) qui implique l'extraction de lignes qui peuvent avoir des valeurs NULL. Il y a quatre tables ici, tags, questions_tags, users_experience et answers. Les connexions sont plutôt simples. Les questions sont étiquetées, les étiquettes ont des noms, les utilisateurs donnent des réponses aux questions et les utilisateurs ont de l'expérience avec des étiquettes particulières. Je veux trouver les réponses que les utilisateurs ont données, et leur expérience (qui peut être NULL) pour les balises de ces questions. Je commande par le nombre de réponses données pour un tag particulier.MySQL JOINs avec des valeurs NULL

Ma requête est la suivante. Ce n'est pas du tout optimisé (et si vous avez des suggestions d'optimisation, s'il vous plaît suggérer loin!):

SELECT t.tag_id, t.name, ue.body, COUNT(a.qid) 
FROM tags AS t 
LEFT JOIN users_experience AS ue 
    ON t.tag_id = ue.tag_id 
LEFT JOIN questions_tags AS qt 
    ON qt.tag_id = t.tag_id 
LEFT JOIN answers AS a 
    ON a.qid = qt.qid 
WHERE a.uid=1 
GROUP BY t.tag_id 
ORDER BY COUNT(a.qid) DESC; 

Le problème que je suis face à la requête ci-dessus est que si quelqu'un a noté l'expérience pour une étiquette particulière , cela apparaîtra pour l'utilisateur que ce soit leur expérience ou non. Je ne souhaite voir que l'expérience de cet utilisateur particulier, ce que cette requête ne fait tout simplement pas. J'ai rencontré ce problème ailleurs et j'ai été perplexe, alors j'ai dû le contourner.

J'ai essayé d'ajouter AND ue.uid = 1 à la requête, mais cela limitera les résultats sur seulement ceux où l'expérience a déjà été donnée, et ne renverra pas les valeurs NULL que je désire, aussi bien.

Des idées sur ce qu'il faut faire?

Répondre

2

Votre instinct AND ue.uid = 1 n'était pas faux, mais il appartient à la clause ON, donc il fait partie du JOIN, et il est utilisé pour déterminer quelles lignes peuvent être jointes.

LEFT JOIN users_experience AS ue 
    ON t.tag_id = ue.tag_id AND ue.uid = a.uid 

(Et jointure doit alors être placé sous la answers rejoindre.)

+0

Génial, merci! –

0

Je pense que vous devez inclure une table d'utilisateur. Filtrer par l'uid dans la table de l'utilisateur (où il est garanti d'être) et qui devrait vous donner ce que vous voulez. (y compris les valeurs nulles). Je ne sais pas exactement ce que votre table utilisateur ressemble mais je suppose votre requête serait quelque chose comme ceci:

SELECT t.tag_id, t.name, ue.body, COUNT(a.qid) 
    FROM users 
LEFT JOIN users_experience AS ue 
    ON users.uid = ue.uid 
LEFT JOIN tags 
    ON t.tag_id = ue.tag_id 
LEFT JOIN questions_tags AS qt 
    ON qt.tag_id = t.tag_id 
LEFT JOIN answers AS a 
    ON a.qid = qt.qid 
WHERE users.uid=1 
GROUP BY t.tag_id 
ORDER BY COUNT(a.qid) DESC; 

En ce qui concerne l'optimisation - LEFT JOIN est généralement considéré comme mauvais à moins que ces tables sont assez petites. MySQL doit fondamentalement rechercher toute la table pour les valeurs NULL. Il peut être plus rapide de vérifier si votre utilisateur a d'abord des enregistrements dans les tables jointes à gauche et d'émettre une seconde requête en utilisant le JOIN uniquement si vous trouvez des enregistrements.

Espérons que ça aide!

+0

Vous n'êtes pas joindre plus de 'users_experience' sur les balises pertinentes; vous comptez toute l'expérience dans tous les domaines. Et il n'est pas nécessaire de se joindre à 'users' si la valeur' uid' est déjà disponible (cela ajoute simplement un traitement inutile). – VoteyDisciple

+0

@VoteyDisciple - il veut les informations de l'utilisateur où ils ont de l'expérience ou non. Son problème d'origine indiquait que lorsqu'il ajoutait un ID utilisateur, il n'obtenait pas les valeurs NULL. En effet, l'ID utilisateur est nul dans sa table user_experience. J'ai joint des tags sur users_experience à l'envers. Ça fonctionne encore. (Notez que je ne pense pas que ce soit une bonne idée, je pense qu'il devrait repenser son design de table) – Cfreak