UNION
et allons apprendre à l’utiliser pour combiner les résultats de deux déclarations SELECT
ou pour simuler le comportement d’un FULL OUTER JOIN
.
Conditions d’utilisation de l’opérateur SQL UNION
Nous allons utiliser l’opérateur SQL UNION
pour combiner les résultats de deux déclarations SELECT
.
Afin que cet opérateur fonctionne correctement et que les résultats puissent bien être combinés, il va falloir respecter les règles suivantes :
- Chacun des
SELECT
dans unUNION
doit posséder le même nombre de colonnes ; - Les colonnes doivent posséder le même type de données une à une dans chaque
SELECT
(la première colonne du premierSELECT
doit posséder des données de même type que la première colonne du deuxièmeSELECT
et etc.).
Notez bien que les colonnes sélectionnées peuvent avoir un nom différent entre les différents SELECT
du moment que le type de données est le même. En cas de nom différent, et si nous voulons harmoniser les noms, nous pouvons tout à fait utiliser un alias.
Par défaut, l’opérateur SQL UNION
ne va pas sélectionner les doublons dans les valeurs (les valeurs qui sont trouvées plusieurs fois n’apparaitront qu’une seule fois).
Nous allons par exemple pouvoir sélectionner la liste des noms et prénoms dans nos tables « users » et « employes » créées précédemment :
<!DOCTYPE html> <html> <head> <title>Cours PHP / MySQL</title> <meta charset='utf-8'> </head> <body> <h1>Bases de données MySQL</h1> <?php $servname = "localhost"; $dbname = "cours"; $user = "root"; $pass = "root"; try{ $dbco = new PDO("mysql:host=$servname;dbname=$dbname", $user, $pass); $dbco->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); /*L'opérateur SQL UNION*/ $sth = $dbco->prepare(" SELECT nom, prenom FROM employes UNION SELECT nom, prenom FROM users "); $sth-> execute(); $resultat = $sth->fetchAll(PDO::FETCH_ASSOC); echo '<pre>'; print_r($resultat); echo '</pre>'; } catch(PDOException $e){ echo "Erreur : " . $e->getMessage(); } ?> </body> </html>
Comme vous pouvez le voir, les valeurs dupliquées n’apparaissent qu’une fois. Notez bien qu’ici on sélectionne les noms et prénom dans chaque table qui vont représenter une entité et c’est bien sur cette entité nom + prénom que va être évaluée la duplication.
C’est pour cela qu’on a deux fois le prénom « Pierre » qui s’affiche, tout simplement car nos « Pierre » n’ont pas le même nom.
Si nous n’avions sélectionnés que les prénoms, le prénom « Pierre » ne se serait affiché qu’une fois :
<!DOCTYPE html> <html> <head> <title>Cours PHP / MySQL</title> <meta charset='utf-8'> </head> <body> <h1>Bases de données MySQL</h1> <?php $servname = "localhost"; $dbname = "cours"; $user = "root"; $pass = "root"; try{ $dbco = new PDO("mysql:host=$servname;dbname=$dbname", $user, $pass); $dbco->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); /*L'opérateur SQL UNION*/ $sth = $dbco->prepare(" SELECT prenom FROM employes UNION SELECT prenom FROM users "); $sth-> execute(); $resultat = $sth->fetchAll(PDO::FETCH_ASSOC); echo '<pre>'; print_r($resultat); echo '</pre>'; } catch(PDOException $e){ echo "Erreur : " . $e->getMessage(); } ?> </body> </html>
Si vous souhaitez obtenir toutes les valeurs (incluant les valeurs en double et etc.) alors il faudra utiliser UNION ALL
.
<!DOCTYPE html> <html> <head> <title>Cours PHP / MySQL</title> <meta charset='utf-8'> </head> <body> <h1>Bases de données MySQL</h1> <?php $servname = "localhost"; $dbname = "cours"; $user = "root"; $pass = "root"; try{ $dbco = new PDO("mysql:host=$servname;dbname=$dbname", $user, $pass); $dbco->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); /*UNION ALL*/ $sth = $dbco->prepare(" SELECT prenom, nom FROM employes UNION ALL SELECT prenom, nom FROM users "); $sth-> execute(); $resultat = $sth->fetchAll(PDO::FETCH_ASSOC); echo '<pre>'; print_r($resultat); echo '</pre>'; } catch(PDOException $e){ echo "Erreur : " . $e->getMessage(); } ?> </body> </html>
Utiliser l’opérateur UNION pour simuler un FULL OUTER JOIN en MySQL
Rappel : un FULL OUTER JOIN
est un type de jointures externes qui va récupérer toutes les données pour les colonnes sélectionnées de chacune des deux tables.
Le mot clef FULL OUTER JOIN
n’est pas supporté en tant que tel par MySQL. Nous allons cependant pouvoir simuler un FULL OUTER JOIN
en utilisant un LEFT JOIN
et un RIGHT JOIN
avec une clause WHERE
pour ne pas avoir les données présentes dans les deux tables en double dans notre résultat final et en combinant les résultats obtenus avec un UNION
.
Pour rappel, un LEFT JOIN
va nous permettre de récupérer toutes les données de notre table de gauche ainsi que les données de la table de droite qui satisfont à la condition dans la jointure.
Un RIGHT JOIN
va lui nous permettre de récupérer toutes les données de notre table de droite ainsi que les données de la table de gauche qui satisfont à la condition dans la jointure.
Ainsi, en s’arrêtant là, nous allons avoir les données de la table de droite qui satisfont à la condition de notre LEFT JOIN
et les données de la table de gauche qui satisfont à la condition de notre RIGHT JOIN
en double (puisque notre RIGHT JOIN
récupère déjà toutes les données de la table de droite et notre LEFT JOIN
récupère déjà toutes les données de la table de gauche).
Ainsi, nous allons utiliser une clause WHERE
soit dans notre LEFT JOIN
soit dans notre RIGHT JOIN
pour ne pas sélectionner plus de données que ce qu’on doit.
Attention, on ne parle pas ici de supprimer les résultats dupliqués, on veut seulement tenter de simuler le résultat d’un FULL OUTER JOIN
et pour cela il ne faut pas aller sélectionner deux fois les mêmes résultats. Il est possible que l’on ait des valeurs dupliquées au final s’il y en a dans nos colonnes à la base.
Essayons de simuler le comportement d’un FULL OUTER JOIN
qui sélectionnerait tous les prénoms et noms de notre table « users » et tous les contenus et les dates des commentaires de notre tables « comments » :
<!DOCTYPE html> <html> <head> <title>Cours PHP / MySQL</title> <meta charset='utf-8'> </head> <body> <h1>Bases de données MySQL</h1> <?php $servname = "localhost"; $dbname = "cours"; $user = "root"; $pass = "root"; try{ $dbco = new PDO("mysql:host=$servname;dbname=$dbname", $user, $pass); $dbco->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //Simulation d'un FULL OUTER JOIN $sth = $dbco->prepare(" SELECT u.prenom, u.nom, c.contenu, c.dateComment FROM users AS u LEFT JOIN comments AS c ON u.id = c.userId UNION ALL SELECT u.prenom, u.nom, c.contenu, c.dateComment FROM users AS u RIGHT JOIN comments AS c ON u.id = c.userId WHERE u.id IS NULL "); $sth-> execute(); $resultat = $sth->fetchAll(PDO::FETCH_ASSOC); echo '<pre>'; print_r($resultat); echo '</pre>'; } catch(PDOException $e){ echo "Erreur : " . $e->getMessage(); } ?> </body> </html>
Ici, le premier SELECT
sélectionne tous les prénoms et noms de notre table « users » ainsi que les contenus et dates de nos commentaires liés à un id utilisateur.
Le deuxième SELECT
sélectionne lui uniquement les contenus et dates des commentaires qui n’ont pas d’utilisateur associé (WHERE u.id IS NULL
).
En unissant ces deux SELECT
avec un UNION ALL
, cela nous permet donc d’obtenir tous les noms et prénoms de nos utilisateurs et tous les contenus et dates des commentaires.