我有一个包含两个主表notes
和labels
. 它们具有多对多的关系(类似于 stackoverflow.com 对标签的问题)。我想知道的是如何使用 SQL 使用多个标签搜索笔记?
例如,如果我有一个带有三个标签“一”、“二”和“三”的便笺“test”,而我有一个带有标签“一”和“二”的第二个便笺“test2”,那么 SQL 查询将是什么?找到所有与标签“一”和“二”相关的笔记?
我有一个包含两个主表notes
和labels
. 它们具有多对多的关系(类似于 stackoverflow.com 对标签的问题)。我想知道的是如何使用 SQL 使用多个标签搜索笔记?
例如,如果我有一个带有三个标签“一”、“二”和“三”的便笺“test”,而我有一个带有标签“一”和“二”的第二个便笺“test2”,那么 SQL 查询将是什么?找到所有与标签“一”和“二”相关的笔记?
要获取同时具有标签“一”和“二”的注释的详细信息:
select * from notes
where note_id in
( select note_id from labels where label = 'One'
intersect
select note_id from labels where label = 'Two'
)
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')
编辑:感谢大家的建议,我大脑中的星期一齿轮有点慢......看起来我应该维基百科!
注意:我还没有实际测试过这个。它还假设您有一个名为 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 列作为复合主键。
像这样的东西......(你需要另一个链接表)
SELECT *
FROM Notes n INNER JOIN NoteLabels nl
ON n.noteId = nl.noteId
WHERE nl.labelId in (1, 2)
编辑:NoteLabel 表将有两列,noteId 和 labelId,带有一个复合 PK。
假设您有一个规范化的数据库,您应该在两者之间有另一个notes
表labels
然后,您应该使用 aninner join
将表连接在一起
labels
使用绑定表(多对多表)加入表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
子句……但我将由您决定。
你什么也没说这种多对多关系是如何实现的。我假设标签表是 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);
替换为您的列名,并为标签插入适当的占位符。
如果你只需要一个列表,你可以使用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