Il n'existe pas en SQL d'équivalent direct à la division.
Cependant il est toujours possible de trouver une autre solution, notamment par l'intermédiaire des opérations de calcul et de regroupement.
Dans l'exemple présenté, on souhaite trouver les athlètes qui participent à toutes les épreuves. En algèbre relationnelle une autre solution que la division pourrait être :
N=CALCULER(EPREUVE, Comptage())
R1=REGROUPER_ET_CALCULER(PARTICIPER, Athlète, Nb:Comptage())
R2=SELECTION(R1, Nb=N)
R3=PROJECTION(R2, Athlète)
et en SQL :
SELECT Athlète FROM PARTICIPER
GROUP BY Athlète
HAVING COUNT(*) = (SELECT COUNT(*) FROM EPREUVE) ;
On pourra trouver cette solution imparfaite par rapport aux solutions plus "propres" généralement données pour la division, solutions souvent basées sur une double négation et mettant en oeuvre plusieurs SELECT imbriqués et corrélés (très coûteux en temps d'exécution). Approfondissons les choses avec un autre jeu d'essai volontairement plus contraignant :
PARTICIPER | EPREUVE | ||
Athlète | Epreuve | Epreuve | |
---|---|---|---|
Dupont | 200 m | 200 m | |
Dupont | 400 m | 400 m | |
Dupont | 110 m H | 110 m H | |
Dupont | 100 m | 200 m | |
Martin | 200 m | ||
Martin | 400 m | ||
Martin | 110 m H | ||
Bertrand | 200 m | ||
Bertrand | 400 m | ||
Michel | 200 m |
On peut alors
vouloir obtenir :
1) les athlètes ayant participé au moins à toutes les épreuves de la table
EPREUVE (Dupont et Martin)
2) les athlètes qui ont participé uniquement aux épreuves de la table EPREUVE
et à aucune autre (Division "exacte"), ici Martin.
Dans le 1er cas, la solution n'est guère plus compliquée :
SELECT Athlète FROM PARTICIPER
WHERE Epreuve IN (SELECT Epreuve FROM EPREUVE)
GROUP BY Athlète
HAVING COUNT(*) = (SELECT COUNT(DISTINCT Epreuve) FROM EPREUVE) ;
Dans le 2ème cas, il est encore possible de répondre assez "simplement" à la question sur la base d'une jointure externe (SQL2) et en utilisant la particularité des fonctions d'agrégation d'ignorer les valeurs nulles. Voici cette solution :
SELECT Athlète
FROM PARTICIPER A LEFT JOIN (SELECT DISTINCT Epreuve FROM EPREUVE) B ON A.Epreuve = B.Epreuve
GROUP BY Athlète
HAVING COUNT(*) = (SELECT COUNT(DISTINCT Epreuve) FROM EPREUVE)
AND COUNT(B.Epreuve)
=(SELECT COUNT(DISTINCT Epreuve) FROM EPREUVE) ;
Pour mieux comprendre, voici le résultat renvoyé par la jointure externe gauche (LEFT JOIN) entre PARTICIPER et EPREUVE :
Athlète | A.Epreuve | B.Epreuve |
---|---|---|
Dupont | 200 m | 200 m |
Dupont | 400 m | 400 m |
Dupont | 110 m H | 110 m H |
Dupont | 100 m | NULL |
Martin | 200 m | 200 m |
Martin | 400 m | 400 m |
Martin | 110 m H | 110 m H |
Bertrand | 400 m | 400 m |
Bertrand | 200 m | 200 m |
Michel | 200 m | 200 m |
Ensuite le regroupement avec comptage renvoie :
Athlète | COUNT(*) | COUNT(B.Epreuve) |
---|---|---|
Dupont | 4 | 3 |
Martin | 3 | 3 |
Bertrand | 2 | 2 |
Michel | 1 | 1 |
Seul Martin a les 2 résultats égaux au nombre d'épreuves différentes (et vérifie donc la condition exprimée derrière le HAVING). CQFD !
Je vous laisse comparer avec l'autre type de solution (plus élégante) basée sur la double négation et les SELECT imbriqués et corrélés :
SELECT Athlète FROM PARTICIPER A
WHERE NOT EXISTS (SELECT * FROM EPREUVE
WHERE NOT EXISTS (SELECT * FROM PARTICIPER B
WHERE (A.Athlète = B.Athlète)
AND (B.Epreuve = EPREUVE.Epreuve)))
GROUP BY Athlète
HAVING COUNT(*) = (SELECT COUNT (DISTINCT Epreuve) FROM EPREUVE) ;