Заставить mySQL присоединиться к таблице (сделать неоптимизированный запрос, который мне нужен) - PullRequest
1 голос
/ 21 марта 2009

Это довольно странно. У меня есть следующий запрос:

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

Как видите, я использую t, чтобы присоединиться, чтобы найти нужные мне сообщения, с другой стороны, я использую x, чтобы напечатать теги сообщения. Я стираю строку:

AND x.id = r.tag_id

Я получу сообщения, которые хочу, но теги будут иметь ВСЕ теги в таблице тегов, разделенные комой. Если я оставлю линию там, я получу только эти 2 тега. Если я использую объяснение, я получаю:

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

Сейчас я не эксперт в этом, но я думаю, что проблема в том, что он отказывается повторно присоединять таблицу в процессе оптимизации запроса.

Что ты думаешь? Любое быстрое решение?

Ответы [ 2 ]

1 голос
/ 22 марта 2009

Проблема в том, что вы пытаетесь присоединиться к таблице tag дважды, но вам действительно нужно присоединиться к таблице tag_message_rel дважды, и от каждого из них до соответствующей строки в таблице tag.

Думайте о «псевдонимах таблицы» как о ссылке на строку в таблице , а не на саму таблицу. Эта идея помогла мне лучше понять сложные объединения.

Вот как бы я написал этот запрос:

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;

Вы должны выработать привычку последовательно использовать синтаксис JOIN. Смешивание JOIN и объединение в стиле запятых может вызвать некоторые тонкие проблемы.

Вот альтернативный запрос, который вытягивает некоторые объединения в некоррелированный подзапрос, поэтому вы избегаете декартово произведение между t и x и исключаете модификаторы DISTINCT в групповых функциях.

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 голосов
/ 21 марта 2009

Вы получаете только эти два тега, потому что:

  1. t.tag IN ('kikikiki', 'dsa'). 2 t.id IN (id-for-kikikiki, id-for-dsa)
  2. t.id = r.tag_id
  3. r.tag_id = x.id
  4. Объединение 3 и 4, t.id = x.id
  5. Объединяя все, x.tag IN ('kikikiki', 'dsa')

Я думаю, что вы хотите присоединиться ко второму tag_message_rel. Присоединитесь t к первому; присоединить x ко второму; присоединяйся к обоим message. Не совсем уверен, что это то, что вы хотите, так как вы на самом деле не сказали ...

...