• Aucun résultat trouvé

Extension de la clause select

Dans le document BASES DE DONNÉES ET MODÈLES DE CALCUL (Page 137-143)

SQL avancé

6.3 EXTENSION DE LA STRUCTURE DES REQUÊTES SFW

6.3.1 Extension de la clause select

La clause select permet de spécifier les grandeurs à extraire de chaque ligne de la table fictive (ou de l’ensemble des lignes pour les fonctions agrégatives) définie par les clauses from/group-by/having. Toute expression qui renvoie une valeur pour chaque ligne est valide : nom de colonne, constante, expression arithmétique, de caractères ou de date. Il est aussi possible d’y inclure une requête SFW pourvu qu’elle renvoie une seule valeur :

select NCOM, (select sum(QCOM*PRIX) from DETAIL D, PRODUIT P where D.NPRO = P.NPRO

and D.NCOM = M.NCOM) as MONTANT from COMMANDE M

where MONTANT > 1000 6.3.2 Extension de la clause from

D’une manière générale, la clause from permet de mentionner les tables desquelles des données élémentaires ou calculées sont extraites (clause select). Jusqu’ici, on n’y mentionnait que le nom de tables de base ou de vues. Il est cependant possible d’y spécifier des tables dérivées, résultant d’opérateurs produisant des tables. Nous en examinerons trois : les opérateurs ensemblistes, la requête SFW elle-même et les opérateurs de jointure.

a) Les expressions ensemblistes

Toute expression consistant en une union, une intersection ou une différence de tables, qu’elles soient de base ou dérivées, peut être utilisée dans la clause from. Les règles de formation de telles expressions sont celles qui ont été brièvement décrites en 5.4.4. Considérant les tables BON_CLIENT (décrivant un sous-ensemble des clients) et PROSPECT (reprenant les clients potentiels), de même schéma que CLIENT, on pourra écrire5 :

select NCLI, NOM

from ((select NCLI, NOM, LOCALITE from CLIENT) except

(select NCLI, NOM, LOCALITE from BON_CLIENT) union

(select NCLI, NOM, LOCALITE from PROSPECT)) where LOCALITE = ’Poitiers’

b) Les requêtes SFW

Rappelons que l’évaluation d’une requête SFW a pour résultat une table d’une ou plusieurs colonnes, éventuellement vide. Il est permis d’utiliser cette table comme source de lignes pour une autre requête SFW sans qu’il soit nécessaire de stocker la table intermédiaire par un "insertinto"6. La requête suivante calcule la moyenne des montants des commandes7 :

select avg(MONTANT)

from (select NCOM, sum(QCOM*PRIX) as MONTANT from DETAIL D, PRODUIT P

where D.NPRO = P.NPRO group by NCOM)

5. La forme intuitive from CLIENTexceptBON_CLIENT ... n’est pas valide. Cependant la forme équivalente fromtableCLIENTexcepttableBON_CLIENT ... est autorisée.

6. La section 6.2.6 propose une autre technique, basée sur la définition d’une vue intermédiaire.

7. On ne peut pas écrire : avg(sum(QCOM*PRIX)).

6.3 Extension de la structure des requêtes SFW 139

© Dunod – La photocopie non autorisée est un délit.

Combinant cette possibilité avec les opérateurs ensemblistes, on peut aussi écrire, pour afficher la quantité totale commandée de chaque produit :

select NPRO, TOTAL_QTE

from ( (select NPRO, sum(QCOM) from PRODUIT P, DETAIL D where P.NPRO=D.NPRO

group by NPRO) union

(select NPRO, 0 from PRODUIT

where NPRO not in (select NPRO from DETAIL)) )

as DP(NPRO,TOTAL_QTE) where TOTAL_QTE < 1000

On notera la définition d’un alias de table (DP) accompagnée de celle des colonnes résultantes, ce qui permet de les utiliser dans les clauses select et where.

c) Les opérateurs de jointure

