2015-09-10 43 views
0

我正在使用一个系统,用户可以将标签附加到消息上以便于搜索/识别(就像这里一样)。这是简化的架构:查找与标签组相关的标签

message: message_id 
tag: tag_id, tag_name 
tag_message: tag_id (FK), message_id (FK) 

我面临的问题是:

鉴于tag_id的输入列表,我想找到什么其他的标记出现在标记有在输入的信息标签

这是我想出了查询:

SELECT 
    tag2.tag_name, 
    COUNT(*) AS tagged_message_count 

FROM tag AS tag1 
    LEFT JOIN tag_message     ON tag_message.tag_id = tag1.tag_id 
    LEFT JOIN message      ON message.message_id = tag_message.message_id 
    LEFT JOIN tag_message AS tag_message2 ON tag_message2.message_id = message.message_id 
    LEFT JOIN tag   AS tag2   ON tag_message2.tag_id = tag2.tag_id 
WHERE 
    tag1.tag_id = ? 
AND 
    tag1.tag_id <> tag2.tag_id 
GROUP BY 
    tag2.tag_id; 

作品但它只适用于1个标签,我需要它与标签组合作。

鉴于标签ID 1,2,3我们应该首先找到所有标有这三个标签的邮件,然后查看其他标签并返回它们。

我有一种感觉,每个标签都会有额外的连接,但是我不确定如何修改查询来修改它。

+0

'...标记有这三个标签,...' :**全部**这三个标签,或者**这三个标签中的任何**? – joop

+0

所有这三个标签都需要附加到邮件中,我们通过标签过滤邮件(我们只想要包含所有这些标签的邮件,但可能还有其他一些) – Eleeist

回答

1

可以使用发现标记为1,2, 3消息:

select tm.message_id 
from tag_message tm 
where tm.tag_id in (1, 2, 3) 
group by tm.message_id 
having count(*) = 3; 

您可以使用找到其他标签:

select tag_id, count(*) 
from tag_message 
where message_id in (select tm.message_id 
        from tag_message tm 
        where tm.tag_id in (1, 2, 3) 
        group by tm.message_id 
        having count(*) = 3 
        ) and 
     tag_id not in (1, 2, 3) 
group by tag_id 
order by count(*) desc; 

如果你想有标签1的消息, 2, 3,然后删除having子句。

+0

谢谢,第二个查询是我正在查找的内容。查询不会按原样编译,因为您在第一个查询中忘记了GROUP BY,在第二个示例中忘记了嵌套查询。这两个修复程序后,它按预期工作:) – Eleeist

0

这可能过于复杂,但它至少是一种替代解决方案...


 -- For convience: put the arguments to the query into a CTE 
     -- (a temp table *could* be faster for larger sets) 
     -- ------------------------------------------- 
WITH args(tagid) AS (
    VALUES (2), (3) , (4) 
    ) 
     -- messages that have ALL the tags 
     -- [the double NOT EXISTS 
     -- is a standard relational-division solution] 
, msg_with_all_args AS (
    SELECT msgid FROM msg m 
    WHERE NOT EXISTS (
     SELECT * FROM tag t 
     WHERE EXISTS (SELECT * FROM args a WHERE a.tagid = t.tagid) 
     AND NOT EXISTS (
      SELECT * FROM msg_tag mt 
      WHERE mt.msgid = m.msgid 
      AND mt.tagid = t.tagid 
      ) 
     ) 
    ) 
     -- additional tags, associated with 
     -- messages with all tags 
, more_tags AS (
    SELECT * FROM tag t 
    WHERE EXISTS (
     SELECT * 
     FROM msg_tag mt 
     JOIN msg_with_all_args at ON at.msgid = mt.msgid 
     AND mt.tagid = t.tagid 
     ) 
     -- exclude the tags that are already in the set 
    AND NOT EXISTS (SELECT * FROM args nx where nx.tagid = t.tagid) 
    ) 
SELECT * FROM more_tags 
    ;