Skip to content

1 4 3 3 quantificateurs

SQL, langage déclaratif. ¾ : quantificateurs et négation

Supports complémentaires :

Jusqu'à présent les seules variables que nous utilisons sont des variables libres de la formule, définies dans la clause from de la syntaxe SQL. Nous n'avons pas encore rencontré de variable liée parce que nous n'avons pas utilisé les quantificateurs.

SQL propose uniquement le quantificateur existentiel. Le quantificateur universel peut être obtenu en le combinant avec la négation. Rappelons que les quantificateurs servent à exprimer des conditions sur l'ensemble d'une relation (qui peut être une relation en base, ou une relation calculée). Ils sont particulièrement utiles pour les requêtes qui comportent des négations ("je ne veux pas des objets qui ont telle ou telle propriété dans mon résultat").

Le quantificateur exists

Reprenons simplement la requête qui demande les logements où l'on peut faire du ski. La formule donnée précédemment est la suivante :

\[\{ l.nom | Logement(l) \land Activité(a) \land l.code = a.codeLogement \land a.codeActivité=\text{'Ski'} \}\]

On remarque que la variable libre \(a\) n'est pas utilisée dans la construction du nuplet-résultat (qui ne contient que l.nom). On pourrait donc affecter le nuplet a à une variable liée, ce qui revient à formuler la requête légèrement différemment : "donnez-moi le nom des logements pour lesquels il existe une activité Ski".

Ce qui donne la formule suivante :

\[\{ l.nom | Logement(l) \land \exists a (Activité(a) \land l.code = a.codeLogement \land a.codeActivité=\text{'Ski'})\}\]

On a introduit la sous-formule suivante :

\[\exists a (Activité(a) \land l.code = a.codeLogement \land a.codeActivité=\text{'Ski'})\]

Cette sous-formule est satisfaite dès que l'on a trouvé au moins un nuplet qui satisfait les conditions demandées, à savoir un code activité égal à Ski, et le même code logement que celui de la variable \(l\).

Qui dit sous-formule dit logiquement sous-requête en SQL. Voici la syntaxe :

select distinct l.nom
from Logement as l
where exists (select ''
              from Activité as a
              where l.code = a.codeLogement
              and a.codeActivité = 'Ski')

Le résultat est construit à partir du select de premier niveau, qui ne peut accéder qu'à la variable l, et pas à la variable (liée) a.

Note
La clause du select imbriquée ne sert donc absolument à rien d'autre qu'à respecter la syntaxe SQL, et on peut utiliser select '', select * ou n'importe quoi d'autre.

Cet exemple montre qu'il est possible d'exprimer une même requête avec des syntaxes différentes, que ce soit au niveau de la formulation en langage naturel ou de l'expression formelle (logique ou SQL).

Les quantificateurs permettent d'imbriquer des formules dans des formules, sans limitation de profondeur. En SQL, on peut de même avoir des imbrications de requêtes sans limitation. La lisibilité et la compréhension en sont quand même affectées.

Prenons une requête un peu plus complexe : je veux les noms des voyageurs qui sont allés dans les Alpes. Une première formulation, complètement "à plat" est la suivante :

select distinct v.prénom, v.nom
from Voyageur as v, Séjour as s, Logement as l
where v. idVoyageur=s.idVoyageur
and   s.codeLogement = l .code
and   l.lieu = 'Alpes'

Ni la variable s, ni la variable l ne sont utilisées pour construire le nuplet-résultat. On peut donc l'exprimer ainsi : "je veux les noms des voyageurs pour lesquels il existe un séjour dans les Alpes". Ce qui donne :

select distinct v.prénom, v.nom
from Voyageur as v
where exists (select '' 
              from Séjour as s, Logement as l
              where v. idVoyageur=s.idVoyageur
              and   s.codeLogement = l .code
              and   l.lieu = 'Alpes')

On pourrait même aller encore plus loin dans l'imbrication avec la requête suivante :

select distinct v.prénom, v.nom
from Voyageur as v
where exists (select '' 
              from Séjour as s
              where v. idVoyageur=s.idVoyageur
              and exists (select '' 
                          from  Logement as l
                           where s.codeLogement = l .code
                           and   l.lieu = 'Alpes')
               )

La troisième version correspond à la formulation "Les voyageurs tels qu'il existe un de leurs séjours tels que le logement existe dans les Alpes". Elle n'est pas très naturelle, et, de plus, probablement la plus difficile à comprendre, ce qui ne plaide pas en sa faveur.

Quantificateurs et négation

Il nous reste à découvrir les requêtes probablement les plus complexes, celle où l'on exprime une négation. Voici un premier exemple : on veut les logements qui ne proposent pas de ski. En reprenant la requête "positive" étudiée précédemment, il suffit d'ajouter une négation devant le quantificateur existentiel.

\[\{ l.nom | Logement(l) \land \not \exists a (Activité(a) \land l.code = a.codeLogement \land a.codeActivité=\text{'Ski'})\}\]

On a donc formulé la requête en termes logiques : "je veux les logements tels qu'il n'existe pas d'activité Ski". Voici la requête SQL.

select distinct l.nom
from Logement as l
where not exists (select ''
                 from Activité as a
                 where l.code = a.codeLogement
                 and a.codeActivité = 'Ski')

C'est la seule manière de l'exprimer correctement. Elle donne le résultat suivant :

nom
Causses
U Pinzutu
Tabriz

Vous devriez ête convaincus que la requête suivante est très différente (et ne correspond pas à ce que l'on souhaite). L'opérateur != signifie différent de en SQL.

select l.nom
from Logement as l
where  exists (select ''
                 from Activité as a
                 where l.code = a.codeLogement
                 and a.codeActivité != 'Ski')

Dont le résultat est :

nom
Causses
Génépi
U Pinzutu

Réfléchissez au sens de cette requête, trouvez le résultat sur notre petite base. Rappelez-vous que les quantificateurs servent à exprimer une condition sur un ensemble de nuplets, pas sur chaque nuplet en particulier.

Le not exists est la porte d'entrée pour exprimer le quantificateur universel. Supposons que l'on cherche les voyageurs qui sont allés dans tous les logements. On reformule cette requête avec deux négations : on cherche les voyageurs tels qu'il n'existe pas de logement où ils ne sont pas allés.

select distinct v.prénom, v.nom
from Voyageur as v
where  not exists (select ''
                   from Logement as l
                   where not exists  (select ''
                                      from Séjour as s
                                      where l.code = s.codeLogement
                                      and   v.idVoyageur = s.idVoyageur)
                   )

Vous devriez obtenir :

prénom nom
Nicolas Bouvier

Vous savez maintenant tout sur la version déclarative de SQL, qui n'est rien d'autre qu'une syntaxe concrète pour exprimer des formules ouvertes sur une base de données. Tout ce qui peut s'exprimer par une formule logique est exprimable en SQL. Ni plus, ni moins. Inversement, tout ce qui ne s'exprime pas par une formule (boucles, incrémentations, etc.) ne s'exprime pas en SQL.

Dans le prochain chapitre, nous verrons la version procédurale, mais il est important de préciser qu'elle n'apporte rien en terme de possibilités d'expression. En d'autres termes, vous avez déjà, avec ce que nous venons d'étudier, la capacité d'exprimer toutes les requêtes possibles (à l'exception des agrégations). La version procédurale n'est qu'une manière alternative de concevoir l'interrogation d'une base relationnelle.

Prenez le temps de bien maîtriser ce qui précède, car la compréhension du sens de ce que l'on exprime avec les formules de logique des prédicats est la condition nécessaire et suffisante pour utiliser correctement SQL.