SQL propose des opérateurs explicites pour réaliser la jointure de deux ou plusieurs tables. Leur usage peut simplifier certaines requêtes, mais aussi rendre proprement illisibles certaines expressions, auquel cas on lui préférera celui de la formulation étudiée en 5.4.1, plus régulière. Le langage offre cinq formes d’opérateurs, large-ment redondantes, et dont l’utilité peut laisser perplexe.

Cross join La forme : select *

from CLIENT cross join COMMANDE where ...

est équivalente à l’expression classique du produit relationnel : select *

from CLIENT, COMMANDE where ...

Natural join

Si les colonnes servant à la jointure portent le même nom, et que toutes les autres colonnes portent des noms différents dans les deux tables, on peut écrire :

select *

from CLIENT natural join COMMANDE where ...

dont la formulation classique est : select *

from CLIENT C, COMMANDE M where C.NCLI = M.NCLI and ...

Join on

La même requête pourrait s’écrire : select *

from CLIENT C join COMMANDE M on (C.NCLI = M.NCLI) where ...

Il est possible de construire des jointures multiples. Dans ce cas, elles sont évaluées de gauche à droite. Ainsi, l’expression :

select *

from CLIENT C join COMMANDE M on (C.NCLI = M.NCLI) join DETAIL D on (M.NCOM = D.NCOM)

join PRODUIT P on (D.NPRO = P.NPRO) where ...

est-elle équivalente à l’expression classique, plus régulière et plus lisible : select *

from CLIENT C,COMMANDE M, DETAIL D,PRODUIT P where C.NCLI = M.NCLI

and M.NCOM = D.NCOM and D.NPRO = P.NPRO

Attention. Les jointures étant effectuées de gauche à droite, une condition ne peut citer que les composants (tables, alias, colonnes) des arguments de l’opérateur auquel elle est associée, et qui sont mentionnés à sa gauche. Ainsi, dans la formula-tion ci-dessus, la condiformula-tion de la première jointure ne peut citer que les tables CLIENT et COMMANDE alors que la dernière peut citer les quatre tables. L’ordre de ces opérateurs est donc important, contrairement à la formulation classique.

Join using

Le comité de normalisation propose une version plus compacte du joinon : select *

from CLIENT join COMMANDE using (NCLI) where ...

En revanche, les colonnes ayant servi à définir cette jointure ne peuvent être préfixées dans les clauses select et where : on peut y citer NCLI, mais pas CLIENT.NCLI8. Il est permis d’utiliser le qualifieur inner, facultatif :

6.3 Extension de la structure des requêtes SFW 141

© Dunod – La photocopie non autorisée est un délit.

from CLIENT inner join COMMANDE using (NCLI)

Outer join

La section 5.4.4 introduisait la possibilité d’ajouter au résultat d’une jointure les lignes célibataires d’une des tables, c’est-à-dire les lignes qui n’ont pas de corres-pondant dans l’autre table. Cette extension de la jointure s’appelle jointure externe, par opposition à la jointure classique, dite interne. Ainsi, la requête de la section 5.4.4, qu’on rappelle :

select NCOM, C.NCLI, DATECOM, NOM, LOCALITE from COMMANDE M, CLIENT C

where M.NCLI = C.NCLI union

select null, NCLI, null, NOM, ADRESSE from CLIENT

where NCLI not in (select NCLI from COMMANDE) peut être écrite de manière plus compacte et plus claire sous la forme : select NCOM, C.NCLI, DATECOM, NOM, LOCALITE

from COMMANDE M right outer join CLIENT C on (M.NCLI = C.NCLI)

Le terme rightouterjoin indique qu’on inclut les lignes célibataires de la table de droite (CLIENT) dans le résultat. Il existe une version left outer join qui préserve les lignes célibataires de la table de gauche et full outer join qui conserve les lignes célibataires des deux tables.

Cette syntaxe n’est pas adoptée par tous les SGBD. C’est ainsi qu’Oracle utilise une variante de la jointure standard, illustrée ci-dessous par la reprise de la dernière requête. Le côté célibataire y est indiqué par le symbole "(+)".

select NCOM, C.NCLI, DATECOM, NOM, LOCALITE from COMMANDE M, CLIENT C

