Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

consulta de um conjunto em um banco de dados relacional


Não comentarei se existe um esquema mais adequado para fazer isso (é bem possível), mas para um esquema com colunas name e item , a consulta a seguir deve funcionar. (sintaxe mysql)
SELECT k.name
FROM (SELECT DISTINCT name FROM sets) AS k
INNER JOIN sets i1 ON (k.name = i1.name AND i1.item = 1)
INNER JOIN sets i2 ON (k.name = i2.name AND i2.item = 3)
INNER JOIN sets i3 ON (k.name = i3.name AND i3.item = 5)
LEFT JOIN sets ix ON (k.name = ix.name AND ix.item NOT IN (1, 3, 5))
WHERE ix.name IS NULL;

A ideia é que tenhamos todas as chaves definidas em k , que então juntamos com os dados do item do conjunto em sets uma vez para cada item do conjunto que estamos procurando, três neste caso. Cada uma das três associações internas com aliases de tabela i1 , i2 e i3 filtre todos os nomes de conjuntos que não contenham o item pesquisado com essa junção. Finalmente, temos uma junção esquerda com sets com alias de tabela ix , que traz todos os itens extras do conjunto, ou seja, todos os itens que não estávamos procurando. ix.name é NULL no caso de nenhum item extra ser encontrado, que é exatamente o que queremos, portanto, o WHERE cláusula. A consulta retorna uma linha contendo a chave do conjunto se o conjunto for encontrado, caso contrário, nenhuma linha.

Editar: A ideia por trás da resposta colapsars parece ser muito melhor que a minha, então aqui está uma versão um pouco mais curta disso com explicação.
SELECT sets.name
FROM sets
LEFT JOIN (
    SELECT DISTINCT name
    FROM sets
    WHERE item NOT IN (1, 3, 5)
) s1
ON (sets.name = s1.name)
WHERE s1.name IS NULL
GROUP BY sets.name
HAVING COUNT(sets.item) = 3;

A ideia aqui é que a subconsulta s1 seleciona as chaves de todos os conjuntos que contêm itens diferentes dos que estamos procurando. Assim, quando saímos do join sets com s1 , s1.name é NULL quando o conjunto contém apenas itens que estamos procurando. Em seguida, agrupamos por chave de conjunto e filtramos quaisquer conjuntos com o número errado de itens. Ficamos então com apenas conjuntos que contêm apenas itens que estamos procurando e são do tamanho correto. Como os conjuntos só podem conter um item uma vez, só pode haver um conjunto que satisfaça esse critério, e é esse que estamos procurando.

Editar: Apenas me ocorreu como fazer isso sem a exclusão.
SELECT totals.name
FROM (
    SELECT name, COUNT(*) count
    FROM sets
    GROUP BY name
) totals
INNER JOIN (
    SELECT name, COUNT(*) count
    FROM sets
    WHERE item IN (1, 3, 5)
    GROUP BY name
) matches
ON (totals.name = matches.name)
WHERE totals.count = 3 AND matches.count = 3;

A primeira subconsulta encontra a contagem total de itens em cada conjunto e a segunda encontra a contagem de itens correspondentes em cada conjunto. Quando matches.count for 3, o conjunto terá todos os itens que estamos procurando e, se totals.count também é 3, o conjunto não possui itens extras.