我有 3 个表格,其中包含以下列:
话题: [主题 ID] [主题名称]
留言: [消息 ID] [消息文本]
消息主题关系 [EntryID] [MessageID] [TopicID]
消息可以涉及多个主题。问题是:给定几个主题,我需要获取关于所有这些主题的消息,而不是更少,但它们也可以是关于其他主题的。不会包含与其中一些给定主题有关的消息。我希望我能很好地解释我的要求。否则我可以提供样本数据。谢谢
我有 3 个表格,其中包含以下列:
话题: [主题 ID] [主题名称]
留言: [消息 ID] [消息文本]
消息主题关系 [EntryID] [MessageID] [TopicID]
消息可以涉及多个主题。问题是:给定几个主题,我需要获取关于所有这些主题的消息,而不是更少,但它们也可以是关于其他主题的。不会包含与其中一些给定主题有关的消息。我希望我能很好地解释我的要求。否则我可以提供样本数据。谢谢
以下使用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 上进行测试...
我查看了比尔关于不需要加入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
...根本不会返回任何东西,因为 thetopicid
永远不可能同时成为所有值。
这是一个非常不优雅的解决方案
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
)
编辑:感谢@Paul Creasey 和@OMG Ponies 发现我的方法中的缺陷。
正确的方法是对每个主题进行自连接;如主要答案所示。
另一个非常不雅的条目:
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'
...
回复: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,即使不同值少于三个。