web-dev-qa-db-fra.com

Sélectionnez un groupe de lignes qui correspondent à tous les éléments d'une liste

Supposons que j'ai deux tables:

cars - liste des voitures

carname | modelnumber | ...

passedtest - contient tous les tests réussis par une voiture:

id | carname | testtype | date | ...
1  | carA    | A        | 2000 |
2  | carB    | C        | 2000 |
3  | carC    | D        | 2001 |
4  | carA    | C        | 2002 |

Maintenant, comment puis-je sélectionner une voiture dans la table passedtest qui a réussi tous les tests (A, B, C, D)?

J'ai essayé l'instruction IN mais elle correspond également aux voitures qui réussissent même un test. Je recherche une déclaration pour faire correspondre les valeurs all dans une liste sur toutes les lignes.

33
user1229351

Que dis-tu de ça?

SELECT carname
FROM PassedTest
GROUP BY carname
HAVING COUNT(DISTINCT testtype) = 4

Vous pouvez également l'utiliser comme une instruction interne pour extraire des informations de la table cars:

SELECT *
FROM cars
WHERE carname IN (
    SELECT carname
    FROM PassedTest
    GROUP BY carname
    HAVING COUNT(DISTINCT testtype) = 4
)
40
MarcinJuraszek

Ce type de problème est appelé Relational Division.

SELECT  a.*
FROM    Cars a
        INNER JOIN
        (
            SELECT  CarName
            FROM    PassedTest 
            WHERE   testType IN ('A', 'B', 'C', 'D')
            GROUP   BY CarName
            HAVING  COUNT(*) = 4
        ) b ON a.CarName = b.CarName

si une contrainte UNIQUE n'était pas appliquée sur TestType pour chaque CarName sur la table PassedTest un mot clé DISTINCT est requis sur la fonction COUNT() donc il ne comptera que les valeurs uniques.

SELECT  a.*
FROM    Cars a
        INNER JOIN
        (
            SELECT  CarName
            FROM    PassedTest 
            WHERE   testType IN ('A', 'B', 'C', 'D')
            GROUP   BY CarName
            HAVING  COUNT(DISTINCT TestType) = 4
        ) b ON a.CarName = b.CarName

mais si vous n'êtes intéressé que par le CARNAME, vous n'avez pas besoin de rejoindre les tables. L'interrogation sur la table PassedTest conviendra à vos besoins.

SELECT  CarName
FROM    PassedTest 
WHERE   testType IN ('A', 'B', 'C', 'D')
GROUP   BY CarName
HAVING  COUNT(*) = 4
32
John Woo

Vous souhaitez effectuer une division relationnelle, une opération qui n'est pas implémentée dans SQL. Voici un exemple où nous avons une table produit-fournisseur et une table produit-requis:

CREATE TABLE product_supplier (
    product_id int NOT NULL,
    supplier_id int NOT NULL,
    UNIQUE (product_id, supplier_id)
);
INSERT INTO product_supplier (product_id, supplier_id) VALUES
(1, 1),
(2, 1),
(3, 1),
(1, 2),
(2, 2),
(3, 2),
(4, 2),
(2, 3),
(3, 3),
(4, 3);

CREATE TABLE reqd (
    product_id int NOT NULL,
    UNIQUE (product_id)
);
INSERT INTO reqd (product_id) VALUES
(1),
(2),
(3);

... et nous voulons trouver tous les fournisseurs qui fournissent TOUS les produits requis et peut-être d'autres. Le résultat dans l'exemple ci-dessus serait fournisseur 1 et 2.

La solution la plus simple est la suivante:

SELECT product_supplier.supplier_id
FROM product_supplier
LEFT JOIN reqd ON product_supplier.product_id = reqd.product_id
GROUP BY product_supplier.supplier_id
HAVING COUNT(reqd.product_id) = (SELECT COUNT(*) FROM reqd);
+-------------+
| supplier_id |
+-------------+
|           1 |
|           2 |
+-------------+

Et si nous voulons trouver tous les fournisseurs qui fournissent TOUS les produits requis et aucun autre (division exacte/pas de reste), ajoutez une condition supplémentaire à ce qui précède:

SELECT product_supplier.supplier_id
FROM product_supplier
LEFT JOIN reqd ON product_supplier.product_id = reqd.product_id
GROUP BY product_supplier.supplier_id
HAVING COUNT(reqd.product_id) = (SELECT COUNT(*) FROM reqd)
AND COUNT(product_supplier.product_id) = (SELECT COUNT(*) FROM reqd);
+-------------+
| supplier_id |
+-------------+
|           1 |
+-------------+

Une autre solution consiste à reformuler le problème: sélectionner les fournisseurs où il n’existe pas de produit requis qui n’existe pas dans les produits fournis par le fournisseur. Hmmm:

SELECT DISTINCT supplier_id
FROM product_supplier AS ps1
WHERE NOT EXISTS (
    SELECT *
    FROM reqd
    WHERE NOT EXISTS (
        SELECT *
        FROM product_supplier AS ps2
        WHERE ps1.supplier_id = ps2.supplier_id AND ps2.product_id = reqd.product_id
    )
);
+-------------+
| supplier_id |
+-------------+
|           1 |
|           2 |
+-------------+
3
Salman A