Mysql
 sql >> Base de données >  >> RDS >> Mysql

interroger un ensemble dans une base de données relationnelle

Je ne commenterai pas s'il existe un schéma mieux adapté pour cela (c'est tout à fait possible), mais pour un schéma ayant des colonnes name et item , la requête suivante devrait fonctionner. (syntaxe 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;

L'idée est que nous avons toutes les clés définies dans k , que nous joignons ensuite aux données de l'élément défini dans sets une fois pour chaque élément de l'ensemble que nous recherchons, trois dans ce cas. Chacune des trois jointures internes avec des alias de table i1 , i2 et i3 filtre tous les noms d'ensembles qui ne contiennent pas l'élément recherché avec cette jointure. Enfin, nous avons une jointure gauche avec sets avec l'alias de table ix , qui apporte tous les éléments supplémentaires de l'ensemble, c'est-à-dire tous les éléments que nous ne recherchions pas. ix.name est NULL dans le cas où aucun élément supplémentaire n'est trouvé, ce qui est exactement ce que nous voulons, donc le WHERE clause. La requête renvoie une ligne contenant la clé de l'ensemble si l'ensemble est trouvé, aucune ligne sinon.

Modifier : L'idée derrière la réponse collapsars semble être bien meilleure que la mienne, alors voici une version un peu plus courte de celle-ci avec une explication.

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;

L'idée ici est que la sous-requête s1 sélectionne les clés de tous les ensembles qui contiennent des éléments autres que ceux que nous recherchons. Ainsi, lorsque nous sommes partis rejoindre sets avec s1 , s1.name est NULL lorsque l'ensemble ne contient que les éléments que nous recherchons. Nous regroupons ensuite par clé d'ensemble et filtrons tous les ensembles ayant le mauvais nombre d'éléments. Il nous reste alors uniquement des ensembles qui ne contiennent que les éléments que nous recherchons et qui sont de la bonne longueur. Étant donné que les ensembles ne peuvent contenir un élément qu'une seule fois, il ne peut y avoir qu'un seul ensemble répondant à ce critère, et c'est celui que nous recherchons.

Modifier : Je viens de comprendre comment faire cela sans l'exclusion.

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 première sous-requête trouve le nombre total d'éléments dans chaque ensemble et la seconde trouve le nombre d'éléments correspondants dans chaque ensemble. Quand matches.count est 3, l'ensemble contient tous les éléments que nous recherchons, et si totals.count est également 3, l'ensemble ne contient aucun élément supplémentaire.