SQL – WHERE NOT EXISTS – est ce qu’une salle est disponible à la réservation ?
Introduction
Nous avons deux tables :
- une qui contient une liste de salle de réunion
- une autre qui contient la liste de toutes les réservations
- elles sont reliées via un clé étrangère
La question est de savoir quelles salles de réunion sont libres à une date donnée ??? De préférence avec une seule requête SQL … Sans avoir à faire des boucles en PHP qui contiennent des requêtes SQL !

Pour réaliser cette opération nous allons utiliser plusieurs concepts :
- deux requêtes :
- liste de toutes les salles ( requête principale )
- liste de toutes les salles réservées pour une date donnée ( sous requête )
- ces deux requêtes vont être imbriquées :
- la requête principale dispose d’une clause WHERE
- la sous-requête va alimenter la clause WHERE de la requête principale
- ceux deux requêtes vont être corrélées :
- définition : une sous-requête corrélée est une sous-requête qui s’exécute pour chaque ligne de la requête principale et non une fois pour toute
- la sous requête va être exécutée pour chaque valeur de la requête principale
- Donc 1 : la sous requête va utiliser une variable de requête principale
- Donc 2 : cette variable égalise une variable de la sous requête
- utilisation de l’opérateur EXISTS dans la requête principale
- L’opérateur EXISTS est toujours associé à la clause WHERE et à une sous-requête
- retourne TRUE si la sous requête renvoie une valeur
- retourne FALSE si la sous requête aucune valeur
Pour rappel, le requête suivante ne renvoie rien :
SELECT * FROM table WHERE FALSE ;
La requête suivante est équivalente (si on considère qu’il n’y a aucun enregistrement ayant l’id 10 dans la tablea loc_agences ):
SELECT * FROM `loc_agences` WHERE EXISTS
(SELECT * FROM `loc_agences` WHERE id = 10);
Tous les exemples ci dessous ont été réalisés sur MySQL
Structure et Jeu de données
--
-- Structure de la table `sallea_reservation_salle`
--
CREATE TABLE `sallea_reservation_salle` (
`id` int(10) UNSIGNED NOT NULL,
`salle_id` int(10) UNSIGNED NOT NULL,
`date` date NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Déchargement des données de la table `sallea_reservation_salle`
--
INSERT INTO `sallea_reservation_salle` (`id`, `salle_id`, `date`) VALUES
(1, 1, '2019-06-04'),
(2, 2, '2019-06-04'),
(3, 3, '2019-06-05'),
(4, 2, '2019-06-07');
-- --------------------------------------------------------
--
-- Structure de la table `sallea_salle`
--
CREATE TABLE `sallea_salle` (
`id` int(10) UNSIGNED NOT NULL,
`nom` varchar(60) NOT NULL,
`ville` enum('Paris','Lyon') NOT NULL,
`capacite` int(10) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Liste des salles de réunion à réservable';
--
-- Déchargement des données de la table `sallea_salle`
--
INSERT INTO `sallea_salle` (`id`, `nom`, `ville`, `capacite`) VALUES
(1, 'Salle 1 Paris', 'Paris', 6),
(2, 'Salle 2 Paris', 'Paris', 10),
(3, 'Salle 1 Lyon', 'Lyon', 10);
--
-- Index pour les tables déchargées
--
--
-- Index pour la table `sallea_reservation_salle`
--
ALTER TABLE `sallea_reservation_salle`
ADD PRIMARY KEY (`id`),
ADD KEY `salle_id` (`salle_id`);
--
-- Index pour la table `sallea_salle`
--
ALTER TABLE `sallea_salle`
ADD PRIMARY KEY (`id`);
--
-- AUTO_INCREMENT pour les tables déchargées
--
--
-- AUTO_INCREMENT pour la table `sallea_reservation_salle`
--
ALTER TABLE `sallea_reservation_salle`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
--
-- AUTO_INCREMENT pour la table `sallea_salle`
--
ALTER TABLE `sallea_salle`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
--
-- Contraintes pour les tables déchargées
--
--
-- Contraintes pour la table `sallea_reservation_salle`
--
ALTER TABLE `sallea_reservation_salle`
ADD CONSTRAINT `sallea_reservation_salle_ibfk_1` FOREIGN KEY (`salle_id`) REFERENCES `sallea_salle` (`id`);
COMMIT;
Jeu de données sous forme graphique
Table sallea_salle
| id | nom | ville | capacite |
|---|---|---|---|
| 1 | Salle 1 Paris | Paris |
6 |
| 2 | Salle 1 Paris | Paris | 10 |
| 3 | Salle 1 Lyon | Lyon | 10 |
Table sallea_reservation_salle
| id | salle_id | date |
|---|---|---|
| 1 | 1 | 2019-06-04 |
| 2 | 2 | 2019-06-04 |
| 3 | 3 | 2019-06-05 |
| 4 | 2 | 2019-06-07 |
Requête SQL
Voici la requête à effectuer :
SET @dtresa = "2019-06-04" ; SELECT s.nom FROM `sallea_salle` as s WHERE NOT EXISTS (SELECT * FROM sallea_reservation_salle as r WHERE 1 = 1 AND r.salle_id = s.id AND r.date = @dtresa)
Quelques remarques :
- La requête va retourner
Salle 1 Lyoncar effectivement le 2019-06-04 c’est la seule salle qui n’est pas réservée : qui n’a pas d’enregistrement dans la table sallea_reservation_salle - La sous requête est bien corrélée avec la requête principale grâce à l’égalité :
r.salle_id = s.id - J’ai utilisé une variable SQL,
SET @dtresa = "2019-06-04"pour facilité les tests sur d’autres dates