Chapitre 5

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)

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