【问题标题】:SQL how to search a many to many relationshipSQL如何搜索多对多关系
【发布时间】:2010-09-25 20:39:19
【问题描述】:

我有一个包含两个主表noteslabels 的数据库。它们具有多对多的关系(类似于 stackoverflow.com 对标签的问题)。我想知道的是如何使用 SQL 使用多个标签搜索笔记?

例如,如果我有一个带有三个标签“一”、“二”和“三”的便笺“test”,而我有第二个带有标签“一”和“二”的便笺“test2”,那么 SQL 是什么将查找与标签“一”和“二”相关联的所有注释的查询?

【问题讨论】:

  • 请提供您的表格布局
  • 您是否尝试动态选择仅用于多个笔记的标签?如果是这样,我认为您将需要 HAVING 关键字
  • 请在您的问题中明确说明您是否对以下内容感兴趣: * 所有与标签“一”或“二”(或两者)相关的注释或 * 仅与 both 标签“一”和“二”。

标签: sql search many-to-many relational-division


【解决方案1】:
select * from notes a
inner join notes_labels mm on (mm.note = a.id and mm.labeltext in ('one', 'two') )

当然,替换为您的实际列名,希望我对您的表的假设是正确的。

实际上,由于英语以及有时如何使用“and”一词,您的问题可能存在一些歧义。例如,如果您的意思是要查看标记为“一”而不是“二”的注释,则应该可以(将您的“和”解释为“向我显示所有带有标签的注释”和/加上所有带有“二”标签的注释)。但是,如果您只想要具有两个标签的笔记,这将是一种方法:

select * from notes a
where exists (select 1 from notes_labels b where b.note = a.id and b.labeltext = 'one')
     and exists (select 1 from notes_labels c where c.note = a.id and c.labeltext = 'two')

编辑:感谢大家的建议,我大脑中的星期一齿轮有点慢......看起来我应该在维基上找到它!

【讨论】:

  • 括号都是多余的,但如果您正在构建类似的查询,可能会有用。
  • 这个条件永远不会成立:(mm.labeltext = 'one' and mm.labeltext = 'two')
  • (mm.labeltext = 'one' OR mm.labeltext = 'two') oder: mm.labeltext IN ('one', 'two')
  • IN ('One, 'Two') 会是更好的语法。
  • 为什么不将该代码放入代码块中以使其可读?
【解决方案2】:

类似这样...(您需要另一个链接表)

SELECT *
FROM Notes n INNER JOIN NoteLabels nl
ON n.noteId = nl.noteId
WHERE nl.labelId in (1, 2)

编辑:NoteLabel 表将有两列,noteId 和 labelId,具有复合 PK。

【讨论】:

    【解决方案3】:

    假设您有一个规范化的数据库,您应该在noteslabels 之间有另一个表

    然后您应该使用inner join 将表格连接在一起

    1. labels 表与绑定表(多对多表)一起加入
    2. notes 表与上一个查询结合起来

    例子:

    select * from ((labels l inner join labels_notes ln on l.labelid = ln.labelid) inner join notes n on ln.notesid = n.noteid)

    这样,您将两个表连接在一起。

    现在您需要添加的是 where 子句...但我会留给您。

    【讨论】:

      【解决方案4】:

      你什么也没说这种多对多关系是如何实现的。我假设标签表是 Labels(noteid: int, label: varchar) - 主键跨越两者?

      SELECT DISTINCT n.id from notes as n, notes_labels as nl WHERE n.id = nl.noteid AND nl.text in (label1, label2);
      

      替换为您的列名,并为标签插入适当的占位符。

      【讨论】:

        【解决方案5】:

        要获取同时标签为“一”和“二”的笔记的详细信息:

        select * from notes
        where note_id in
        ( select note_id from labels where label = 'One'
          intersect
          select note_id from labels where label = 'Two'
        )
        

        【讨论】:

        • 谢谢!迄今为止唯一能给出正确结果的解决方案! “...SQL 查询将查找与标签‘一’和‘二’相关的所有注释?”
        • 啊,你明白了。我现在有另一种语法,但直到现在我才看到你的。 +1
        【解决方案6】:

        如果你只需要一个列表,你可以使用where exists以避免重复。如果您的选择条件中有多个针对节点的标签,您将在结果中获得重复的行。以下是where exists 的示例:

        create table notes (
               NoteID int not null primary key
              ,NoteText varchar (max)
        )
        go
        
        create table tags (
               TagID int not null primary key
              ,TagText varchar (100)
        )
        go
        
        create table note_tag (
               NoteID int not null
              ,TagID int not null
        )
        go
        
        alter table note_tag
          add constraint PK_NoteTag
              primary key clustered (TagID, NoteID)
        go
        
        insert notes values (1, 'Note A')
        insert notes values (2, 'Note B')
        insert notes values (3, 'Note C')
        
        insert tags values (1, 'Tag1')
        insert tags values (2, 'Tag2')
        insert tags values (3, 'Tag3')
        
        insert note_tag values (1, 1) -- Note A, Tag1
        insert note_tag values (1, 2) -- Note A, Tag2
        insert note_tag values (2, 2) -- Note B, Tag2
        insert note_tag values (3, 1) -- Note C, Tag1
        insert note_tag values (3, 3) -- Note C, Tag3
        go
        
        select n.NoteID
              ,n.NoteText
          from notes n
         where exists
               (select 1
                  from note_tag nt
                  join tags t
                    on t.TagID = nt.TagID
                 where n.NoteID = nt.NoteID
                   and t.TagText in ('Tag1', 'Tag3'))
        
        
        NoteID      NoteText
        ----------- ----------------
        1           Note A
        3           Note C
        

        【讨论】:

          【解决方案7】:

          注意:我还没有实际测试过这个。它还假设您有一个名为 notes_labels 的多对多表,但情况可能根本不是这样。

          如果您只想要带有任何标签的注释,可以是这样的

          SELECT DISTINCT n.id, n.text
          FROM notes n
          INNER JOIN notes_labels nl ON n.id = nl.note_id
          INNER JOIN labels l ON nl.label_id = l.id
          WHERE l.label IN (?, ?)
          

          如果您想要包含所有标签的笔记,还有一些额外的工作

          SELECT n.id, n.text
          FROM notes n
          INNER JOIN notes_labels nl ON n.id = nl.note_id
          INNER JOIN labels l ON nl.label_id = l.id
          WHERE l.label IN (?, ?)
          GROUP BY n.id, n.text
          HAVING COUNT(*) = 2;
          

          ?是一个 SQL 占位符,2 是您要搜索的标签数。这是假设链接表有两个 ID 列作为复合主键。

          【讨论】:

            猜你喜欢
            • 2012-03-22
            • 1970-01-01
            • 1970-01-01
            • 2013-09-30
            • 2015-10-29
            • 2019-05-10
            • 1970-01-01
            • 2013-01-10
            • 1970-01-01
            相关资源
            最近更新 更多