3

我有 3 个表格,其中包含以下列:

话题:
[主题 ID] [主题名称]
留言:
[消息 ID] [消息文本]
消息主题关系
[EntryID] [MessageID] [TopicID]

消息可以涉及多个主题。问题是:给定几个主题,我需要获取关于所有这些主题的消息,而不是更少,但它们也可以是关于其他主题的。不会包含与其中一些给定主题有关的消息。我希望我能很好地解释我的要求。否则我可以提供样本数据。谢谢

4

4 回答 4

5

以下使用x,yz代表主题 ID,因为没有提供示例。

使用 JOIN:

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

使用 GROUP BY/HAVING COUNT(*):

  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

在这两者中,JOIN 方法更安全。

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永远不可能同时成为所有值。

于 2010-01-03T03:01:22.037 回答
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
    )
于 2010-01-03T02:56:11.007 回答
1

编辑:感谢@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'
...
于 2010-01-03T03:00:12.007 回答
1

回复:OMG Ponies 的回答,你不需要加入TOPICS表。该HAVING COUNT(DISTINCT)子句在 MySQL 5.1 中运行良好。我刚刚测试了它。

这就是我的意思:

使用 GROUP BY/HAVING COUNT(*):

  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,即使不同值少于三个。

于 2010-01-04T06:01:57.177 回答