2009-03-21 5 views
1

C'est un peu bizarre. J'ai la requête suivante:Forcer mySQL à rejoindre une table (pour faire une requête non optimisée, ce dont j'ai besoin)

SELECT * , GROUP_CONCAT(x.tag 
SEPARATOR ',') AS tags 
FROM tag AS t, tag AS x, tag_message_rel AS r, message m 
INNER JOIN `user` AS u ON m.user_id = u.id 
WHERE t.tag 
IN (
'kikikiki', 'dsa' 
) 
AND m.id = r.message_id 
AND t.id = r.tag_id 
AND x.id = r.tag_id 
GROUP BY m.id 
HAVING COUNT(*) >=2 
ORDER BY m.created_at DESC 
LIMIT 0 , 20 

Comme vous pouvez le voir j'utilise t à se joindre à trouver les messages que je veux, de l'autre côté j'utiliser x pour imprimer les étiquettes d'un message. I i effacer la ligne:

AND x.id = r.tag_id 

je vais obtenir les messages que je veux, mais les balises auront toutes les balises dans la table tags séparés par des virgules. Si je laisse la ligne là, je n'aurai que ces 2 tags. Si j'utilise expliquer i get:

id select_type  table type possible_keys key  key_len  ref  rows Extra 
1 SIMPLE u system PRIMARY  NULL NULL NULL 1 Using temporary; Using filesort 
1 SIMPLE t range PRIMARY,tag  tag  252  NULL 2 Using where 
1 SIMPLE x eq_ref PRIMARY  PRIMARY  4 verse.t.id 1  
1 SIMPLE r ALL  NULL NULL NULL NULL 180  Using where; Using join buffer 
1 SIMPLE m eq_ref PRIMARY  PRIMARY  4 verse.r.message_id 1 Using where 

maintenant je suis pas expert dans ce domaine, mais je pense que le problème est qu'il refuse de réintégrer une table dans le processus d'optimisation de la requête.

Qu'en pensez-vous? Une solution rapide?

Répondre

1

Le problème est que vous essayez de joindre deux fois à la table de tag, mais vous avez vraiment besoin de se joindre à la double table de tag_message_rel, et de chacun de ceux-ci à la ligne respective dans le tableau tag. Pensez à "alias de table" comme faisant référence à une ligne dans une table, pas la table elle-même. Cette idée m'a aidé à mieux comprendre les jointures complexes.

Voilà comment j'écrire cette requête:

SELECT m.*, u.*, GROUP_CONCAT(DISTINCT x.tag) AS tags 
FROM message m 
JOIN `user` u ON (u.id = m.user_id) 
JOIN tag_message_rel r1 ON (m.id = r1.message_id) 
JOIN tag t ON (t.id = r1.tag_id) 
JOIN tag_message_rel r2 ON (m.id = r2.message_id) 
JOIN tag x ON (x.id = r2.tag_id) 
WHERE t.tag IN ('kikikiki', 'dsa') 
GROUP BY m.id 
HAVING COUNT(DISTINCT t.tag) = 2 
ORDER BY m.created_at DESC 
LIMIT 0 , 20; 

Vous devez développer l'habitude d'utiliser systématiquement JOIN syntaxe. Le mélange JOIN et les jointures de style virgule peuvent causer des problèmes subtils.

est ici une requête de remplacement, qui tire une partie des jointures dans une sous-requête non corrélée, de sorte que vous éviter un produit cartésien entre t et x, et gomment les DISTINCT modificateurs des fonctions du groupe.

SELECT m.*, u.*, GROUP_CONCAT(x.tag) AS tags 
FROM message m 
JOIN `user` u ON (u.id = m.user_id) 
JOIN tag_message_rel r ON (m.id = r.message_id) 
JOIN tag x ON (x.id = r.tag_id) 
WHERE m.id = ANY (
    SELECT m2.id 
    FROM message m2 
    JOIN tag_message_rel r2 ON (m2.id = r2.message_id) 
    JOIN tag t ON (t.id = r2.tag_id) 
    WHERE t.tag IN ('kikikiki', 'dsa') 
    GROUP BY m2.id 
    HAVING COUNT(t.tag) = 2) 
GROUP BY m.id 
ORDER BY m.created_at DESC 
LIMIT 0 , 20; 
0

Vous obtenez seulement ces deux balises, parce que:

  1. t.tag IN ('kikikiki', 'dsa'). 2 t.id IN (id-à-kikikiki, id-for-dsa)
  2. t.id = r.tag_id
  3. r.tag_id = x.id
  4. La combinaison 3 & 4, t.id = x.id
  5. la combinaison de tout, x.tag IN ('kikikiki', 'dsa')

Je pense que ce que vous voulez est de se joindre à un deuxième tag_message_rel. Rejoignez t au premier; joindre x à la seconde; joignez-les tous les deux au message. Pas tout à fait sûr que ce soit ce que vous voulez, comme vous l'avez dit pas ...

Questions connexes