【问题标题】:Help with Many-to-Many Filtering Queries帮助多对多过滤查询
【发布时间】:2011-06-10 21:36:23
【问题描述】:

我正在尝试弄清楚如何在我的数据库中有效地实现标签。

一种方法是有一个文章表(比如Articles, ArtID int PK, ArtText varchar(max))和一个标签表(比如Tags, TagID int PK, TagTitle varchar(15))。然后我将创建一个连接表来创建文章和标签之间的多对多关系(比如说ArticleTags, ArtID int, TagID int (compound primary key)

我的第一个问题是编写查询以查找与给定标签关联的所有文章的最佳方法是什么?如果我想返回组合数据,我知道连接,但如果我只想知道哪些文章行与特定标签相关联,那么最有效的查询是什么。在现实生活中,我将需要查找具有多个标签的文章,并且还可以找到与特定标签无关的文章。

我的第二个问题是关于我的 Tags 表是否应该有一个 int PK?使用 TagTitle 作为主键是否更有意义?

【问题讨论】:

    标签: sql-server database database-design query-optimization


    【解决方案1】:

    (1)

    (a) 带有特定标签的文章:

    SELECT columns FROM Articles WHERE EXISTS 
      (SELECT null FROM ArticleTags at 
        WHERE at.ArtID = Articles.ArtID AND at.TagID=x)
    

    (b) 没有特定标签的文章:

    SELECT columns FROM Articles WHERE NOT EXISTS 
      (SELECT null FROM ArticleTags at 
        WHERE at.ArtID = Articles.ArtID AND at.TagID=x)
    

    (2) 如果您只是获取与文章关联的标签列表,则使用 TagTitle 会更快,但对于大多数其他可能的操作,代理 int 会更快。

    【讨论】:

    • 谢谢,但这真的是最有效的方法吗?对于 Articles 表中的每一行,这种方法都必须扫描 ArticleTags 表。
    • 自己尝试一下,看看查询的执行计划。只要有 ArticleTags.TagID 和 ArticleTags.ArtID 的索引,SQL Server 的实际计划就会非常高效。如果读取效率比写入重要得多,请务必将这些字段放在聚集索引中。
    【解决方案2】:

    1a) 我的第一个问题是编写查询以查找与给定标签关联的所有文章的最佳方法是什么?

    select ArtID  -- if only IDs are required
    from ArticleTags
    where TagID=1  -- or use the text (where tagtitle='x')
    
    select A.*
    from ArticleTags T inner join Articles A on A.ArtID = T.ArtID
    where T.TagID=1
    

    1b) 在现实生活中,我需要查找具有多个标签的文章

    -- has tags 1,3 and 8
    select T1.ArtID
    from ArticleTags T1
    inner join ArticleTags T2 on T1.ArtID = T2.ArtID and T2.TagID = 3
    inner join ArticleTags T3 on T1.ArtID = T3.ArtID and T3.TagID = 8
    where T1.TagID=1
      -- or use the text (tagtitle='x') on each filter
    

    在某些情况下,这种形式的工作速度更快。可以加入这个或上一个来获取文章记录。

    select ArtID
    from (
        select T1.ArtID from ArticleTags T1 where T1.TagID=1
        union all
        select T2.ArtID from ArticleTags T2 where T2.TagID=3
        union all
        select T3.ArtID from ArticleTags T3 where T3.TagID=8
    ) X
    group by ArtID
    having count(ArtID) = 3
    

    1c) 也可以找到与特定标签无关的文章。

    select A.*
    from Articles A
    left join ArticleTags T on T.ArtID = A.ArtID and T.TagTitle = 'nomatch'
    where T.ArtID is null
    

    2) 我的第二个问题是关于我的 Tags 表是否应该有一个 int PK?使用 TagTitle 作为主键是否更有意义?

    我坚信每个表都应该有一个连续的、无意义的整数 ID。它减少了存储空间(来自其他表的 FK)并且 int 查找/范围合并总是比 varchar 快。

    【讨论】:

    • 对于 1c,我会使用 NOT EXISTS sqlinthewild.co.za/index.php/2010/03/23/… 你为什么还要在 1b 中使用 UNION ALLIN(1,3,8) 似乎更直接
    • 这有点多。根据数据分布,并不总是一个比另一个快。虽然 NOT EXISTS 可能更简洁易读
    • 有人能帮我理解为什么像SELECT a.ArtID, ... FROM Articles a INNER JOIN ArticleTags at ON a.ArtID = at.ArtID INNER JOIN Tags t ON at.TagID = t.TagID WHERE t.TagTitle = 'tag'这样的事情不会更快吗?
    • 对 (1)(a) 的注意事项——只有在 TagIDArticleTags 表中的 TagIDArtID 的组合存在唯一约束时才能正常工作。似乎有道理,但如果没有,多个相同的标签将多次返回Article.*。使用EXISTS 可以避免这种情况,更能表达所需的输出,并且执行计划同样有效(应该几乎相同)。
    • @ric - 问题状态 ArtID int, TagID int (compound primary key) == 独一无二,来自我的阅读
    猜你喜欢
    • 1970-01-01
    • 2016-03-08
    • 2011-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-07
    • 1970-01-01
    相关资源
    最近更新 更多