SQLITE: Un problème de tags et de produits


10

Je recherche un moyen de créer une requête pour effectuer les opérations suivantes:

Prenons 3 tableaux:

  • produits: Liste des produits
  • balises: liste de balises
  • tag_ties: tableau utilisé pour associer un tag à un produit

Considérons cette structure pour chaque table:

Des produits:

  • id (int, incrémentation automatique)
  • nom (varchar, nom du produit)

Mots clés:

  • id (auto-incrémentation)
  • label (varchar, label du tag)

Tag_ties:

  • id (int, incrémentation automatique)
  • tag_id (int, référence à un identifiant de tag)
  • ref_id (int, référence à un identifiant de produit)

Ce que je veux:

Obtenez tous les produits étiquetés avec les étiquettes id 10, 11 et 12 par exemple.

Cette requête ne fonctionne pas, car elle renvoie les produits ayant au moins l'une des balises:

select 
    p.name as name,
    p.id as id
from 
    products p inner join tag_ties ties
on
    p.id=ties.ref_id
where
    ties.ref_id=p.id and
    ties.tag_id in (10,11,12)
group by 
    p.id
order by 
    p.name asc

Réponses:


9

Essayez quelque chose comme ceci:

select
    t1.id,
    t1.name
from
    (
    select
        p.name as name,
        p.id as id
    from
        products p inner join tag_ties ties
    on
        p.id=ties.ref_id
    where
        ties.tag_id in (10,11,12)
    ) as t1
group by
    t1.id,
    t1.name
having
    count(t1.id) = 3
order by
    t1.name asc
;

Ça marche :)
Julien L

11

Vous pouvez résoudre ce problème à l'aide d'instructions d'intersection. Faites une sélection distincte pour chaque tag_id et joignez-les aux intersections et vous n'obtiendrez que les enregistrements qui correspondent aux trois tag_id.

select products.id, products.name from 
products join tag_ties
on tag_ties.ref_id = products.id
where tag_ties.tag_id = 10
intersect
select products.id, products.name from 
products join tag_ties
on tag_ties.ref_id = products.id 
where tag_ties.tag_id = 11
intersect
select products.id, products.name from 
products join tag_ties
on tag_ties.ref_id = products.id 
where tag_ties.tag_id = 12

Voici un article de référence sur l'utilisation de l'intersection

Vous pouvez également utiliser une vue temporaire pour rendre cela un peu plus agréable.

create temporary view temp_view as 
select name, products.id as id, tag_ties.tag_id as tag_id 
from products join tag_ties
on tag_ties.ref_id = products.id

select name, id from temp_view where tag_id = 10
intersect ...

8

La sous-requête de la réponse sélectionnée n'est pas nécessaire. Pour sélectionner des produits avec tous les identifiants de tag donnés, la requête peut être simplement:

SELECT 
    p.*
FROM 
    products AS p
INNER JOIN
    tag_ties AS tt
ON
    tt.ref_id = p.id
AND 
    tt.tag_id IN (10, 11, 12)
GROUP BY 
    p.id
HAVING 
    COUNT(p.id)=3

En étendant cette idée, nous pouvons également interroger en fonction des étiquettes de balises en une seule fois. Pour sélectionner des produits avec les tags ('foo', 'bar', 'baz'):

SELECT 
    p.*
FROM 
    products AS p
INNER JOIN
    tags AS t
ON
    t.label IN ('foo', 'bar', 'baz')
INNER JOIN
    tag_ties AS tt
ON
    tt.ref_id = p.id
AND 
    tt.tag_id = t.id
GROUP BY 
    p.id
HAVING 
    COUNT(p.id)=3

Pour compliquer un peu les choses, nous pouvons utiliser une sous-requête pour mélanger intersection ( AND) et union ( OR). La requête ci-dessous renverra des produits avec toutes les balises du groupe ('foo', 'bar')et au moins une des balises du groupe ('baz', 'ding'):

SELECT 
    p.*
FROM 
    (
    SELECT 
        p.*
    FROM 
        products AS p
    INNER JOIN 
        tags AS t
    ON
        t.label IN ('foo', 'bar')
    INNER JOIN 
        tag_ties AS tt
    ON
        tt.ref_id = p.id
    AND 
        tt.tag_id = t.id
    GROUP BY 
        p.id
    HAVING 
        COUNT(p.id)=2
    ) AS p
INNER JOIN 
    tags AS t
ON 
    t.label IN ('baz', 'ding')
INNER JOIN
    tag_ties AS tt
ON
    tt.ref_id = p.id
AND 
    tt.tag_id = t.id
GROUP BY 
    p.id

2
Vous n'en avez pas besoin JOIN? Non, techniquement non, mais y a-t-il une raison pour ne pas l'utiliser? Et pour revenir à la notation SQL-89 des jointures implicites?
ypercubeᵀᴹ

5
Je downvoting parce que vous devez utiliser la fonction Joindre toujours. stackoverflow.com/questions/5654278/… Sans un JOIN explicite, votre code ne serait pas déployé dans ma boutique
gbn

3
Hé les gars, merci de me dire que les jointures implicites sont un mauvais style. J'avais principalement l'intention de souligner que la sous-requête de la réponse sélectionnée n'était pas nécessaire. J'ai modifié la réponse pour utiliser les jointures. Si vous voyez autre chose de mal dans mes requêtes, faites-le moi savoir.
moraes

5
+1 pour avoir pris le downvote sans vous énerver et avoir réellement pris en considération les conseils pour améliorer vos compétences.
Zane

2
Ce que @Zane a dit. +1 aussi
gbn
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.