2010-12-10 18 views
2

J'utilise Postgresql. J'ai une table d'éléments, une table d'étiquettes, et une table qui lie beaucoup de balises à un article. Je dois sélectionner les éléments correspondant à 2 (ou plus) balises données.Sélection de l'endroit où plusieurs valeurs d'une table jointe correspondent à un ensemble donné

Je peux sélectionner des éléments qui correspondent à 1 ou plus avec WHERE tag.name IN ('tag1', 'tag2') En MySQL je pense que j'aurais pu dire WHERE x IN ALL (y, z), mais ça ne marche pas, c'est du pgsql. Jusqu'à présent, le mieux est de joindre les deux tables deux fois en utilisant une sous-requête. Évidemment, cela ne correspondra pas à l'appariement de nombreuses étiquettes et je suis certain que ce n'est pas l'approche la plus efficace.

SELECT * 
FROM item 
JOIN (SELECT item.id FROM item 
     JOIN tagged on tagged.item=item.id 
     JOIN tag ON tag.id=taged.tag 
     WHERE tag.name='tagOne') p ON p.id=item.id 
JOIN tagged ON tagged.item=item.id 
JOIN tag ON tag.id=tagged.tag 
WHERE tag.name='tagTwo'` 

Edit: Je teste encore les choses, mais en utilisant cela dans une sous-requête fonctionne mieux que ma ci-dessus tentative

SELECT item.id, count(tag2) AS relevance 
FROM item 
JOIN tagged tagged1 ON tagged1.item=item.id 
JOIN tag tag1 ON (tag1.id=tagged1.tag AND tag1.name='tag1') 
JOIN tagged tagged2 ON tagged2.item=item.id 
JOIN tag tag2 ON (tag2.id=tagged2.tag) 
WHERE tag2.name IN ('tag2', 'tag3') 
GROUP BY item.id 

Comme l'a demandé voici quelques définitions de table de clarification:

CREATE TABLE item (id serial, [...]); 
CREATE TABLE tag (id serial, name string UNIQUE); 
CREATE TABLE taged (tag int references tag(id), item int references item(id)); 
+1

Voir http://stackoverflow.com/questions/2161588/what-mysql-query-should-i-use-to-select-a-category-that-matches-all-my-criteria –

+1

Je ne sais pas pense 'WHERE x IN ALL (y, z)' fait ce que vous pensez dans MySQL. Pouvez-vous fournir un exemple MySQL afin que nous puissions voir exactement ce que vous pensez? –

+0

cela permettrait d'améliorer la question si vous ajoutez des définitions de table car le nom de la table/colonne est un peu déroutant –

Répondre

4

ici sont 3 (de nombreuses approches possibles):

select * 
from item 
where id in (select tagged.item 
       from tagged join tag on(tag.id=taged.tag) 
       where tag.name in('tagOne', 'tagTwo') 
       group by tagged.item 
       having count(*)=2); 

select * 
from item join (select tagged.item, count(*) as numtags 
       from tagged join tag on(tag.id=taged.tag) 
       where tag.name in('tagOne', 'tagTwo') 
       group by tagged.item) using (id) 
where numtags=2; 

select * 
from item 
where id in (select tagged.item 
       from tagged join tag on(tag.id=taged.tag) 
       where tag.name='tagOne' 
       intersect 
       select tagged.item 
       from tagged join tag on(tag.id=taged.tag) 
       where tag.name='tagTwo'); 

si vous voulez juste 2 ou plusieurs matches, mais vous ne me dérange pas que les balises sont présents:

select * 
from item 
where id in (select item 
       from tagged 
       group by item 
       having count(*)>=2); 
+0

Je voterais ça mais je n'ai pas encore le 15 représentant. Je vais laisser cela ouvert un peu plus longtemps et accepter cette réponse à moins qu'il n'y ait quelque chose de mieux posté, merci. –

+0

Hmm, est-ce que ça marche vraiment? L'idée est assez chouette mais joindre ID à ITEM ne semble pas être correct - même chose pour ID IN (item ...) –

+0

@a_horse 'item' est un nom de colonne de la table' tagged' - voir 'JOIN tagged on tagged. item = item.id' dans la question par exemple –

0

Je ne suis pas sûr que je l'ai compris, mais vous pouvez peut-être tout simplement écrire:

WHERE tag.name IN (y) AND tag.name IN (z) 
+0

Cela renvoie zéro résultat. La ligne testée par rapport à la condition where ne correspond pas aux deux conditions. –

0

J'aime utiliser la forme:

SELECT * 
FROM item 
WHERE EXISTS (SELECT 1 FROM tagged JOIN tag ON tag.id = tagged.tag 
       WHERE tag.name = 'tag1') 
     AND 
     EXISTS (SELECT 1 FROM tagged JOIN tag ON tag.id = tagged.tag 
       WHERE tag.name = 'tag2') 

Vous affirmez que « ce ne sera évidemment pas à l'échelle correspondant à plusieurs balises et je suis sûr est pas l'approche la plus efficace » - mais il semble lik e vous devinez?

+0

Je pense que le * "n'échelonnera pas" * se rapporte à la quantité de SQL qui doit être ajoutée/changée chaque fois qu'une nouvelle étiquette devrait être incluse –

+0

Je devine en ce qui concerne l'efficacité, mais un_horse_with_no_name est correct, je voudrais être capable de faire correspondre 2 ou plusieurs tags avec le même SQL. –