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.
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
)
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
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 |
+-------------+