SQL как выполнить поиск отношения "многие ко многим"
-
20-08-2019 - |
Вопрос
У меня есть база данных с двумя основными таблицами notes
и labels
.У них есть отношения "многие ко многим" (аналогично тому, как stackoverflow.com имеет вопросы с ярлыками).Что мне интересно, так это как я могу выполнить поиск заметки, используя несколько меток, используя SQL?
Например, если у меня есть заметка "test" с тремя метками "one", "two" и "three", и у меня есть вторая заметка "test2" с метками "one" и "two", какой SQL-запрос найдет все заметки, связанные с метками "one" и "two"?
Решение
Чтобы получить подробную информацию о заметках, которые имеют и то, и другое метки "Один" и "Два":
select * from notes
where note_id in
( select note_id from labels where label = 'One'
intersect
select note_id from labels where label = '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 - количеством тегов, которые вы искали.Это предполагает, что таблица ссылок содержит оба столбца идентификаторов в качестве составного первичного ключа.
select * from notes a
inner join notes_labels mm on (mm.note = a.id and mm.labeltext in ('one', 'two') )
Конечно, замените ваши фактические названия столбцов, надеюсь, мои предположения о вашей таблице были правильными.
И на самом деле в вашем вопросе есть некоторая двусмысленность благодаря английскому языку и тому, как иногда используется слово "и".Если вы имеете в виду, что хотите увидеть, например, заметку с пометкой "один", но не "два", это должно сработать (интерпретируя ваши "и" как означающие "покажите мне все заметки с пометкой "один" и / или все заметки с пометкой "два").Однако, если вам нужны только заметки с обоими метками, это был бы один из способов добиться этого:
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')
Редактировать:спасибо всем за предложения, шестеренки понедельника в моем мозгу немного тормозят ... Похоже, я должен был сделать это вики!
Что-то вроде этого...(вам понадобится еще одна таблица ссылок)
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
Затем вы должны использовать inner 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 есть метки is(noteid:int, метка: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