Заставить mySQL присоединиться к таблице (сделать неоптимизированный запрос, который мне нужен)
Это немного странно. У меня есть следующий запрос:
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 ответа
Проблема в том, что вы пытаетесь присоединиться к 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;
Вы получаете только эти два тега, потому что:
- t.tag IN ('kikikiki', 'dsa'). 2 t.id IN (id-for-kikikiki, id-for-dsa)
- t.id = r.tag_id
- r.tag_id = x.id
- Объединяя 3 и 4, t.id = x.id
- Объединяя все, x.tag IN ('kikikiki', 'dsa')
Я думаю, что вы хотите присоединиться ко второму tag_message_rel
, Присоединиться t
к первому; присоединиться x
ко второму; присоединиться к ним обоим message
, Не совсем уверен, что это то, что вы хотите, как вы на самом деле не сказали...