1

这有点奇怪。我有下一个查询:

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

现在我不是这方面的专家,但我认为问题在于它拒绝在优化查询的过程中重新加入表。

你怎么看?有什么快速解决办法吗?

4

2 回答 2

1

问题是您尝试加入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和逗号样式的连接可能会导致一些微妙的问题。

这是一个替代查询,它将一些连接拉入一个不相关的子查询中,因此您可以避免 and 之间的笛卡尔积tx并消除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;
于 2009-03-22T03:38:15.520 回答
0

你只会得到这两个标签,因为:

  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。不完全确定这是您想要的,因为您实际上并没有说...

于 2009-03-21T06:32:56.093 回答