2008-12-08 8 views
4

J'ai une base de données avec deux tables principales notes et labels. Ils ont une relation plusieurs-à-plusieurs (similaire à la façon dont stackoverflow.com a des questions avec des étiquettes). Ce que je me demande est comment puis-je rechercher une note en utilisant plusieurs étiquettes en utilisant SQL? Par exemple si j'ai une note "test" avec trois étiquettes "un", "deux" et "trois" et j'ai une deuxième note "test2" avec les étiquettes "un" et "deux" quel est le Requête SQL qui trouvera toutes les notes associées aux étiquettes "un" et "deux"?SQL comment rechercher une relation plusieurs à plusieurs

+0

Veuillez indiquer le format de votre tableau – JSC

+0

Essayez-vous de sélectionner dynaimquement uniquement les étiquettes utilisées sur plusieurs notes? Si oui, je pense que vous aurez besoin du mot-clé AYANT – Brabster

+0

S'il vous plaît de préciser dans votre question de savoir si vous êtes intéressé par: * Toutes les notes associées à l'étiquette « un » ou « deux » (ou les deux) ou * Seules les notes associées à _both_ étiquettes "un" et "deux". –

Répondre

7

Pour obtenir les détails des notes qui ont les deux étiquettes de One »et « 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' 
) 
+0

Merci! seule solution qui donne le résultat correct jusqu'à présent! "... Requête SQL qui trouvera toutes les notes associées aux étiquettes 'un' et 'deux'?" –

+0

Ah, vous l'avez. J'ai maintenant une syntaxe alternative sur la mienne, mais je n'ai pas vu la tienne jusqu'à maintenant. +1 – Kev

1
select * from notes a 
inner join notes_labels mm on (mm.note = a.id and mm.labeltext in ('one', 'two')) 

Bien sûr, remplacez par vos noms de colonnes réels, j'espère que mes hypothèses sur votre table étaient correctes.

En fait, il y a un peu d'ambiguïté possible dans votre question grâce à l'anglais et à la façon dont le mot «et» est parfois utilisé. Si vous voulez dire que vous voulez voir, par exemple, une note étiquetée 'un' mais pas 'deux', cela devrait fonctionner (interpréter votre 'et' signifier, 'montrer toutes les notes avec l'étiquette' un 'et/ou tout les notes avec l'étiquette «deux»). Toutefois, si vous ne voulez notes qui ont les deux étiquettes, ce serait une façon d'aller à ce sujet:

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') 

Edit: Merci pour les suggestions tout le monde, les rapports lundi dans mon cerveau sont un peu lent ... On dirait que je devrais l'avoir wiki!

+0

Les parenthèses sont toutes redondantes mais pourraient être utiles si vous construisiez des requêtes similaires. – Kev

+0

Cette condition ne sera jamais vraie: (mm.labeltext = 'un' et mm.labeltext = 'deux') –

+0

(mm.labeltext = 'un' OU mm.labeltext = 'deux') ou mm.labeltext IN ('un', 'deux') –

0

Quelque chose comme ça ... (vous aurez besoin d'une autre table de lien)

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

Modifier: la table NoteLabel aura deux colonnes, NoteID et labelId, avec un composite PK.

0

En supposant que vous avez une base de données normalisée, vous devriez avoir une autre table entre notes et labels

Vous devriez alors utiliser un inner join pour joindre les tables ensemble

  1. joindre à la table labels avec le bind- Table (many-to-many tableau)
  2. joindre à la table notes avec la requête précédente

Exemple:

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

De cette façon, vous avez connecté les deux tables ensemble.

Maintenant, ce que vous devez ajouter est la clause where ... mais je vous laisse le soin de le faire.

0

Vous ne dites rien sur la façon dont cette relation many-to-many est réalisée. Je suppose que la table des étiquettes a des étiquettes (note: int, label: varchar) - avec une clé primaire couvrant les deux?

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

Remplacez par vos noms de colonnes et insérez les espaces réservés appropriés pour les étiquettes.

0

Si vous avez juste besoin d'une liste, vous pouvez utiliser where exists pour éviter les doubles emplois. Si vous avez plusieurs tags par rapport à un nœud dans vos critères de sélection, vous obtiendrez des lignes dupliquées dans le résultat. Voici un exemple de 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 
1

Remarque: Je n'ai pas réellement testé cela. Il suppose également que vous avez une table many-to-many nommée notes_labels, ce qui peut ne pas être du tout le cas.

Si vous voulez juste les notes présentant l'une des étiquettes, il est quelque chose comme ceci

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 (?, ?) 

Si vous voulez que les notes qui ont toutes les étiquettes, il y a un peu de travail supplémentaire

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; 

? être un espace réservé SQL et 2 étant le nombre de tags que vous recherchiez. Cela suppose que la table de liens possède les deux colonnes d'ID en tant que clé primaire composée.

+0

Fait ma journée! Merci ... Merci Google! – fanjabi