Chapitre 4 : Les langages
relationnels
Les opérateurs algébriques,
qui effectuent la manipulation interne des relations dans un SGBD
relationnel, constitue un premier langage de requêtes. Ils présentent
cependant l'inconvénient majeur d'exprimer les manipulations à
effectuer de façon procédurale. En effet, la spécification
directe par l'utilisateur d'une séquence d'opérations relationnelles
correspond au choix d'une exécution particulière ; plusieurs exécutions
sont possibles pour une même question. L'utilisateur à qui l'on
offre un langage algébrique a donc plusieurs choix pour formuler sa question
: certains sont performants, d'autres induisent des temps de réponses
impressionnants, car ils génèrent un volume considérable
de données intermédiaires et de très nombreux accès.
Les concepteurs de systèmes
ont donc cherché à proposer un langage de manipulation de données
qui ne demande précise pas l'ordre des opérations. Un tel langage
est donc non-procédural. Pour cela, la complétude de l'algèbre
relationnelle est exploitée, c'est-à-dire qu'elle est équivalente
à la logique des prédicats du premier ordre (sans fonctions).
Il s'agit d'un résultat important que nous admettrons simplement ici.
Une requête s'exprime donc comme une assertion sur la base de données.
Le critère logique que doivent vérifier les tuples du résultat
est spécifié ; cette assertion décrit une caractéristique
du résultat, indépendamment de la façon de l'obtenir. C'est
le système qui, à partir de l'expression d'une assertion, génère
une exécution particulière, c'est-à-dire une certaine séquence
d'opérateurs relationnels.
La première tentative pour
construire un véritable langage de requêtes, transformable en séquence
d'opérations relationnelles, date de 76 : le langage SEQUEL
du laboratoire IBM de San José. C'est lui qui donnera
plus tard naissance à SQL (Structured Query Language).
IV.1. Le standard SQL
Hier norme de fait, SQL
est devenu une norme de droit quand l'International Organization for Standardization
l'a pris en compte en 86 [ISO 86]. SQL
est arrivé à maturité. Sa version normalisée par l'ISO
fige ses différents aspects : définition des concepts, du vocabulaire,
du langage de définition de données (déclaration du schéma,
des vues, du contrôle des autorisations) et du langage de manipulation sont
désormais stabilisés. Les procédures d'intégration
des appels SQL dans un langage hôte (de type Cobol,
Pascal, C, Fortran�), regroupées sous le nom de "Embedded SQL",
souvent noté ESQL, sont également définies. Presque tous
les constructeurs proposent le langage SQL. Plus de 70 produits
du marché le fournissent, sur des machines qui vont du PC
au plus importants mainframes. Celui-ci étend même sa percée
vers des produits micros qui, sans devenir de véritables SGBD
relationnels complets, intègrent déjà la partie de SQL
qui concerne la manipulation des données.
SQL est un facteur
important de promotion du modèle relationnel. Il apporte une vision unifiée
et une large communauté de concepts et de vocabulaire. Reposant sur une
base théorique solide (la logique des prédicats), il concerne
à la fois les administrateurs, les programmeurs d'applications, et les
utilisateurs finals pour la définition et la manipulation des données.
IV.1.1. Eléments d'un
langage pour les SGBD relationnels
La norme SQL comporte
trois parties distinctes concernant :
- la manipulation de l'information
; le (DML : Data Manipulation Language) concerne l'ensemble
des requêtes de recherche et de mise à jour des informations
: selection, insertion, modification et suppression. Il est essentiellement
destiné à l'utilisateur final ;
- la définition des structures
de données ; le DDL (Data Definition Language)
contient l'ensemble des commandes de création, de suppression, de
modification des relations. Elle intègre la créations, la
suppressions de chemins d'accès (index) et les commandes concernant
la gestion des droits et des autorisations. Elle est largement destinée
à l'administrateur ;
- l'accès aux informations
stockées dans le SGBD depuis un langage de programmation
; l' Embedded SQL définit l'ensemble des interfaces, l'utilisation
des ordres SQL dans un langage de programmation, ainsi
que l'ensemble des codes d'erreur renvoyés par le SGBD
au programme d'application défini. L' Embedded SQL
est destiné au programmeur d'applications.
La liste des avantages et inconvénients
de la norme SQL telle qu'elle se présente aujourd'hui
peut être rapidement dressée : à son actif, citons sa simplicité,
sa souplesse et ses fonctionnalités étendues ; à son passif,
un manque de rigueur (il autorise - et encourage - certaines formulations procédurales),
une utilisation lourde (mot clés, pas d'utilisation de moyens modernes
d'interface homme/machine), le fait qu'il constitue, comme toute norme, un frein
à la créativité des concepteurs de nouveaux systèmes.
Rappelons enfin qu'il s'agit d'un langage inventé par IBM,
argument stratégique délicat à placer dans les qualités
ou les défauts du langage.
La normalisation de SQL, dont la
première version a été achevée en 86, conduit également
à se poser la question "à qui peut servir la normalisation ?".
En effet, si l'on examine les différentes parties de la norme, on constate
que l'administrateur doit disposer d'outils moins primitifs et plus spécifiques
pour gérer convenablement son SGBD ; l'utilisateur
final a bien du mal à utiliser directement SQL en
interactif : il utilise des interfaces spécifiques (menus, écrans�)
plus conviviales ; enfin le programmeur d'applications est aujourd'hui largement
sollicité par les Langages de quatrième Génération
(L4G) qui, sans être normalisés, sont plus
pratiques et procurent les mêmes services de base que du Embedded SQL.
Certaines mauvaises langues en concluent donc que la normalisation ne sert qu'aux
normalisateurs� L'apport de la normalisation du SQL est cependant indiscutable
pour les utilisateurs désirant garantir la portabilité de leurs
applications d'un système SQL sur un autre. C'est
l'interface standard de tout véritable SGBD relationnel.
Il précise, d'autre part, un niveau d'interface précis lorsqu'on
envisage des systèmes répartis et hétérogènes.
Comme pour les opérateurs
de l'algèbre relationnelle, nous illustrerons l'utilisation de SQL
sur la base-jouet suivante :
PLAGE (NP, NOMP,
TYPE, REGION, POLLUTION)
NAGEUR (NN,
NOM, PRENOM, QUALITE)
BAIGNADE (NN,
NP, DATE, DUREE)
où NP désigne
le numéro de plage, NN le numéro de nageur.
IV.1.2. Expression des opérations
relationnelles
SQL permet d'exprimer
simplement les opérations relationnelles de base.
Le bloc SQL
se compose du mot-clé SELECT, suivi des attributs
qu'on désire voir figurer dans le résultat, du mot clé
FROM, suivi de la liste des relations touchées par
la question, enfin du mot clé WHERE, suivi d'une
qualification. Deux blocs SQL peuvent être conjugués par l�un des
opérateurs binaires de l�algèbre relationnelle : UNION, INTERSECT,
MINUS.
SELECT <liste d'attributs
projetés>
FROM <liste de relations>
WHERE <qualification>
Expression de projections
L'obtention de colonnes déterminées
s'effectue simplement en omettant de spécifier une qualification (le
prédicat absent derrière le WHERE est alors
supposé 'vrai' pour tous les tuples).
SELECT NOMP, REGION
FROM PLAGE
permet d'obtenir l'ensemble des couples
(nom_de_plage, région) présents dans la relation PLAGE.
Cependant l'élimination des doubles n'est pas automatique : pour lister
l'ensemble des régions distinctes on utilise le mot clé DISTINCT:
SELECT DISTINCT
REGION
FROM PLAGE
Enfin, les mots clés ORDER
BY, ASC, DESC permettent de compléter le
langage et d'ordonner les résultats pour l'utilisateur. Ainsi, pour obtenir
toutes les plages, triées par ordre alphabétique décroissant
sur les pollutions et croissant sur les noms de plage, on écrit :
SELECT *
FROM PLAGE
ORDER BY POLLUTION DESC, NOMP ASC
L'utilisation du "*" permet de demander
tous les attributs de la relation.
Expression des restrictions
La restriction consiste à
sélectionner l'ensemble des tuples vérifiant le critère
placé dans la clause WHERE. Ce critère de
restriction est une combinaison de critères élémentaires,
connectés à l'aide de AND et de OR
et pouvant présenter des NOT. Le critère
atomique est de la forme <Attribut q valeur> ou <Attribut1 q Attribut2>.
Le comparateur q appartient à l'ensemble {=, <>, >, ?, <, ?}.
Un bloc permettant d'exprimer la
restriction est par exemple:
SELECT *
FROM NAGEUR
WHERE QUALITÉ = 'médiocre' OR PRÉNOM
<> 'Jean'
La forme <Attribut1 q Attribut2>
exprime un critère de restriction s'il s'agit d'une comparaison entre
la valeur de l'Attribut1 et la valeur de l'Attribut2 du même tuple.
Le critère est évalué pour chaque tuple en comparant deux
valeurs d'attributs différents du même tuple. Par exemple, pour
connaître les bains pris sur des plages de même identifiant (NP)
que celui du baigneur (NN) - sémantiquement peu intéressant! -
on écrirait :
SELECT *
FROM BAIGNADE
WHERE NP = NN
La sélection mono-relation
générale utilise un bloc SQL qui combine une restriction et une
projection. Ainsi, pour obtenir les plages de sable de Bretagne, triées
par ordre alphabétique décroissant sur les pollutions et croissant
sur les noms de plage, on écrira :
SELECT NP, NOMP,
POLLUTION
FROM PLAGE
WHERE TYPE = 'sable' OR REGION <> 'Bretagne'
ORDER BY POLLUTION DESC, NOMP ASC
Expression des jointures
L'expression de questions multi-relations
en SQL , c'est-à-dire de questions mettant en �uvre, dans le résultat
ou dans la qualification, des informations issues de plusieurs relations, nécessite
d'exprimer l'opération de jointure.
SQL propose
deux façons d'exprimer la jointure : l'une est véritablement
assertionnelle, l'autre reste procédurale et utilise une imbrication
de blocs de base (un bloc de base est un ensemble SELECT�FROM�WHERE�).
Exemple : donner les
noms des nageurs ayant pris un bain
a) Expression assertionnelle
:
SELECT NOM
FROM NAGEUR, BAIGNADE
WHERE NAGEUR.NN = BAIGNADE.NN
Pour distinguer deux attributs
portant le même nom, ceux-ci sont préfixés par le nom
de la relation d'origine.
b) Expression procédurale
:
SELECT NOM
FROM NAGEUR
WHERE NN IN (SELECT NN
FROM BAIGNADE)
Cette manière d'exprimer la jointure présente deux inconvénients
:
- elle est procédurale
et demande par conséquent au système d'exécuter une
séquence d'opérations particulières. L'optimisation
et l'ordonnancement ne sont plus mis en �uvre par le système ; une
question mal posée peut, par conséquent, entraîner des
temps de réponse catastrophiques. Néanmoins certains bons
systèmes acceptent une question ainsi formulée, mais la transformer
en une assertion de façon à pouvoir ensuite utiliser leur
optimiseur.
- elle ne permet pas d'exprimer
une 'vraie' jointure : en effet seuls les attributs originaires d'une seule
des deux relations peuvent être présents dans le résultat
; il s'agit en fait d'une semi-jointure
Définition : semi-jointure:
la semi-jointure de R1 par R2,
notée R1 |X R2, est la jointure de R1 et de R2
projetée sur les attributs de R1.
La semi-jointure peut être considérée
comme une généralisation de la restriction. Ceci explique en effet
son coût relativement faible : l'opération n'exige qu'une seule
passe sur chaque relation. La relation R2 est d'abord lue pour obtenir les valeurs
de l'attribut de jointure ; puis la relation R1 est restreinte avec les valeurs
de l'attribut de jointure apparaissant dans R2. Chacun des tuples de R1 et R2
n'est lu qu'une seule fois.
L'utilisation de blocs imbriqués
dans SQL permet cependant d'exprimer simplement des prédicats
quantifiés (", $) grâce aux sous-questions (voir § IV.1.3).
Remarque: restriction et auto-jointure
La présence de prédicat
de la forme <Attribut1 q Attribut2> dans une qualification peut correspondre
soit à un prédicat de jointure, soit à un prédicat
de restriction. En fait, la question est de savoir si la comparaison porte sur
la valeur des attributs 1 et 2 d'un même tuple ou de tuples différents.
Si l'Attribut1 et l'Attribut2 sont préfixés par une variable relation
identique (ou ne le sont pas dans les cas où il n'y a pas d'ambiguité
sur la relation), il s'agit des valeurs du même tuple. C'est donc d'un
critère de restriction. Si l'Attribut1 et l'Attribut2 sont préfixés
par des variables relations différentes, il s'agit de la comparaison
d'une valeur de l'Attribut1 avec toutes les valeurs de l'Attribut. C'est donc
d'un critère de jointure.
Question :
SELECT NN
FROM NAGEUR
WHERE NOM = PRENOM
exprime la requête 'quels
sont les nageurs dont le nom est identique au prénom ?' (restriction);
en explicitant par une variable
N1 le lien attributs- relation, la question devient :
SELECT N1.NN
FROM NAGEUR N1
WHERE N1.NOM = N1.PRENOM
alors qu'en écrivant:
SELECT N1.NN
FROM NAGEUR N1, NAGEUR N2
WHERE N1.NOM = N2. PRENOM AND N1.NN <> N2.NN
on exprimerait 'quels sont
les nageur ayant pour nom le prénom d'un autre nageur ?' (auto-jointure).
Deux variables distinctes N1 et N2 sont déclarées cette fois-ci
sur la relation NAGEUR. SQL, on le voit, oblige a plus
de précision que la langue "naturelle".
IV.1.3. Sous-questions, prédicats
quantifiés, composition de questions
L'utilisation de blocs imbriqués
permet d'exprimer simplement et de façon très compréhensible
des expressions quantifiées. Le bloc interne est alors une sous-question.
Définition : sous-question
Une sous question est une question
SQL qui rend une seule colonne
Cette colonne est un ensemble de valeurs
qui constitue l'argument d'un prédicat IN, ALL,
ANY ou EXISTS du bloc externe (dans
le cas du EXISTS la sous-question peut rendre un nombre quelconque
de colonnes).
Illustrons ces possibilités
sur des exemples :
a) Prédicat IN
: quels sont les nageurs qui se sont baignés (au moins une fois) sur
une plage très polluée ?
SELECT NN FROM
BAIGNADE
WHERE NP IN (SELECT NP FROM PLAGE WHERE
POLLUTION = 'élevée')
IN est le test de présence
de la valeur d'un attribut dans un ensemble de valeur du même type. On l'utilise
pour exprimer la semi-jointure.
b) Prédicat EXISTS
: quels sont les nageurs qui se sont [ne se sont pas] baignés en 89
?
SELECT *
FROM NAGEUR N
WHERE [NOT] EXISTS (
SELECT *
FROM BAIGNADE
WHERE DATE BETWEEN '01-JAN-89' AND '31-DEC-89'
AND B.NN
= N.NN )
c) Prédicat ALL
: quelle est la plus longue baignade ?
SELECT *
FROM BAIGNADE
WHERE DUREE >= ALL (
SELECT DISTINCT
DUREE
FROM BAIGNADE)
d) Prédicat ANY
: quels sont les noms des nageurs qui se sont baignés plus longtemps
que Dupond ? (qui se sont baignés au moins une fois plus longtemps qu'au
moins une des baignades de Dupond)
SELECT N1.NOM
FROM NAGEUR N1, BAIGNADE B1
WHERE N1.NN = B1.NN
AND B1. DUREE
> ANY (
SELECT DISTINCT
DUREE
FROM BAIGNADE B2, NAGEUR N2
WHERE B2.NN = N2.NN
AND N2.NOM
= 'Dupond')
Enfin, on peut composer logiquement
- par union, intersection, différence, cf. § III.2.1 - deux questions
SQL. Si, par exemple, on voulait comparer notre table de nageurs à une
table d'élèves qui aurait le schéma ELEVE(NE, NOM, PRENOM,
AGE) alors les expressions SQL suivantes sont possibles - et leur sens est
identique à celui des expressions algébriques équivalentes
:
SELECT NOM , PRENOM
FROM ELEVE UNION SELECT NOM , PRENOM FROM NAGEUR
INTERSECT
MINUS
IV.1.4. Fonctions et groupements
La norme SQL
présente un ensemble de fonctions prédéfinies : COUNT,
SUM, AVG, MAX et
MIN. Ces fonctions opèrent sur une collection de valeurs
scalaires d'une colonne (éventuellement plusieurs pour COUNT)
d'une relation.
COUNT nombre
de valeurs de la colonne ;
SUM somme des valeurs de la colonne (argument de type
numérique) ;
AVG moyenne des valeurs de la colonne (argument de type
numérique) ;
MAX plus grande valeur de la colonne ;
MIN plus petite valeur de la colonne.
Cette collection de valeurs scalaires
d'une colonne est obtenue en effectuant un agrégat.
Définition : agrégat
Un agrégat est un partitionnement
horizontal (les tuples sont répartis en plusieurs groupes) d'une relation
selon des valeurs d'attributs, suivi d'un regroupement par une fonction de
calcul (somme, moyenne, minimum, maximum, compte�)
Les fonctions s'appliquent sur un agrégat
défini par les attributs figurant derrière un mot clé GROUP
BY, et après application d'un éventuel critère figurant
derrière un mot clé WHERE.
La clause HAVING permet
d'appliquer un critère de restriction sur des groupes.
Exemple :
Donner, par numéro de
plage, la durée moyenne et la somme des durées des baignades effectuées
après le 1/1/88, lorsque les durées cumulées dépasse
200 minutes.
SELECT NP,
AVG(DURÉE), SUM(DURÉE)
FROM BAIGNADE
WHERE DATE >= '01-JAN-88'
GROUP BY NP
HAVING SUM(DURÉE) > 200
Le système exécute d'abord
la restriction, puis effectue le regroupement, le calcul des fonctions et la
restriction sur le résultat du calcul ; exemple exécution de la
question précédente :
WHERE DATE ? 01/01/88
IV.1.5. Mises à jour
SQL propose trois
clauses correspondant aux différents types de mises à jour possibles
sur une base de données : l'insertion (clause INSERT),
la modification (clause UPDATE) et la suppression (clause
DELETE).
Insertion
Une insertion est l'ajout d'un
ou plusieurs tuples dans une relation. On peut insérer, par la commande
INSERT, des tuples explicitement mais un par un; le mot-clé
VALUES précède les valeurs de ses attributs.
INSERT INTO NAGEUR
VALUES (145, 'BOUJUS', 'Jacques', 'excellent')
On peut également insérer
directement un ensemble de tuples. Cet ensemble de taille quelconque doit être
le résultat d'une requête ensembliste. La seule condition est que
le schéma de la relation résultat (nombre et types des attributs)
soit le même que celui de la relation dans laquelle on ajoute des valeurs.
On parle dans ce cas d'insertion calculée.
La puissance d'expression d'un
langage relationnel s'affirme ici clairement. Ainsi la requête :
INSERT INTO BAIGNADE
SELECT '10',
NP, DATE, DUREE
FROM BAIGNADE
WHERE NN = '20'
ajoute pour le nageur 10 les baignades
effectuées par le nageur 20.
Mise à jour
L'aspect ensembliste du langage
est ici encore particulièrement utile pour les applications. Accéder
aux tuples un par un n'est pas nécessaire. Il suffit d'exprimer correctement
la condition qui conduit à la modification.
Par exemple :
UPDATE PLAGE
SET POLLUTION = 'élevée'
WHERE REGION = 'Bretagne'
AND NP IN ( SELECT NP FROM BAIGNADE
GROUP BY NP, DATE
HAVING COUNT (NN) > 5000 )
permettra de prendre en compte le
fait qu'une forte fréquentation des plages de Bretagne (les plages ayant
vu plus de 5000 baignades en un jour !) entraîne une pollution élevée.
Suppression
Enfin, la suppression ensembliste
s'exprime elle aussi très simplement. On supprime directement les mauvais
nageurs considérés comme noyés au delà de 300 minutes
dans l'eau.
DELETE FROM NAGEUR
WHERE QUALITE = 'exécrable'
AND NN IN
(
SELECT NN
FROM BAIGNADE
WHERE DURÉE > 300 )
ou, dans un exemple plus sérieux,
on supprime les baignades de Dupond comme ceci :
DELETE FROM BAIGNADE
WHERE NN IN (
SELECT NN
FROM NAGEUR
WHERE NOM = 'Dupond' )
IV.1.6. Le langage de déclaration
de données
Rappelons qu'une base de données
est définie par un schéma. Ce schéma est composé d'un
ensemble de relations. La création d'une nouvelle relation (vide) est réalisée
par l'instruction :
CREATE TABLE nom_de_relation
( liste de noms_d'attributs )
Chaque nom d'attribut a un type (CHARACTER,
NUMERIC, DECIMAL, INTEGER, SMALLINTEGER, FLOAT, REAL). Une contrainte NOT
NULL ou UNIQUE peut, de surcroît, être
imposé à un attribut . Déclarer une clé utilise simultanément
ces deux contraintes.
Exemple :
CREATE TABLE PLAGE
(
NP INTEGER
NOT NULL UNIQUE,
NOMP CHARACTER(30) NOT NULL,
REGION CHARACTER (30),
POLLUTION CHARACTER (20) )
CREATE TABLE NAGEUR (
NN INTEGER
NOT NULL UNIQUE,
NOM CHARACTER(15) NOT NULL,
PRENOM CHARACTER(15),
QUALITÉ CHARACTER(20) )
CREATE TABLE BAIGNADE (
NN INTEGER
NOT NULL,
NP INTEGER NOT NULL,
DATE CHARACTER(8) NOT NULL,
DURÉE INTEGER ,
UNIQUE (NN, NP, DATE) )
Remarquez l'utilisation de la clause
UNIQUE dans la création de BAIGNADE
: il s'agit ici de déclarer une clé multi-attributs (il
n'existe pas deux triplets identiques de valeurs NN, NP, DATE).
Chaque attribut NN, NP et DATE
est déclaré NOT NULL, puis l'unicité
du triplet est déclarée.
La clause CREATE TABLE
est beaucoup plus complète que nous ne l'indiquons ici : elle permet notamment
de préciser d'autres contraintes d'intégrité, des droits
ainsi qu'un certain nombre de contraintes concernant l'implantation physique de
la relation.
IV.1.7. Intégration
d'un langage de manipulation de données dans un langage de programmation
L'intégration de SQL
dans un langage de programmation apparaît très vite indispensable
lorsqu'on considère l'utilisation, par des programmes d'application, des
informations stockées dans la BD relationnelle. Deux approches sont possibles
: la première consiste à intégrer le langage de manipulation
de données dans un langage de programmation quelconque, considéré
comme un langage hôte. La seconde, plus ambitieuse, étend un langage
de programmation donné avec des clauses spécifiques de la manipulation
des données. Elle est restée jusqu'à présent du domaine
de la recherche, mais le problème, bien réel, de la "cohabitation"
entre un langage de programmation procédural, privilégiant un traitement
unaire et un langage de manipulation ensembliste des données, redevient
d'actualité avec les Bases de Données Orientées Objet. L'approche
Objet tente, en effet, de réconcilier données et traitements.
La démarche dominante d'intégration
de SQL est précisée jusque dans la norme
sous le nom de Embedded SQL. Dans cette approche,
les attributs des relations manipulées sont d'abord déclarés
comme des variables du langage de programmation. Un programme hôte comporte
des instructions standard du langage, une section de déclaration, un
ensemble de définitions de curseurs et un ensemble de traitements SQL.
L'implantation d'un Embedded SQL
est généralement effectuée à l'aide d'une technique
de précompilation. Le précompilateur analyse des commandes SQL
et insère des appels de sous-programmes du langage hôte à
destination du SGBD (cela suppose l'existence d'une bibliothèque
de primitives de bas niveau entre le langage hôte et le SGBD).
Il recopie telles quelles les instructions du langage hôte. Le précompilateur
génère en sortie, on obtient un programme en langage hôte
qui est compilable par le compilateur de ce langage.
Nous donnons ici un exemple en C-Embedded
SQL d'une mise à jour sur l'attribut POLLUTIONde
la relation PLAGE :
#include <stdio.h>
EXEC SQL INCLUDE SQLCA.H ;
main(argc,argv)
int argc;
char *argv[];
{
char *answer[1];
EXEC SQL BEGIN DECLARE SECTION ;
VARCHAR db_connect_string[32];
VARCHAR xx[32];
VARCHAR yy[16];
VARCHAR zz[16];
EXEC SQL END DECLARE SECTION ;
strcpy(db_connect_string, argv[1]);
EXEC SQL CONNECT : db_connect_string;
EXEC SQL DECLARE C1 CURSOR FOR
SELECT nomp, pollution FROM
plage WHERE region = :yy
FOR UPDATE OF pollution;
while
(printf("\nRégion? (\0 pour fin): ") && scanf("%s", zz) &&
strcmp(zz,'\0') != 0)
{
EXEC SQL OPEN C1 ;
while (sqlca.sqlcode = 0)
{ /* la structure sqlca est
dans SQLCA.H*/
EXEC SQL FETCH C1 INTO :xx, :zz;
printf("\n%32s, qui était %16s, devient-elle Alarmante? (O|N)",
xx, zz) ;
scanf("%s", answer) ;
if (answer == 'O')
EXEC SQL UPDATE PLAGE
SET pollution = 'Alarmante'
WHERE CURRENT OF C1 ;
} /* end of while cursor open */
EXEC SQL CLOSE C1 ;
EXEC SQL COMMIT WORK ;
} /* end of while Région indiquée */
} /* end of main */
Commentaires
- un traitement SQL
est annoncé par EXEC SQL et terminé par
un caractère point-virgule :
- toutes les variables hôtes
référencées dans un traitement SQL
sont définies ; dans une "embedded declare section", délimitée
par un BEGIN DECLARE SECTION et un END
DECLARE SECTION ;
- le type des variables hôtes
doit être compact avec celui des attributs qui les accueillent ;
- tout programme SQL
doit inclure une variable hôte appelé SQLCODE.
A chaque exécution d'un traitement SQL, l'indicateur
numérique SQLCODE, renvoie le résultat
de la requète SQL (OK,
¢OK, fin curseur).
- un traitement SQL
inclut des références aux variables hôtes. Celles-ci
doivent être préfixées par un nom d'attribut pour les
distinguer.
Des curseurs. sont utilisés
pour traiter des ensembles de tuples dans le langage. Le mécanisme de curseur
permet un traitement unaire (tuple à tuple) d'un ensemble de tuples fourni
par le SGBD, par le langage hôte, en réponse à une requête
SQL. Les curseurs peuvent être ouverts (OPEN
C) , fermés (CLOSE C), positionnés en
lecture sur un tuple (ordre FETCH) et utilisés comme
position de référence (CURRENT OF C).
Remarquez que cette approche, à
présent normalisée, est assez riche en possibilités ; elle
est cependant relativement lourde à mettre en �uvre et l'on peut s'attendre
dans les prochaines années à son abandon progressif au profit
des langages de quatrième génération (L4G).
Langage de quatrième génération
Nous terminerons cette section
par quelques mots sur les langages de quatrième génération.
Concernant directement les développeurs d'applications, ces langages
visent à faire gagner du temps et à baisser les coûts d'écriture
des programmes. Ils utilisent largement les possibilités des SGBD
relationnels à travers une manipulation essentiellement ensembliste des
données. Ils ne constituent cependant pas simplement un langage de manipulation
de données comme SQL, mais intègrent un ensemble
complet de possibilités de traitement en fournissant des structures de
contrôle d'un langage structuré de haut niveau (boucle, condition,
parcours�). Ils s'efforcent, de surcroît, d'intégrer un ensemble
d'outils (générateurs de rapports, de graphiques, d'applications�)
souvent disponibles comme des logiciels à adjoindre au SGBD.
Orientés vers la programmation visuelle, ils constituent parfois un véritable
environnement de développement et sont aujourd'hui l'objet d'une forte
demande.
Il ne faut cependant pas perdre
de vue que ces langages modernes ne constituent qu'une couche de manipulation
des informations stockées dans un SGBD. La qualité,
les performances, la puissance d'expression résulte, en dernière
analyse, essentiellement à la qualité du SGBD
relationnel sous-jacent.
Signalons de surcroît qu'un
L4G ne fournit aucun support à une gestion de
transactions (voir chapitre 6), nécessaire à tout usage multi-utilisateurs
: cette gestion de transactions doit alors être prise en compte au niveau
de l'application. Les L4G ne sont pas l'objet d'une définition
standard acceptée par tous : à ce jour, le choix d'un L4G
particulier condamne l'utilisateur à une dépendance totale envers
son fournisseur. Certains L4G génèrent
cependant du SQL standard, élément favorable
qui peut être pris en compte dans le choix.
IV.2. Autres langages relationnels
Deux autres langages méritent
d'être mentionnés : QUEL et QBE.
IV.2.1. QUEL
Le langage QUEL
fut le concurrent malheureux de SQL sur le terrain de la
normalisation. Très proche, il constitue le langage natif du prototype
INGRES de BERKELEY (INGRES, à
présent propose les deux langages QUEL et SQL).
Sa syntaxe oblige à déclarer
des variables sur les relations manipulées:
RANGE OF variable IS
nom-de-relation
Plusieurs variables peuvent être
associées à une relation. Les recherches élémentaires
s'expriment par les clauses :
RETRIEVE (liste résultat)
WHERE qualification
Exemple : donner le nom des
nageurs qui se sont baignés à la fois à Deauville et à
Bayonne en 1983.
RANGE OF P, P',
N, B, B' IS PLAGE, PLAGE, NAGEUR, BAIGNADE, BAIGNADE
RETRIEVE N.NOM
WHERE N.NN = B.NN AND B.NP = P.NP AND B.DATE
= '1983' AND P.NOM = 'Deauville'
AND N.NN = B'.NN AND B'.NP
= P'.NP AND B'.DATE = '1983' AND P'.NOM = 'Bayonne'
Seules les recherches avec fonctions
et agrégats s'éloignent de SQL par exemple
:
RETRIEVE AVG (B.DURÉE
WHERE B.DATE = '02-07-89')
permet de demander la durée
moyenne des baignades du 2 juillet 1989.
IV.2.2. Query By Example (QBE)
La langage QBE
est conçu comme un langage graphique. Développé par IBM
à Yorktown Height, et aujourd'hui commercialisé par IBM.
Il est basé sur une mise en �uvre visuelle des questions et des résultats
; il constitue à ce titre un précurseur des langages visuels d'aujourd'hui.
Lorsqu'un utilisateur désire
utiliser les informations d'une relation, il en demande l'affichage du squelette,
c'est-à-dire le schéma. C'est dans ce squelette qu'il spécifie
ses requêtes. Nous illustrons très rapidement les possibilités
de QBE par quelques questions simples.
a) Quelles sont les durées
des baignades du baigneur N°10 ?
b) Quelles sont les pollutions
des plages où s'est baigné Durand ?
on utilise ici la notion de variable
exemple (une chaîne quelconque soulignée est un exemple) pour exprimer
la jointure entre deux relations. Le "P." indique la colonne demandée
en résultat.
c) Lister les noms des plages de
sable moins polluées que la plage n° 10.
On a exprimé ci-dessus une
auto-jointure.
IV.3. Conclusions
Les langages relationnels d'aujourd'hui
s'articulent tous autour du SQL. Sa standardisation par l'ISO - SQL1 ou SQL89,
puis SQL2 ou SQL92 - est en effet incontournable, mais présente des inconvénients
et des lourdeurs liées à l'absence, de prise en compte des outils
moderne d'interface (menus, multi-fenêtrage, souris�). De surcroît,
SQL, malgré son apparente simplicité, présente
des subtilités qui le rendent difficilement utilisable par l'utilisateur
final. Pour toutes ces raisons, l'ensemble des constructeurs proposent aujourd'hui
des outils (easy SQL, générateur de SQL�)
destinés à faciliter l'tilisation interactive de SQL.
IV.4. Annexe : sémantique
et syntaxe de SQL
a) Sémantique de SQL
Afin de bien comprendre la sémantique
d'une requête SQL, il faut se rappeler qu'à chaque ligne d'une
question correspond une opération. Les opérations sont interprétées
dans l'ordre 1/ 2/ 3/ 4/ 5/ 6/.
SELECT
<liste d'attributs>
5/ projection finale ;
FROM
<liste de relations>
1/ produit cartésien de toutes les relations citées ;
WHERE
<qualification>
2/ applications des restrictions sur les tuples
(après le produits cartésien, tous les prédicats élémentaires
de la qualification sont des prédicats de restriction) ;
GROUP BY
<liste d'attribut>
3/ calcul des agrégats et des fonctions ;
HAVING <qualification>
4/ application d'une restriction sur les groupes résultant d'un calcul
de fonction ;
ORDER BY
<liste d'attribut>
6/ tri du résultat.
Attention : il s'agit de l'interprétation
sémantique de la requête SQL ; il ne faut
surtout pas en conclure qu'un SGBD relationnel exécute
la requête de cette façon.
b) Règles syntaxiques en
grammaire BNF
Query expressions
query-exp ::= query-term | query-exp
[UNION | intersect | minus query-term]
query-term ::= query-spec | (query-exp)
query-spec ::= SELECT
[ALL | DISTINCT] selection table-exp
selection ::= scalar-exp-commalist
| *
table-exp ::= from-clause
[ where-clause ]
[ group-by-clause ]
[ having-clause ]
from-clause ::= FROM table-ref-commalist
table-ref ::= table [range-variable]
where-clause ::= search-condition
group-by-clause ::= GROUP
BY column-ref-commalist
having-clause ::= HAVING
search-condition
Search conditions
search-condition ::= boolean-term
| search-condition OR boolean-term
boolean-term ::= boolean-factor
| boolean-term AND boolean-factor
boolean-factor ::= [ NOT]
boolean-primary
boolean-primary ::= predicate
| ( search condition )
predicate ::= comparison-predicate
| between-predicate
| like-predicate
| test-for-null
| in-predicate
| all-or-any-predicate
| existence-test
comparison-predicate ::= scalar-exp comparison {scalar-exp | subquery}
comparison ::= = | <> | <
| > | ? | ?
between-predicate ::= scalar-exp
[NOT] BETWEEN scalar-exp AND
scalar-exp
like-predicate ::= column-ref
[NOT] LIKE atom [ESCAPE
atom]
test-for-null ::= column-ref
IS [NOT] NULL
in-predicate ::= scalar-exp [NOT]
IN
{ subquery | atom [,
atom-commalist] }
all-or-any-predicate ::= scalar-exp comparison [ALL
| ANY | SOME ] subquery
existence-test ::= EXISTS
subquery
subquery ::= ( SELECT
[ALL | DISTINCT] selection table-exp
)
Scalar expressions
scalar-exp ::= term | scalar-exp
{ + | - } term
term ::= factor ? term { * |
/ } factor
factor ::= [ + | - ] primary
primary ::= atom
| column-ref
| function-ref
| ( scalar-exp )
atom ::= parameter-ref
| literal
| USER
parameter-ref ::= parameter [ [INDICATOR] parameter
function-ref ::= COUNT
(*) ? distinct-function-ref ? all-function-ref
distinct-function-ref ::= { AVG
| MAX | MIN | SUM
| COUNT } ( DISTINCT column-ref
)
all-function-ref ::= { AVG
| MAX | MIN | SUM
| COUNT } ( [ALL] scalar-exp )
column-ref ::= [column-qualifier.]
column
column-qualifier ::= table |
range-variable
IV.5. Références
[Date 87] C.J. DATE :
"A Guide to the SQL Standard", Addison-Wesley, 1987.
[ISO 86] ISO
ANSI, "Database Language SQL", ISO/DIS 9075, Draft International
Standard, 1986.
[ISO 88] ISO
ANSI, "Database Language SQL2", Working Draft, 1988.
Chapitre 5