Join [RESOLVIDO]

6 respostas
gqferreira

Boa noite pessoal.
Tenho uma dúvida aparentemente simples mas ainda não caiu a ficha de como resolver.

Vamos ao exemplo:

Table: PRODUTO
|codigo | nome|
|1 |prodX |
|2 |prodY |
|3 |prodZ |

Table: MATERIA_PRIMA
|codigo | nome |
|1 | Ferro |
|2 | Papel |
|3 | Plastico |
|4 | Borracha|

Blz… até aê simples… apenas duas tabelas com dois campos cada um, uma de produto e outra de materia prima. Como um produto é constituido de matéria prima, segue agora a tabela N-N.

Table: PRODUTO_COMPOSICAO
|fk_codigo_produto | fk_codigo_materia |
|1 |1 |
|1 |2 |
|2 |1 |
|2 |2 |
|2 |3 |
|3 |4 |
|3 |1 |
Com essa tabela eu quis dizer o seguinte:
O produto ‘prodX’ é composto de ‘Ferro’ e 'Papel’
O produto ‘prodY’ é composto de ‘Ferro’, ‘Papel’ e 'Plástico’
O produto ‘prodZ’ é composto de ‘Borracha’ e ‘Papel’

Tudo bem até aqui?

Agora vem a dúvida:
Preciso fazer uma consulta de produto filtrada por materia prima, por exemplo: Listar todos os produtos que tenha pelo menos ‘Ferro’ e ‘Papel’. O resultado seria o produto ‘prodX’ e o ‘prodY’.

Bom, acho que a dúvida é simples, agora não sei quanto a solução pois não fui capaz de conseguir :?

6 Respostas

Adelar

Olá,
você pode consultar na tabela PRODUTO_COMPOSICAO e escolher uma chave ou outra. Segue SQL:

select distinct(fk_codigo_produto) from PRODUTO_COMPOSICAO where fk_codigo_materia = 1 or fk_codigo_materia = 2;
O distinct é para evitar repetição caso um produto possua duas composições.

[]'s

A

A solução do Adelar funcionaria legal se você precisasse dos produtos com Papel OU com Ferro…
No seu caso que precisa de ambos é um pouco mais complexo…

Aliás, que banco de dados está utilizando?

A primeira idéia que me vem a cabeça (e não deve ser a melhor), é adicionar no WHERE uma condição para cara matéria prima obrigatória:

SELECT *
  FROM
    PRODUTO P
  WHERE
    EXISTS( SELECT 1 FROM PRODUTO_COMPOSICAO PC INNER JOIN MATERIA_PRIMA MP ON PC.FK_CODIGO_MATERIA = MP.CODIGO WHERE MP.NOME = 'Ferro' )
  AND EXISTS( SELECT 1 FROM PRODUTO_COMPOSICAO PC INNER JOIN MATERIA_PRIMA MP ON PC.FK_CODIGO_MATERIA = MP.CODIGO WHERE MP.NOME = 'Papel' )

Provavelmente eu melhoraria esta query contando se o produto possui o número de condições necessárias (no exemplo 2).

gqferreira

Muito obrigado Abel, com pequenas adaptações funcionou perfeito.
Realmente a ideia do Adelar não daria muito certo, mas mesmo assim, muito obrigado!.

SELECT * FROM produto WHERE EXISTS (SELECT 1 FROM produto_composicao WHERE codigo_materia_prima = 1 AND codigo_produto = produto.codigo) AND EXISTS (SELECT 1 FROM produto_composicao WHERE codigo_materia_prima = 2 AND codigo_produto = produto.codigo)

No caso eu nao interagi com a tabela materia_prima porque eu vou fazer a pesquisa com o codigo da materia prima e nao com o nome literal dela, eu tenho já esse codigo… coloquei a tabela de materia_prima para ilustrar melhor o ambiente…

Há… o banco é PostgreSQL mas a sql ficou bem genérica, vai rodar em todos os sgbds (acho :roll: )

renamed
Select 
	distinct(Prod.Nome)
From 
	Produto Prod 
		Inner Join PRODUTO_COMPOSICAO Prod_Comp On Prod.Codigo = Prod_Comp.fk_codigo_produto
		Inner Join MATERIA_PRIMA MP On MP.Codigo = Prod_Comp.fk_codigo_materia
Where
	MP.nome = 'Ferro' And MP.nome = 'Papel'
A

Então gqferreira,

Tinha um erro na minha subquery anterior por não fazer join com a produto, mas você já corrigiu…

De qualquer forma, agora que tive tempo pra pensar, tem uma solução que acredito ser mais limpa e deve até rodar melhor.

O problema da anterior é que terá que colocar um exists(subquery) para cada matéria prima do produto…
Acho que poderia utilizar a abordagem abaixo:

SELECT *
FROM
  produto p
  INNER JOIN 
  (
    SELECT pc.fk_codigo_produto 
    FROM
      produto_composicao pc
    WHERE
      pc.fk_codigo_materia in (1,5)
    GROUP BY
      pc.fk_codigo_produto
    HAVING
      COUNT(pc.fk_codigo_materia) = 2
  ) cont
  ON p.codigo = cont.fk_codigo_produto

Neste caso, a única coisa que varia na query é a lista de materiais in (1,5) e a quantidade esperadas de materiais por produto COUNT(pc.fk_codigo_materia) = 2.

Adelar

Já está resolvido mas vou postar a outra versão que fiz. Não tinha notado que não poderia resolver. Não testei, mas acho que desta forma funciona:

select P.codigo from PRODUTO P left join PRODUTO_COMPOSICAO PC on P.codigo = PC.fk_codigo_produto where PC.fk_codigo_materia in (1,2) and PC.fk_codigo_produto is not null;
[]'s

Criado 28 de janeiro de 2011
Ultima resposta 29 de jan. de 2011
Respostas 6
Participantes 4