J'ai un problème et je ne sais pas quelle est la meilleure solution. Ok, j'ai 2 tables: posts (id, titre), posts_tags (post_id, tag_id). J'ai la tâche suivante: doit sélectionner des messages avec des étiquettes ID par exemple 4, 10 et 11. Pas exactement, la poste pourrait avoir d'autres tags en même temps. Alors, comment je pourrais le faire plus optimisé? Créer une table temporaire dans chaque requête? Ou peut-être une sorte de procédure stockée? À l'avenir, l'utilisateur pourrait demander à un script de sélectionner des messages avec n'importe quel nombre de tags (il peut s'agir de 1 tag ou 10 en même temps) et je dois être sûr que la méthode que je choisirai . Désolé pour mon anglais, Merci pour l'attention.requête many-to-many
Répondre
select id, title
from posts p, tags t
where p.id = t.post_id
and tag_id in (4,10,11) ;
?
Est-ce que cela fonctionne?
select *
from posts
where post.post_id in
(select post_id
from post_tags
where tag_id = 4
and post_id in (select post_id
from post_tags
where tag_id = 10
and post_id in (select post_id
from post_tags
where tag_id = 11)))
Cette solution suppose que (post_id, tag_id) en post_tags est appliquée comme UNIQUE:
SELECT id, title FROM posts
INNER JOIN post_tag ON post_tag.post_id = posts.id
WHERE tag_id IN (4, 6, 10)
GROUP BY id, title
HAVING COUNT(*) = 3
Bien que ce n'est pas une solution pour toutes les combinaisons de balises possibles, il est facile de créer aussi dynamique SQL. Pour changer pour d'autres ensembles de variables, changez la liste IN() pour avoir toutes les variables, et COUNT (*) = pour vérifier le nombre de variables spécifiées. L'avantage de cette solution par rapport à la cascade d'un ensemble de JOINs est qu'il n'est pas nécessaire d'ajouter des JOINs, ou même des termes WHERE supplémentaires, lorsque vous modifiez la requête.
+1 Pour utiliser GROUP BY avec HAVING. – Joop
Vous pouvez effectuer un compromis de stockage de temps en stockant un hachage unidirectionnel des noms de balises de tri triées par ordre alphabétique.
Lorsqu'un message est balisé, exécutez select t.name from tags t inner join post_tags pt where pt.post_id = [ID_of_tagged_post] order by t.name
. Concaténez tous les noms de balises, créez un hachage à l'aide de l'algorithme MD5 et insérez la valeur dans une colonne à côté de votre message (ou dans une autre table jointe par une clé étrangère, si vous préférez). Lorsque vous souhaitez rechercher une combinaison spécifique de variables, il suffit d'exécuter (en rappelant de trier les noms de variables) select from posts p where p.taghash = MD5([concatenated_tag_string])
.
Ce sélectionne tous les messages qui ont tout des étiquettes (4, 10, 11):
select distinct id, title from posts
where exists (
select * from posts_tags
where
post_id = id and
tag_id in (4, 10, 11))
Ou vous pouvez utiliser ceci:
select distinct id, title from posts
join posts_tags on post_id = id
where tag_id in (4, 10, 11)
(Les deux seront optimalisés la de la même façon).
Ce sélectionne tous les messages qui ont tous des balises (4, 10, 11):
select distinct id, title from posts
where not exists (
select * from posts_tags t1
where
t1.tag_id in (4, 10, 11) and
not exists (
select * from posts_tags as t2
where
t1.tag_id = t2.tag_id and
id = t2.post_id))
La liste des balises dans la clause in
est ce qui change de manière dynamique (dans tous les cas).
Mais, cette dernière requête n'est pas vraiment rapide, vous pouvez donc utiliser quelque chose comme ceci:
create temporary table target_tags (tag_id int);
insert into target_tags values(4),(10),(11);
select id, title from posts
join posts_tags on post_id = id
join target_tags on target_tags.tag_id = posts_tags.tag_id
group by id, title
having count(*) = (select count(*) from target_tags);
drop table target_tags;
La partie qui change dynamiquement maintenant dans la deuxième déclaration (l'insert).
Ceci sélectionnera les publications avec 1, 2 ou 3 des étiquettes désirées, pas les trois. Et il serait plus clairement écrit (et s'exécuter plus vite) s'il est exprimé comme un JOIN. –
J'ai ajouté du code pour sélectionner les articles qui ont tous les tags. –
J'ai également ajouté le code de jointure pour le premier cas. Bien que, un optimiseur de requête décent traitera le même que la requête avec la clause exists. –
Il pourrait renvoyer des messages avec l'étiquette 4 OU 10 ou 11.Mais j'ai exactement besoin de tous ces trois tags dans un seul article. Le problème est ici :) – user52005