where M.NCLI = (+) C.NCLI

Conclusion. Il semble que seules les formes d’outer join apportent réellement plus de facilité dans l’écriture des requêtes SFW. En outre, la déclaration explicite de la jointure externe permet au SGBD d’exécuter cet opérateur, par ailleurs très coûteux, de manière efficace. Les autres formes peuvent entraîner une complexité inutile, et donc un risque d’erreurs. On leur préférera autant que possible les expres-sions plus régulières proposées en 5.4.19. Le lecteur apprendra avec ravissement 8. Cette remarque n’a en soi aucun intérêt, mais illustre la complexité induite par ces formes de jointure.

9. Inélégamment qualifiées de old style par certains auteurs [Melton,1999].

que la norme SQL3 ajoute encore quelques formes supplémentaires de jointures relatives notamment aux clés étrangères, chacune plus indispensable que les autres.

d) Retour sur la notion de condition de non association

À la section 5.4.6, nous avions observé qu’une condition d’association pouvait s’exprimer indifférement par une sous-requête ou par une jointure (sans parler de la forme exists). En revanche, une condition de non-association ne pouvait être traduite qu’en une sous-requête ou une forme not exists. Il est en fait possible de l’exprimer en passant pas le mécanisme de jointure externe. Les clients qui n’ont pas passé de commandes peuvent s’obtenir par les trois requêtes suivantes :

select NCLI,NOM from CLIENT

where NCLI not in (select NCLI from COMMANDE) select NCLI,NOM

from CLIENT C

where not exists (select * from COMMANDE where NCLI = C.NCLI) select NCLI,NOM

from CLIENT C left outer join COMMANDE M on (C.NCLI = M.NCLI)

where M.NCOM is null

e) Synthèse sur la forme des requêtes SFW

Ces extensions, ajoutées aux formes de base pourraient donner du langage SQL une image de complexité et d’incohérence. Bien que cette critique soit en partie méritée, les nouvelles constructions augmentent fortement la régularité du langage et simpli-fient ce dernier. Pour dire les choses plus concrètement, la probabilité qu’une requête construite par un rédacteur naïf soit correcte10 et s’interprète comme prévu, est plus grande qu’auparavant !

Une requête SFW comporte principalement deux parties (pour simplifier, on ignorera les clauses groupby, having, orderby et les fonctions agrégatives) : select liste-valeurs

from expression-de-table

liste-valeurs désigne une liste d’expressions définissant chacune une valeur pour chaque ligne de la table expression-de-table; et expression-de-table est une expression définissant une table réelle

ou virtuelle (vue).

10. Un exemple concret : là où il est permis de mentionner une table, il est désormais possible d’y introduire une expression dont l’évaluation produit une table.

6.3 Extension de la structure des requêtes SFW 143

© Dunod – La photocopie non autorisée est un délit.

Un élément de liste-valeurs est toute expression qui peut renvoyer une valeur : le nom d’une colonne, une constante, une expression de calcul (arithmétique, chaîne de caractère, temporel), ou une expression SFW qui renvoie une seule valeur.

Il existe différents moyens de définir une table selon expression-de-table, qui tous ont comme propriété de renvoyer une suite de lignes : une table réelle de base ou une vue (le cas le plus fréquent), l’union (intersection, différence) de deux tables (elles-mêmes réelles ou vues), la jointure de deux tables (réelles ou vues) ou le résultat d’une requête SFW, qui, comme on le sait, renvoie une suite de lignes.

L’élégance de cette définition vient de sa récursivité : on peut rédiger une requête SFW dont la clause from contient la jointure de tables définies par des requêtes SFW, et ainsi de suite. Il faut reconnaître que les développeurs de SGBD n’appré-cient que très modérément cette puissance, et que, dans la réalité, ces possibilités sont souvent fortement bridées, voire ignorées (l’interpréteur, hagard, déclarant forfait, parfois sans avertissement11). Au lecteur de vérifier les possibilités offertes par son SGBD.

Dans le document BASES DE DONNÉES ET MODÈLES DE CALCUL (Page 137-143)