SQLITE: un problema di tag e prodotti


10

Sto cercando un modo per creare una query per effettuare le seguenti operazioni:

Consideriamo 3 tabelle:

  • prodotti: Elenco dei prodotti
  • tags: elenco di tag
  • tag_ties: tabella utilizzata per associare un tag a un prodotto

Consideriamo questa struttura per ogni tabella:

Prodotti:

  • id (int, autoincrement)
  • nome (varchar, nome del prodotto)

tag:

  • id (int autoincrement)
  • etichetta (varchar, etichetta del tag)

Tag_ties:

  • id (int, autoincrement)
  • tag_id (int, riferimento a un ID tag)
  • ref_id (int, riferimento a un ID prodotto)

Quello che voglio:

Ottieni tutti i prodotti che sono taggati con i tag id 10, 11 e 12 per esempio.

Questa query non funziona, in quanto restituisce i prodotti con almeno uno dei tag:

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

Risposte:


9

Prova qualcosa del genere:

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
;

Funziona :)
Julien L

11

È possibile risolvere questo problema utilizzando le istruzioni intersect. Fai una selezione separata per ogni tag_id e uniscili a incroci e otterrai solo i record che corrispondono a tutti e tre i tag_ids.

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

Ecco un articolo di riferimento sull'uso di intersect

Puoi anche utilizzare una vista temporanea per rendere questo aspetto un po 'più bello.

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 sottoquery dalla risposta selezionata non è necessaria. Per selezionare prodotti con tutti gli ID tag indicati, la query può essere semplicemente:

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

Estendendo questa idea, possiamo anche eseguire una query in base alle etichette dei tag in un solo colpo. Per selezionare i prodotti con i tag ('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

Per complicarlo un po ', possiamo usare una sottoquery per mescolare intersection ( AND) e union ( OR). La query seguente restituirà i prodotti con tutti i tag del gruppo ('foo', 'bar')e almeno uno dei tag del gruppo ('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
Non hai bisogno di un JOIN? No, tecnicamente non lo fai ma c'è qualche motivo per non usarlo? E tornando alla notazione SQL-89 dei join impliciti?
ypercubeᵀᴹ

5
Sto effettuando il downvoting perché dovresti usare JOIN sempre. stackoverflow.com/questions/5654278/… Senza un JOIN esplicito, il tuo codice non verrebbe distribuito nel mio negozio
gbn

3
Ehi ragazzi, grazie per avermi detto che i join impliciti sono di cattivo stile. Intendevo principalmente sottolineare che la sottoquery della risposta selezionata non era necessaria. Ho modificato la risposta per utilizzare i join. Se vedi qualcos'altro che non va nelle mie domande, fammi sapere.
Moraes,

5
+1 per prendere il downvote senza arrabbiarsi e prendere effettivamente in considerazione i consigli per migliorare le tue abilità.
Zane,

2
Cosa ha detto @Zane. Anche +1
gbn
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.