从一对多表中选择sql

时间:2010-01-03 02:48:37

标签: sql database select one-to-many

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

Topics:
[TopicID] [TopicName]
Messages:
[MessageID] [MessageText]
MessageTopicRelations
[EntryID] [MessageID] [TopicID]

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

4 个答案:

答案 0 :(得分:5)

以下使用xyz代表主题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上进行测试...

我查看了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中运行良好。我刚试过它。

这就是我的意思:

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