【问题标题】:sql selection from one-to-many table从一对多表中选择sql
【发布时间】:2010-12-31 21:58:01
【问题描述】:

我有 3 个表格,下面是这些列:

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

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

【问题讨论】:

  • 只是为了澄清一点,您的架构看起来像是多对多,而不是一对多...?

标签: sql database select one-to-many


【解决方案1】:

以下使用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 永远不可能同时包含所有值。

【讨论】:

  • 您不需要加入到主题表中,只需要加入到 MessageTopicRelations 表中。我建议 COUNT(DISTINCT topicid) = 3 除非您可以依赖 MessageTopicRelations 表中的唯一约束 (messageid, topicid)。
  • @Bill:上次我检查时,HAVING COUNT(DISTINCT ... 在 MySQL 上不起作用。不知道那张纸条上这是什么数据库
  • 我曾经实现或遇到的每一个多对多表都有一个复合主键,所以我认为这个警告对于大多数模式来说是没有意义的
  • @Paul:ORM 不喜欢复合键 IIRC,关于 SO 的问题太多了,让我从不假设存在唯一键,更不用说主键了键:/
  • 嗯,很公平,我想假设这会让你和我大吃一惊,尤其是在 SO 上。 AFAIK 现在大多数 ORM 的处理复合键都很好,至少我用过的那些都有! :)
【解决方案2】:

这是一个非常不优雅的解决方案

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
    )

【讨论】:

  • '非常不优雅',有趣 :-) 但是我不相信这会起作用,因为除了 OP 需要的一种方式之外,count(*) 可以在许多不同的方式中等于三。
  • @Adam:您的担忧是有道理的——解决方案取决于数据模型。有关详细信息,请参阅我的回答,但请向我 +1。
  • 我假设多对多表使用复合键,这是我认为的典型和最佳实践。如果不是这种情况,则查询无效。
  • @Paul:点得好。我想这会让你成为一个乐观主义者,而我是一个悲观主义者;-)
【解决方案3】:

编辑:感谢@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'
...

【讨论】:

  • 切线:当在 Google App Engine 的数据存储区中查询 ListProperty 时,这种查询非常简单。
  • +1:这也可以,但需要比 JOIN 或 GROUP BY/HAVING COUNT 方法更多的输入。
  • 我不明白这个,我错过了什么吗?这不能解决问题,或者对我有意义。它肯定会返回任何主题的任何消息,以及当你可以使用 OR 或 IN 时为什么要联合
  • @Paul:现在我重新阅读它,是的 - 它会返回等同于使用 IN (1, 2, 3) 的结果,因为 UNION 不能确保返回的消息属于两个主题。
【解决方案4】:

回复: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,即使少于三个不同价值观。

【讨论】:

  • @Bill:这是我能猜到你在谈论的唯一方法 - 它必须与 HAVING COUNT(DISTINCT... 一起使用(这对我来说不适用于 4.1)。谢谢 - 感谢您的信息!
  • MySQL 4.1 已经超过五年了,它已经正式结束扩展支持。我认为根据积极支持的 5.1 版本来回答 StackOverflow 问题是合理的。
  • 我支持使用 4.1 的应用程序 - 我完全是老派 =)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 2011-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-24
  • 1970-01-01
相关资源
最近更新 更多