SQLITE: Um problema de tags e produtos


10

Estou procurando uma maneira de criar uma consulta para fazer o seguinte:

Vamos considerar 3 tabelas:

  • produtos: Lista de produtos
  • tags: lista de tags
  • tag_ties: tabela usada para associar uma tag a um produto

Vamos considerar esta estrutura para cada tabela:

Produtos:

  • id (int, incremento automático)
  • nome (varchar, nome do produto)

Tag:

  • ID (incremento automático)
  • label (varchar, label da tag)

Tag_ties:

  • id (int, incremento automático)
  • tag_id (int, referência a um ID de tag)
  • ref_id (int, referência a um ID do produto)

O que eu quero:

Obtenha todos os produtos marcados com as tags 10, 11 e 12, por exemplo.

Esta consulta não funciona, pois retorna os produtos com pelo menos uma das tags:

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

Respostas:


9

Tente algo como isto:

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
;

Ele está trabalhando :)
Julien L

11

Você pode resolver esse problema usando instruções de interseção. Faça uma seleção separada para cada tag_id e junte-se a elas com interseções e você obterá apenas os registros que correspondem às três 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

Aqui está um artigo de referência sobre o uso de interseção

Você também pode usar uma exibição temporária para tornar isso um pouco melhor.

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

A subconsulta da resposta selecionada não é necessária. Para selecionar produtos com todos os IDs de tag fornecidos, a consulta pode ser simplesmente:

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 essa idéia, também podemos consultar com base nos rótulos das tags em uma única captura. Para selecionar produtos com as 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

Para complicar um pouco, podemos usar uma subconsulta para misturar intersecção ( AND) e união ( OR). A consulta abaixo retornará produtos com todas as tags do grupo ('foo', 'bar')e pelo menos uma das tags do grupo ('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
Você não precisa de um JOIN? Não, tecnicamente você não, mas há alguma razão para não usá-lo? E voltando à notação SQL-89 de junções implícitas?
precisa saber é o seguinte

5
Estou com voto negativo porque você deve usar sempre JOIN. stackoverflow.com/questions/5654278/... Sem uma junção explícita, seu código não seria implantado na minha loja
GBN

3
Ei pessoal, obrigado por me dizer que as junções implícitas são ruins. Pretendi principalmente salientar que a subconsulta da resposta selecionada não era necessária. Eu editei a resposta para usar junções. Se você encontrar mais alguma coisa errada em minhas consultas, entre em contato.
Moraes

5
+1 por aceitar o voto negativo sem ficar chateado e levar em consideração os conselhos para melhorar suas habilidades.
Zane

2
O que o @Zane disse. +1 também
gbn
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.