SQL как искать отношения многие ко многим - PullRequest
5 голосов
/ 08 декабря 2008

У меня есть база данных с двумя основными таблицами notes и labels. Они имеют отношение многие ко многим (подобно тому, как stackoverflow.com имеет вопросы с метками). Меня интересует, как я могу найти заметку, используя несколько меток, используя SQL?

Например, если у меня есть заметка «test» с тремя метками «one», «two» и «three», и у меня есть вторая заметка «test2» с метками «one» и «two», что такое SQL запрос, который найдет все заметки, связанные с метками «один» и «два»?

Ответы [ 7 ]

8 голосов
/ 08 декабря 2008

Чтобы получить сведения о примечаниях, которые имеют оба ярлыки «Один» и «Два»:

select * from notes
where note_id in
( select note_id from labels where label = 'One'
  intersect
  select note_id from labels where label = 'Two'
)
1 голос
/ 08 декабря 2008

Примечание: я на самом деле не проверял это. Также предполагается, что у вас есть таблица «многие ко многим» с именем 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 - количеством тегов, которые вы искали. Предполагается, что таблица ссылок содержит оба столбца идентификатора в качестве составного первичного ключа.

1 голос
/ 08 декабря 2008
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')

Редактировать: спасибо за предложения всем, в понедельник у меня в мозгу немного медленно ... похоже, я должен был это сделать вики!

0 голосов
/ 08 декабря 2008

Если вам нужен только список, вы можете использовать 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
0 голосов
/ 08 декабря 2008

Вы ничего не говорите о том, как реализовано это отношение ко многим. Я предполагаю, что таблица меток имеет метки (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);

Замените ваши имена столбцов и вставьте соответствующие заполнители для меток.

0 голосов
/ 08 декабря 2008

Если у вас есть нормализованная база данных, у вас должна быть другая таблица между notes и labels

Затем вы должны использовать 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 ... но я оставлю это на ваше усмотрение.

0 голосов
/ 08 декабря 2008

Примерно так ... (вам понадобится еще одна таблица ссылок)

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

Редактировать : таблица NoteLabel будет иметь два столбца, noteId и labelId, с составным PK.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...