sql >> Base de Datos >  >> RDS >> Mysql

consulta de un conjunto en una base de datos relacional

No comentaré si existe un esquema más adecuado para hacer esto (es bastante posible), pero para un esquema que tenga columnas name y item , la siguiente consulta debería funcionar. (sintaxis 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;

La idea es que tengamos todas las claves establecidas en k , que luego unimos con los datos del elemento del conjunto en sets una vez por cada elemento del conjunto que estamos buscando, tres en este caso. Cada una de las tres uniones internas con alias de tabla i1 , i2 y i3 filtre todos los nombres de conjunto que no contengan el elemento buscado con esa unión. Finalmente, tenemos una combinación izquierda con sets con alias de tabla ix , que incluye todos los elementos adicionales del conjunto, es decir, todos los elementos que no estábamos buscando. ix.name es NULL en el caso de que no se encuentren elementos adicionales, que es exactamente lo que queremos, por lo tanto, WHERE cláusula. La consulta devuelve una fila que contiene la clave del conjunto si se encuentra el conjunto, no hay filas de lo contrario.

Editar: La idea detrás de la respuesta de colapsar parece ser mucho mejor que la mía, así que aquí hay una versión un poco más corta con una explicación.

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;

La idea aquí es que la subconsulta s1 selecciona las claves de todos los conjuntos que contienen elementos distintos a los que estamos buscando. Así, cuando salimos de unir sets con s1 , s1.name es NULL cuando el conjunto solo contiene elementos que estamos buscando. Luego agrupamos por clave de conjunto y filtramos cualquier conjunto que tenga el número incorrecto de elementos. Entonces nos quedan solo conjuntos que contienen solo los elementos que estamos buscando y tienen la longitud correcta. Dado que los conjuntos solo pueden contener un elemento una vez, solo puede haber un conjunto que satisfaga ese criterio, y ese es el que estamos buscando.

Editar: Me acabo de dar cuenta de cómo hacer esto sin la exclusión.

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;

La primera subconsulta encuentra el recuento total de elementos en cada conjunto y la segunda encuentra el recuento de elementos coincidentes en cada conjunto. Cuando matches.count es 3, el conjunto tiene todos los elementos que estamos buscando, y si totals.count también es 3, el conjunto no tiene elementos adicionales.