我有3张表格,其中包含以下列:
Topics: [TopicID] [TopicName]
Messages: [MessageID] [MessageText]
MessageTopicRelations [EntryID] [MessageID] [TopicID]
消息可以是多个主题。问题是:给定几个主题,我需要获得关于所有这些主题的消息,而不是更少,但它们也可以是关于其他主题的。将不包括关于这些给定主题的一些消息。我希望我能很好地解释我的要求。否则我可以提供样本数据。感谢
答案 0 :(得分:5)
以下使用x
,y
和z
代表主题ID,因为没有提供示例。
SELECT m.*
FROM MESSAGES m
JOIN MESSAGETOPICRELATIONS mtr ON mtr.messageid = m.messageid
JOIN TOPICS tx ON tx.topicid = mtr.topicid
AND tx.topicid = x
JOIN TOPICS ty ON ty.topicid = mtr.topicid
AND ty.topicid = y
JOIN TOPICS tz ON tz.topicid = mtr.topicid
AND tz.topicid = z
SELECT m.*
FROM MESSAGES m
JOIN MESSAGETOPICRELATIONS mtr ON mtr.messageid = m.messageid
JOIN TOPICS t ON t.topicid = mtr.topicid
WHERE t.topicid IN (x, y, z)
GROUP BY m.messageid, m.messagetext
HAVING COUNT(*) = 3
GROUP BY / HAVING依赖MESSAGETOPICRELATIONS.TOPICID
作为主键的一部分,或者具有唯一键约束以确保没有重复。否则,您可能会有2个与消息关联的同一主题的实例 - 这将是误报。使用HAVING COUNT(DISTINCT ...
可以清除任何误报,但支持取决于数据库 - MySQL支持5.1+,但不支持4.1。 Oracle可能必须等到周一才能在SQL Server上进行测试...
我查看了Bill关于不需要加入TOPICS
表的评论:
SELECT m.*
FROM MESSAGES m
JOIN MESSAGETOPICRELATIONS mtr ON mtr.messageid = m.messageid
AND mtr.topicid IN (x, y, z)
...将返回误报 - 与IN
子句中定义的至少一个值匹配的行。和
SELECT m.*
FROM MESSAGES m
JOIN MESSAGETOPICRELATIONS mtr ON mtr.messageid = m.messageid
AND mtr.topicid = x
AND mtr.topicid = y
AND mtr.topicid = z
...根本不会返回任何内容,因为topicid
永远不会同时出现所有值。
答案 1 :(得分:1)
这是一个非常不优雅的解决方案
SELECT
m.MessageID
,m.MessageText
FROM
Messages m
WHERE
m.MessageID IN (
SELECT
mt.MessageID
FROM
MessageTopicRelations mt
WHERE
TopicID IN (1,4,5)// List of topic IDS
GROUP BY
mt.MessageID
HAVING
count(*) = 3 //Number of topics
)
答案 2 :(得分:1)
编辑:感谢@Paul Creasey和@OMG小马寻找我的方法中的缺陷 正确的方法是使用每个主题的自联接;如前面的答案所示。
另一个非常不优雅的条目:
select m.MessageText
, t.TopicName
from Messages m
inner join MessageTopicRelations mtr
on mtr.MessageID = m.MessageID
inner join Topics t
on t.TopicID = mtr.TopicID
and
t.TopicName = 'topic1'
UNION
select m.MessageText
, t.TopicName
from Messages m
inner join MessageTopicRelations mtr
on mtr.MessageID = m.MessageID
inner join Topics t
on t.TopicID = mtr.TopicID
and
t.TopicName = 'topic2'
...
答案 3 :(得分:1)
Re:OMG Ponies的回答,你不需要加入TOPICS
表。 HAVING COUNT(DISTINCT)
子句在MySQL 5.1中运行良好。我刚试过它。
这就是我的意思:
SELECT m.*
FROM MESSAGES m
JOIN MESSAGETOPICRELATIONS mtr ON mtr.messageid = m.messageid
WHERE mtr.topicid IN (x, y, z)
GROUP BY m.messageid
HAVING COUNT(DISTINCT mtr.topicid) = 3
我建议COUNT(DISTINCT)
的原因是,如果列(messageid,topicid)
没有唯一约束,则可能会出现重复项,这会导致组中的计数为3,即使少于三个不同的值。