【问题标题】:SQL SELECT with m:n relationship具有 m:n 关系的 SQL SELECT
【发布时间】:2009-11-05 23:14:48
【问题描述】:

我在用户和标签之间有 m:n 的关系。一个用户可以有m个标签,一个标签可以属于n个用户。表格看起来像这样:

USER:
ID
USER_NAME

USER_HAS_TAG:
USER_ID
TAG_ID

TAG:
ID
TAG_NAME

假设我需要选择所有标签为“apple”、“orange”和“banana”的用户。使用 SQL (MySQL DB) 完成此任务的最有效方法是什么?

【问题讨论】:

    标签: sql mysql


    【解决方案1】:
    SELECT  u.*
    FROM    (
            SELECT  user_id
            FROM    tag t
            JOIN    user_has_tag uht
            ON      uht.tag_id = t.id
            WHERE   tag_name IN ('apple', 'orange', 'banana')
            GROUP BY
                    user_id
            HAVING  COUNT(*) = 3
            ) q
    JOIN    user u
    ON      u.id = q.user_id
    

    通过删除HAVING COUNT(*),您会得到OR 而不是AND(尽管这不是最有效的方式)

    通过将3 替换为2,您可以获得恰好定义了三个标签中的两个的用户。

    通过将= 3 替换为>= 2,您可以获得至少定义了三个标签中的两个的用户。

    【讨论】:

    • 这肯定不是最有效的,因为它将聚合所有记录。例如。如果没有用户符合条件,就会做很多无用的工作 3 selfjoin 是有效的方法
    • @noonex:在真实世界的数据(大量用户、大量标签、高用户标签基数)上,这是一种有效的方法。 tag_name IN (...) 是一个 sargable 条件,它只会聚合带有数学标签的记录。如果您需要使查询匹配420 标签怎么办?使用自联接,您将需要重写查询结构,仅使用 GROUP BY 参数。
    【解决方案2】:

    除了其他好的答案之外,还可以检查 WHERE 子句中的条件:

    select *
    from user u
    where 3 = (
        select count(distinct t.id)
        from user_has_tag uht
        inner join tag t on t.id = uht.tag_id
        where t.name in ('apple', 'orange', 'banana') 
        and uht.user_id = u.userid
    )
    

    count(distinct ...) 确保一个标签只计算一次,即使用户有多个“香蕉”标签。

    顺便说一下,fruitoverflow.com 网站还没有注册:)

    【讨论】:

      【解决方案3】:

      您可以通过连接完成所有操作...

      select u.*
      from user u
      
      inner join user_has_tag ut1 on u.id = ut1.user_id 
      inner join tag t1 on ut1.tag_id = t1.id and t1.tag_name = 'apple'
      
      inner join user_has_tag ut2 on u.id = ut2.user_id 
      inner join tag t2 on ut2.tag_id = t2.id and t2.tag_name = 'orange'
      
      inner join user_has_tag ut3 on u.id = ut3.user_id 
      inner join tag t3 on ut3.tag_id = t3.id and t3.tag_name = 'banana'
      

      【讨论】:

      • 技术上更有效的方法是使用适当的 tag_id 和 selfjoin only user_has_tag 表(3次)。但是方法是正确的
      【解决方案4】:
      SELECT *
      FROM USER u
      INNER JOIN USER_HAS_TAG uht
      ON u.id = uht.user_id
      INNER JOIN TAG t
      ON uht.TAG_ID = t.ID
      WHERE t.TAG_NAME IN ('apple','orange','banana')
      

      【讨论】:

      • 如果您希望用户使用标签“apple”、“orange”或“banana”而不是全部三个标签,则此方法有效。
      猜你喜欢
      • 1970-01-01
      • 2017-02-26
      • 2011-08-24
      • 1970-01-01
      • 1970-01-01
      • 2021-12-31
      • 2014-08-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多