Вы находитесь на странице: 1из 180

 

 
 
  Architectures, modèles et Langages de Données 
 

Ingénierie  des  
bases de données 

Hypercube
c,d
OLAP 

Fascicule 4

  Interface applicative, théorie de la normalisation et optimisation


  des requêtes SQL
 
 
 
 
 
 
 
 
 
 
 AndréGamache 2005
 
 
  
Architectures, Modèles et Langages de Données 
 
Volume 1 
 
Fascicule 1 
1‐ Introduction                  1 
2‐ Architecture  fonctionnelle du SGBD            20   
3‐ Modèle conceptuel des données              79 
‐ Index          
Fascicule 2 
4‐ Modèle relationnel : théorie et contraintes d’intégrité        1 
5‐ Algèbre relationnelle                57 
6‐ Transposition du MCD au MRD             137 
‐ Index  
 
 
 
 
 
Volume 2 
 
Fascicule 3 
7‐ Langage de données SQL               1 
8‐ Indexation, vue relationnelle et base réactive          123 
 ‐ Index 
Fascicule 4 
9‐ Langage de programmation et SQL            1 
10‐ Théorie de la normalisation relationnelle :  FN1 à FN5       45 
11‐ Optimisation des requêtes relationnelles          117 
 
Annexes : 
A‐ SQLoader   
B‐ Projet ALU‐Nord : Script de chargement    
‐ Index 
 
 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université 
Laval, Québec, Qc, Canada, G1K 7P4. Courriel : andre.gamache@ift.ulaval.ca 
 

 
Chapitre 9 
Interface Applicative 
 
Le langage SQL autonome n’est pas l’outil le plus pratique pour traiter les données puisque ce 
dernier  est  limité  à  l’affichage  des  données  sans  mise  en  forme  particulière.  Les  utilisateurs, 
surtout  ceux  qualifiés  d’occasionnels,  formulent  rarement  avec  succès  et  rapidité,  des  requêtes 
SQL  de  jointures  complexes  ou  faisant  usage  du  groupement.  Cʹest  pour  cela  quʹil  faut 
développer des applications pour exploiter de façon plus conviviale les données et cela avec une 
interface  souvent  du  genre  formulaire.  À  partir  d’une  telle  interface,  il  doit  être  possible 
d’implémenter la logique du traitement de l’application et de faire les recherches et les mises à 
jour  formulées  par  l’utilisateur.  Une  application  L3G  doit  donc  interagir  avec  le  SGBD  en 
utilisant les clauses SQL via une API ou un pilote ODBC.  
 
Pour le concepteur dʹapplications, le SQL interactif demeure toujours utile parce qu’il lui permet 
de  tester  rapidement  la  syntaxe  d’une  requête  complexe,  d’évaluer  la  justesse  du  résultat  et 
dʹapprécier  la  performance  de  son  calcul.    Une  fois  mise  au  point,  cette  requête  peut  être 
intégrée directement dans une application.  
9.1 Interaction d’une application avec les données 
Les interfaces de programmation SQL sont essentielles dans le développement des applications 
L3G  adaptées aux besoins des organisations. L’usage le plus fréquent est l’intégration du SQL 
dans un langage L3G comme le C, le Pascal, JAVA, ADA ou FORTRAN. Plusieurs approches de 
programmation SQL ont été proposées, notamment le SQL intégré et l’ODBC. 
 
Pour une application, les interactions générales avec une base de données  suivent le protocole 
suivant 1 : 
Connexion  avec  la  base  de  données  gérée  par  un  serveur  par  lʹentremise  dʹun  compte  dʹaccès 
protégé par un mot de passe. Vérification du succès de lʹopération dʹouverture de la base. En 
cas d’échec, c’est généralement l’arrêt de l’application. 
Exécution  dʹune  clause  SQL  ou  DML  pour  effectuer  une  action  sur  la  base,  suivie  du  test  de 
succès  de  cette  opération  par  l’application.  En  cas  d’échec,  c’est  généralement  l’arrêt  de 
l’application. 
Réception de quelques données provenant de lʹensemble réponse (ensemble résultat stocké dans 
l’espace  curseur  du  serveur)  et  cela,  dans  lʹespace  local  défini  par  les  variables  hôtes  de 
lʹapplication client. En cas d’échec, c’est généralement l’arrêt de l’application. 
Traitement  des  données  reçues  en  mettant  en  œuvre  toutes  les  ressources  disponibles  à 
l’application. 
Validation  ou  recouvrement  des  modifications  effectuées  dans  la  base  de  données  par  un 
commit ou un rollback.  
Fermeture de la base avec interruption de la connexion. 
Résultat formé avec un seul tuple  
Dans un premier temps et pour la simplicité des exemples, les quelques programmes présentés 
concerneront  le  cas  particulier  dʹun  ordre  DML,  dont  la  réponse  contient  obligatoirement  un 

© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université 
Laval, Québec, Qc, Canada, G1K 7P4. Courriel : andre.gamache@ift.ulaval.ca 
Chapitre 9 Interface applicative  8

seul tuple. En d’autres mots, il s’agit d’un ordre dont le critère de recherche utilise généralement 
une  clé  de  relation.  L’autre  cas  possible,  mais  accidentel,  serait  un  ordre  se  rapportant  à  un 
attribut  quelconque  dʹune  table  et  qui  n’aurait  dans  la  réponse  qu’un  seul  tuple  vérifiant  le 
critère  de  sélection.  Ce  type  de  requêtes  fait  appel  à  une  syntaxe  particulière,  légèrement 
différente de la requête qui génère un résultat formé avec plusieurs tuples. 
Résultat formé de plusieurs tuples : mécanisme du CURSEUR 
Le  cas  le  plus  fréquent  et  plus  intéressant  est  légèrement  plus  complexe.  La  réponse  est  
composée  de  plusieurs  tuples  qui  seront  obtenus  et  rangés  temporairement  dans  un  espace 
curseur  créé  et  géré  au  niveau  du  serveur  (Result  Set).  Ces  tuples  obtenus  par  le  calcul  de  la 
réponse  sont  alors  placés  dans  une  table  temporaire  rangée  dans  un  espace  RAM  acquis 
dynamiquement  par  le  SGBD.  Par  la  suite,  le  module  chargé  de  transmettre  les  tuples  de  la 
réponse, le fera sur demande de l’application, un à un ou par petits lots de tuples si une option 
ARRAY  PROCESSING  est  activée.  Dans  ce  dernier  cas,    lʹapplication  devra  alors  gérer  une 
structure de tableau  pour stocker le lot de tuples et pour ensuite les exploiter un à un. En règle 
générale, une application L3G peut gérer facilement la réception dʹun tuple à la fois et exploiter 
simultanément plusieurs curseurs. 
Variable hôte dans les L3G 
Cette  notion  de  variable  hôte  d’un  langage  de  programmation  est  définie  par  rapport  aux 
attributs de la base. Dans une clause SQL, toute variable qui n’est pas explicitement un attribut 
du schéma est généralement une variable hôte (à lʹexception des symboles utilisés dans le SQL 
dynamique  pour  réserver  lʹespace  pour  les  variables  non  encore  définies)  identifiée  dans  la 
clause  SQL  par  le  préfixe  formé  des  deux  points(:).  Une  telle  variable  est  détectée  par  le 
précompilateur  lors  du  parsing  de  la  précompilation.  Cette  variable  hôte  est  généralement 
déclarée  comme  une  variable  globale  ou  encore  déclarée  dans  la  section  délimitée  par  deux 
clauses  spéciales  :  le  BEGIN  DECLARE  et  le  END  DECLARE.    Les  types  de  ces  variables  sont 
ceux du langage hôte ou ceux du SGBD pour lesquels les types correspondants  existent dans le 
langage hôte. 
 
EXEC SQL BEGIN DECLARE SECTION;
/* variables utilisées dans les clauses SQL */
char v_schema[10];
char v_motpasse [15];
int v_quantite;
varchar2(40) nom;
...
EXEC SQL END DECLARE SECTION;
               
Les  variables  schema,  motpasse  et  quantite    sont  des  variables  C  qui  seront  utilisées  dans  une 
clause SQL de l’application.  
                                     
Exemple :    
SELECT qte INTO :v_quantite FROM Ventes;

 
Chapitre 9 Interface applicative  9

Types des variables 
Les types des variables utilisées dans les clauses SQL  sont ceux du langage de programmation 
auxquels sʹajoutent quelques types, notamment le varchar2() pour les chaînes de caractères, qui 
est cependant absent dans plusieurs langages de programmation.  Avec le C, le précompilateur 
transforme le type varchar2() en une structure composée dʹune longueur et dʹun champ  texte :  
ex. nom VARCHAR2(40) est transformé ainsi : 
struct {
unsigned short len;
unsigned char arr[40};
} nom;
 
Chaque  élément  de  la  struct  est  donc  accessible  dans  lʹapplication  au  moyen  dʹune  référence 
pointée : nom.arr. Il faut tenir compte des discordances de types entre ceux implémentés dans le 
SGBD (ex. Oracle) et ceux du langage hôte utilisé pour faire le traitement local sur les données. 
Si un attribut prenom est de type char(25), il faut avoir une variable char prenom[25] qui inclut 
le caractère de fin de chaîne obligatoire en C (ʺ\0ʺ). Par conséquent la longueur maximale réelle 
du prénom sera de 24 caractères.  
9.2 Traitement des exceptions et mode de fonctionnement du SGBD 
Chaque  ordre  DML  exécuté  retourne  des  informations  par  lʹentremise  dʹune  structure  incluse 
dans  une  zone  de  communication  qui  contient  plusieurs  informations,  notamment  l’ordre 
transmis. Cette information, souvent réduit et désignée sous le vocable générique code de retour 
est utilisée pour indiquer notamment le succès ou l’échec dʹune interaction avec la base.  
 
Lʹaccès  à  lʹinformation  de  cette  zone  dépend  du  mode  de  fonctionnement  du  moteur  SGBD. 
Avec ORACLE , il y a deux modes disponibles : ORACLE et ANSI. En mode ORACLE, la zone 
de communication est incluse par un EXEC SQL INCLUDE SQLCA qui comprend notamment 
les  structures  pour  ranger  le  code  de  retour  de  la  dernière  clause  SQL  exécutée  et  des 
informations  comme  le  message  de  lʹexception  et  le  texte  de  cet  ordre.    Le  code  de  retour  est 
accessible  de  plusieurs  manières  à  lʹapplication,  selon  la  version  utilisée.  Ainsi,  il  peut  être  lu 
directement  en  faisant  référence  à  la  struct  sqlca.sqlcode  ou  par  la  variable  symbolique 
SQLCODE définie dans une application C  par la directive suivante : 
#define SQLCODE sqlca.sqlcode
 
En  mode  ANSI,  le  code  de  retour  est  accessible  par  la  variable  SQLCODE  (ISO‐86)  ou  par 
SQLSTATE  (ISO‐92).  Avec  la  norme  SQL/86,  la  variable  SQLCODE  était  utilisée  pour  donner 
accès  à  une  seule  partie  de  cette  information,  mais  la  norme  SQL/92    a  défini  une  nouvelle 
structure plus riche en informations appelée DIAGNOSTICS permettant au concepteur de traiter 
les  cas  dʹerreur  avec  plus  de  détails.  La  variable  SQLCODE  associée  à  la  zone  SQLCA  est 
cependant  encore  supportée  pour  des  fins  de  compatibilité.  Les  deux  variables,  SQLCODE  et 
SQLSTATE  (en  majuscules)  doivent  être  déclarées  dans  une  application  L3G  qui  les  utilisent 
comme variables hôtes dans le traitement des exceptions. La première comme un entier long et 
lʹautre comme une chaîne pour y loger 6  caractères incluant le caractère de fin de chaîne. 

 
Chapitre 9 Interface applicative  10

Variable SQLSTATE  (ISO SQL/92)  
Cʹest une variable de type caractère : deux premiers caractères pour la classe de l’exception et les 
trois autres caractères pour la sous‐classe. Le sixième caractère est celui de la terminaison de la 
chaîne. 
Codes normalisés pour la variable SQLSTATE : 
  00 000  Succès du dernier ordre DML exécuté 
  01 xxx  Avertissement d’une condition particulière 
  01 003  Valeurs NULL ignorées avec une fonction d’agrégation 
  02 000  Aucun tuple traité (réponse vide) 
  23 xxx  Violation dʹune contrainte de la base de données. 
  40 xxx  Reprise forcée pour résoudre une impasse 
 
En  langage  C,  la  variable  SQLSTATE  est  déclarée  comme  une  chaîne  de  longueur  6,  car  elle 
comprend le délimiteur de fin de chaîne ʹ\0ʹ. 
Zone DIAGNOSTICS (mode ANSI) 
Cette zone est une structure de données du programme hôte (ex. struct de C) composée d’un en‐
tête et de plusieurs instances d’une zone dite de détails (Figure 9.1). Cette information n’est pas 
essentielle,  mais  elle  permet  à  lʹapplication  de  traiter  plus  finement  les  erreurs.  Toutefois,  la 
variable  SQLSTATE  est  toujours  valuée  en  conjonction  avec  la  zone  DIAGNOSTICS.  Ainsi  le 
développeur  a  une  information  plus  complète  pour  effectuer  une  analyse  plus  fine  des 
exceptions. 
 
struct DIAGNOSTICS
{en-tête {
NUMBER
MORE
COMMAND_FUNCTION
detail-1
{char SQLSTATE[6]
detail-2
{
RETURNED_SQLSTATE
MESSAGE_TEXT
detail-3
. . . }
 
Structure de la zone Diagnostics 
Figure 9.1 

 
Chapitre 9 Interface applicative  11

Gestion des exceptions en SQL/92 
L’accès à la zone DIAGNOSTICS se fait par une première instruction SQL pour récupérer l’en‐
tête et par une deuxième pour obtenir une sous‐zone de détails. Il y a autant de sous‐zones de 
détails qu’il y a d’erreurs. En effet, il peut arriver quʹun même ordre DML  génère, au moment 
du  Commit,  une  suite  dʹerreurs  dont    chacune  découle  des    vérifications  des  contraintes 
dʹintégrité de la BD définies sur les attributs.  
 
EXEC  SQL  GET  DIAGNOSTICS  :nb  zone  =  NUMBER,  :more  =  MORE,  :commande  = 
COMMAND_FUNCTION; 
où:  
NUMBER :nombre de zones de détails (autant d’erreurs générées par un ordre); 
COMMAND_FUNCTION :  le texte du DML en erreur;   
MORE :  le reste de l’information de la  zone; 
 
EXEC  SQL  GET  DIAGNOSTICS  EXCEPTION  :i  :code  =  RETURNED_SQLSTATE,  :message  = 
MESSAGE_TEXT; 
 
Où    :i  est une variable du langage hôte spécifiant le numéro de la zone de détails à lire. 
  :code    est  aussi  une  variable  hôte  où  est  rangé  le  code  d’erreur  (5  caractères  plus  un 
  caractère de fin de chaîne). 
  :message  est le texte du message d’erreur. 
 
Les mots en minuscules sont des variables C déclarées dans le programme, tandis que les mots 
en  majuscules  sont  des  éléments  de  la  struct  définie  dans  un  fichier  en‐tête  (.h)    de  la  zone 
DIAGNOSTICS.  
Utilisation de la zone SQLCA ( en mode ORACLE et ISO SQL/86) 
La  structure  SQLCA  est  utile  au  SGBD  pour  transmettre  à  l’application  toute  information 
pertinente concernant l’exécution du dernier ordre exécuté par l’application en cours. Cette zone 
est  intégrée  à  l’application  par  EXEC  SQL  INCLUDE  SQLCA  qui  est  un  ordre  déclaratoire, 
remplacé  à  la  compilation  par  une  struct  C  nommée  sqlca  et  peut  être  définie  dans  un  fichier 
SQLCA.h  dont  les  divers  éléments  représentent  notamment  l’erreur  détectée,  le  succès  du 
dernier ordre exécuté et le nombre de tuples traités par celui‐ci.  
 
#define SQLCODE sqlca.sqlcode
struct sqlca
{
char sqlcaid[8]; /*nom de la struct soit sqlca
long sqlabc ; /* longueur de la struct sqlca en octets
long sqlcode; /* code de retour de Oracle:
0 pour succès
<0 terminaison anormale du DML
+1403 NO_DATA_FOUND -réponse vide */
struct {

 
Chapitre 9 Interface applicative  12

unsigned short sqlerml; /* longueur du message */


char sqlrrmc[70]; /* texte du message */
} sqlerrm;
char sqlerrp[8]; /* inutilisé */
long sqlerrd[6]; /* 6 entiers pour le statut
SQLERRD(3) nombre de tuples
SQLERRD(5)position d'une erreur de syntaxe*/
char sqlwarn[8];/* vecteur de 8 indicateurs
SQLWARN(1) un indicateur est actif
SQLWARN(2) indique la troncature d'une chaîne
lors de l'affectation à une variable
SQLWARN(4) absence d'un var. dans liste INTO
SQLWARN(5) DELETE et UPDATE sans WHERE
char sqltext[8]; /* inutilisé */
}
 9.2 

SQLCODE :  (variable déclarée long en C et incluse dans le fichier SQLCA.h) 
0 :  cette valeur  signifie le succès pour l’exécution de l’ordre. 
‐  :  une  valeur  négative  signifie  la  détection  d’une  erreur  avec  le  texte  placé  dans  sqlerrm  et   
activation de lʹerreur pré‐définie SQLERROR. 
+ : une valeur positive signifie l’bsence d’une erreur. Ce code signale cependant une condition 
particulière rencontrée à l’exécution : pour Oracle (1403) correspond  à aucun tuple traité, tandis 
que pour les autres SGBD cʹest le code100 . 
Déclaration de SQLCODE 
Le code de retour pour chaque ordre SQL exécuté est placé dans la struct sqlca et est accessible 
directement par lʹapplication via la variable L3G pointée sqlca.sqlcode. Une autre façon dʹavoir 
accès au même code de retour est de définir une variable symbolique  SQLCODE de type long 
(en majuscules) qui sera initialisée automatiquement en mode ANSI par le SGBD et cela avec le 
code de retour placé dans la struct sqlca.sqlcode.  
Contrôle du DML avec  la zone SQLCA (SQL /ISO‐86) 
Deux façons sont proposées pour exploiter cette zone. La première consiste à vérifier lʹélément 
de la structure SQLCA qui contient le code de retour et cela,  après chaque exécution d’un ordre 
DML. La deuxième utilise une instruction déclaratoire caractérisée par le WHENEVER qui met 
en  place  une  exception  qui  est  activée  immédiatement  par  le  comportement  exceptionnel  dʹun 
ordre DML qui retourne un code SQLCODE différent de zéro. 
Test de SQLCODE 
La  première  méthode  consiste  à  tester  le  code  de  retour  après  chaque  accès  à  la  BD  et  de 
contrôler les accès itératifs à la BD au moyen du même code de retour, utilisé soit par lʹentremise 
dʹune variable L3G qui donne accès à la struct, soit par une variable symbolique.  
...
EXEC SQL SELECT qte INTO :v_quantite FROM Ventes;

 
Chapitre 9 Interface applicative  13

IF (SQLCODE != 0) GOTO SORTIE -- TEST DU CODE DE RETOUR


{ -- si succès poursuivre
...
}
...
exit(0); -- sortie avec une exécution normale
SORTIE: ROLLBACK;
exit(1); -- sortie avec une détection d’erreur
}

Il  est  aussi  possible  de  tester  le  code  directement  en  utilisant  lʹélément  sqlcode  de  la  structure 
initialisée après chaque interaction avec la base de données. 
...
EXEC SQL SELECT qte INTO :v_quantite FROM Ventes;
IF (sqlca.sqlcode != 0) GOTO SORTIE --TEST DU CODE DE RETOUR
{ -- si succès poursuivre
...
}
...
exit(0);
SORTIE: ROLLBACK;
exit(1);
}
 
Mise en place automatique des exceptions 
La deuxième forme de contrôle du code de retour a l’avantage de nécessiter moins de code dans 
lʹapplication et de simplifier la logique du traitement des exceptions. Il sʹagit dʹutiliser la clause 
déclaratoire  WHENEVER  de  ProC  à  partir  de  laquelle  lʹexception  est  mise  en  place  et  le 
demeure tant quʹune autre nʹest pas redéfinie ou annulée.  
 
EXEC SQL WHENEVER <exception>  <action>; 
 
Cette  clause  SQL  génère  par  lʹentremise  du  précompilateur  un  test  de  SQLCODE  ou  de 
SQLSTATE après chaque interaction avec le SGBD  distant. 
Exceptions :
SQLERROR, activée seulement lorsque la valeur de sqlcode est  négative (erreur détectée 
dans le fonctionnement de lʹapplication, du SGBD ou du réseau); 
SQLWARNING, activée lorsque le sqwarn0 est basculé; 
NOT FOUND (NO_DATA_FOUND), activée lorsque SQLCODE est positif et égal à 100 
ou  1403  (Oracle).  Cela  correspond  à  une  situation  exceptionnelle  qui  nʹest  pas  un 
fonctionnement erroné du SGBD. 
Actions :
GOTO erreur  pour un branchement à l’étiquette (ou label) erreur; 

 
Chapitre 9 Interface applicative  14

CONTINUE  pour  ignorer  la  condition  d’erreur  ou  désactiver  lʹexception  courante  et 
passer à l’instruction suivante. 
STOP pour arrêter l’exécution du programme et lancer une reprise. 
DO pour exécuter un bloc dʹénoncés. 
 
Voici  un  exemple  dʹune  application  utilisant  le  WHENEVER  de  ProC qui  ne  fait  pas  appel  à 
plusieurs tests IF : 
EXEC SQL BEGIN DECLARE SECTION
... -- variables utilisées dans les clauses SQL
EXEC SQL END DECLARE SECTION;
main()
{
...
EXEC SQL WHENEVER SQLERROR GOTO ERREUR1;
/*tout sqlcode < 0 retourné après chaque interaction,active cette
exception dont le traitement consiste à faire un branchement automatique
*/
...
EXEC SQL CONNECT :schema IDENTIFIED BY :motpasse;
...
EXEC SQL WHENEVER NOT FOUND GOTO ERREUR2;
-- remplacement de l'exception par une autre
...
exit(0);
ERREUR1: EXEC SQL WHENEVER SQLERROR CONTINUE;
--inhibe les exceptions
printf("connexion incorrecte \n");
exit(1);
ERREUR2: EXEC SQL WHENEVER SQLERROR CONTINUE;
--inhibe les exceptions subséquentes
printf("Aucune donnée reçue \n");
exit(1);
}
La clause Whenever allège le programme en évitant de faire un test IF avec le SQLCODE et cela, 
après chaque lecture, écriture ou connexion avec le SGBD. 
9.3 Programmation SQL 
Il y a plusieurs approches de programmation L3G pour interagir avec la BD en utilisant SQL 2 :  
1) SQL intégré     2) SQL dynamique    3) API et ODBC 
 

 
Chapitre 9 Interface applicative  15

9.3.1 SQL intégré  dans une application hôte L3G 
Cette approche a été proposée en premier par la norme ISO SQL/86 et redéfinie avec celle de ISO 
SQL/92.  Elle  est  généralement  offerte  par  tous  les  systèmes  SGBD  pour  programmer  une 
application  L3G  avec  lʹinsertion  des  clauses  SQL  (embedded  SQL).  Les  ordres  SQL  sont 
directement  intégrés  dans  l’application  en  les  préfixant  par  un  délimiteur  syntaxique  (EXEC 
SQL). Un précompilateur (exemple ProC) transforme l’ordre SQL en un appel de fonction de la 
librairie fournie par le SGBD. Le traitement des erreurs peut être fait (selon la version du SGBD) 
par la variable SQLCODE qui réfère à la zone de communication, i.e. à la structure SQLCA ou 
dans certains cas, selon la norme SQL/92 via la zone DIAGNOSTICS et la variable SQLSTATE. 
Cette dernière variable est utilisée notamment par lʹODBC.  
Processus de développement avec le SQL intégré 
Normalement,  lʹapplication  suffixée  avec  .pc  est  précompilée  par  le  programme  PROC,  soit  la 
version  correspondant  au  langage  de  programmation  utilisé  pour  son  développement.  La 
procédure est la suivante :   
 
a)Développement du programme source (application) en incluant les ordres SQL. Ceux‐ci sont 
validés au préalable en mode autonome avec lʹinterpréteur SQL *Plus du SGBD Oracle.   
 
 
  Pgm C + SQL
 
 
 
Pré compilateur SQL PRO‐C
 
 
 
  Compilateur C Librairie 
  SQL
 
  Edition des liens
 
  Base  de 
  Application .exe données 
 
 
Développement en C  avec le SQL intégré 
 9.3 
 
b) Précompilation du programme source; le résultat est un module source  suffixé .c 
c)  Compilation  standard  du  source  modifié  par  la  précompilation  aboutissant  au  programme 
objet suffixé .o ; exemple : cc pgm.c   ‐pgm.o 

 
Chapitre 9 Interface applicative  16

d)  Édition  des  liens  pour  inclure  les  procédures  à  partir  des  librairies  SQL  afin  dʹobtenir  le 
module  exécutable  (suite  à  la  résolution  des  références  externes  (nom  des  procédures  et  des 
variables)). 
Le  schéma  de  développement  peut  varier  légèrement  d’un  système  à  l’autre.  Ainsi,  il  se  fait 
différemment avec le SGBD DB2 de IBM. Il y a une étape supplémentaire qui tient au fait que le 
plan d’exécution des ordres de l’application est compilé et non interprété.    
Caractéristiques de cette approche 
Une clause SQL utilisée dans ce contexte peut faire référence à une variable du langage hôte et 
inversement. Notez l’usage du préfixe (:) de la variable utilisée dans l’ordre SQL. Une variable 
préfixée  indique  au  précompilateur  qu’il  y  a  référence  dans  l’ordre  SQL  à  une  variable  du 
langage  hôte  et  non  à  un  attribut  du  schéma  de  la  base.  Toutefois,  cette  variable  est  spécifiée 
(déclarée)  dans  le  langage  hôte  sans  les  ʺ:ʺ  ,  puique  le  compilateur  du  langage  hôte  ne 
l’accpterait pas comme un variable.  
Exemple en SQL intégré ( avec un curseur implicite) 
Voici  un  exemple  d’un  programme  utilisant  les  variables  hôtes  et  SQL  avec  un  test  IF  du 
SQLCODE effectué après chaque interaction. Il y a calcul de la marge de profit et mise à jour de 
la table CaisseProfit. Lorsque dans un lot il y a un article qui est absent, toutes les mises à jour 
sont annulées par un ROLLBACK. 
 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <sqlca.h> 
#define SQLCODE sqlca.sqlcode 
/* Déclaration des variables L3G utilisées dans les enoncés SQL */ 
EXEC SQL BEGIN DECLARE SECTION; 
char schema[10], motpasse[15]; 
int v_cout; 
int  v_prix; 
char nom_article[20]; 
EXEC SQL END   DECLARE SECTION; 
/*Inclusion de la structure SQLCA*/ 
EXEC SQL INCLUDE SQLCA; 
main() 

/* Connexion à la base de données Oracle */ 
printf(ʺ\nDonnez le nom du schéma de la base ʺ); 
gets(schema); 
printf(ʺ\nDonnez le mot d’accès à la base ʺ); 
gets(motpasse); 
EXEC SQL CONNECT :schema IDENTIFIED BY :motpasse; 
if(SQLCODE !=0 ) exit(1); /* Test du code de retour : échec */ 
printf(ʺ\nEntrez le nom de l’article ʺ); 

 
Chapitre 9 Interface applicative  17

gets(nom_article); 
While (nom_article != "") –fin lot si nom_article est "0"
  {/* Valuation des variables hôtes :nom_article et :v_cout */ 
EXEC SQL  SELECT  cout, prix  INTO :v_cout, :v_prix  
FROM PieceInv  
WHERE article =:nom_article;      ‐‐ article est de type char(20); 
if(SQLCODE == 0)  
  {printf(ʺ\n%s %d\nʺ, nom_article, v_cout, v_prix); 
EXEC SQL UPDATE CaisseProfit SET nom_article =:nom_article, prix = :v_prix ‐ v_cout); 
}
else {EXEC SQL ROLLBACK;
exit(1);}
printf("\nEntrez le nom de l’article ");
  gets(nom_article); 
}
EXEC SQL COMMIT [WORK RELEASE] ; 

 
Chaque ordre DML débute avec le EXEC SQL et se termine par un point virgule. L’exécution de 
l’ordre retourne un code qui est testé avec la variable symbolique SQLCODE. Ce code de retour 
est  placé  dans  la  zone  SQLCA  (soit  dans  la  structure  définie  dans    SQL  Communication  Area 
conformément  à  la  norme  ISO  SQL/86  ou/et  dans  les  zones  de  SQLSTATE  de  la  zone 
DIAGNOSTICS  de  l’ISO  SQL/92.  Lorsque  plusieurs  tuples  sont  retournés  par  l’exécution  d’un 
ordre,  le  mécanisme  du  curseur  est  utilisé  pour  traiter  chacun  d’entre  eux.  La  lecture  avec  un 
curseur sera étudiée plus loin dans ce chapitre. 
 
Il peut arriver que l’attribut d’une table ait un type différent de celui qu’une application utilise 
pour le traitement des données. Ainsi un attribut de chaîne de caractères de longueur fixe peut 
être lu et stocké dans une variable hôte de longueur variable. Dans ce cas, c’est l ‘application qui 
est chargée de faire la conversion exigée par le type de la variable hôte. Voici un exemple dans 
lequel le nom de l’article est rangé dans la base comme une chaîne de longueur fixe, mais cette 
valeur doit être manipulée comme une variable VARCHAR().  
Exemple d’une application avec SQL  intégré (ISO SQL/86) 
/*Lecture du bénéfice doublé pour un article spécifié à partir de la table Inventaire*/ 
#define SQLCODE sqlca.sqlcode
#include <stdio.h>
#include <string.h>
#include <sqlca.h>
EXEC SQL BEGIN DECLARE SECTION;
char schema[10], motpasse[15];
int prix;
varchar nom_article[20];
char nom_article1[20];

 
Chapitre 9 Interface applicative  18

EXEC SQL END DECLARE SECTION;


EXEC SQL INCLUDE SQLCA;
main(){
int marge_bon = 0;
/* Connexion à la base de données Oracle */
. . .
printf("\nEntrez le nom d'article ");
gets(nom_article1);
strncpy((char*)nom_article.arr,nom_article1,20);
nom_article.len = strlen((char*)nom_article.arr);
EXEC SQL
SELECT prix_vente INTO :prix
FROM Inventaire
WHERE article = :nom_article;
if( SQLCODE == 0)
{marge_bon = prix * 2;
printf("\n %s %d", nom_article.arr, marge_bon);
EXEC SQL
UPDATE Inventaire SET prix_vente = prix_vente + :prix,
cout = cout * 2 WHERE article = :nom_article;
if(SQLCODE !=0 )
{EXEC SQL ROLLBACK WORK; -- suppression des bonifications faites
exit(1);}
else printf("\nCet article n'est pas dans l'inventaire\n");
EXEC SQL COMMIT WORK;
exit(0); - fin normale du pgm
}
 
Quelques remarques sur ce programme simple : 
Cette recherche ne peut retourner qu’un seul tuple, car le prédicat de recherche utilise la clé de 
la table Inventaire. Dans le cas contraire, il y aura une erreur, car aucun curseur n’est utilisé par 
l’application. 
Le  code  de  retour  SQLCODE  est  testé  en  principe  après  chaque  DML  et  cela,  pour  vérifier 
lʹexécution correcte du DML. 
Le  COMMIT  WORK  est  identique  au  COMMIT  au  plan  de  la  sémantique.  La  syntaxe  des 
normes récentes impose le WORK et éventuellement le RELEASE pour forcer la suppression des 
verrous associciés aux données. Ex. : COMMIT WORK RELEASE. 
Gestion des erreurs avec SQL/92 
L’application précédente est reprise avec le DML de SQL/92 qui utilise la variable SQLSTATE et 
la zone DIAGNOSTICS. La variable SQLSTATE est mise à jour par le SGBD à partir de la zone 
DIAGNOSTICS  qui est définie et incluse automatiquement dans lʹapplication. 
 
/*lecture de la marge bénéficiaire sur article spécifié */
#include <stdio.h>

 
Chapitre 9 Interface applicative  19

#include <string.h>
#define OK "00000"
EXEC SQL BEGIN DECLARE SECTION;
/* déclaration des var. L3G utilisées dans SQL*/
char schema[10], motpasse[15];
int marge;
char nom_article[4];
/* variables pour le traitement de l’exception */
char SQLSTATE[6] /* mis à jour par le SGBD après chaque SQL */
char commande_fonct[128];
int nb_tuples, nombre, i, no_cond;
char message[128];
etat_sqlstate[6]; /*pour ranger le code d'erreur lors d'une
gestion plus détaillée de l'erreur */
EXEC SQL END DECLARE SECTION;
main()
{int marge_bon;
printf("\n donnez le nom du schéma: ");
gets(schema);
printf("\n fournir le mot de passe: ");
gets(motpasse);
EXEC SQL CONNECT :schéma IDENTIFIED BY :motpasse;
/*aucune gestion erreur pour cet ordre */
printf("\n entrez le nom de l'article: ");
gets(nom_article);
EXEC SQL SELECT article, prix - cout INTO :nom_article, :marge
FROM Inventaire
WHERE article = :nom_article;
marge_bon = marge * 2;
if (!(strcmp(SQLSTATE, OK))) /*succès avec var.SQL/92 */
printf("%s %d \n", nom_article, marge_bon) ;
else --détails de l'exception avec la struct DIAGNOSTICS
{
printf("\n le code d’exception est %s\n", SQLSTATE);
EXEC SQL GET DIAGNOSTICS :nombre = NUMBER,
:commande_fonct = COMMAND_FUNCTION, :nb_tuples = ROW_COUNT;
printf("\n instruction courante est %s\n", commande_fonct);
printf("\n le nombre de tuples trouvés est %s \n", nb_tuple);
/* devrait être 1 */
printf("\n le nombre d'erreurs est %d \n", nombre);
for (i = 1; i <= nombre; ++i)
{
EXEC SQL GET DIAGNOSTICS EXCEPTION :i
:no_cond = CONDITION_NUMBER,
:etat_sqlstate = RETURNED_SQLSTATE, :message = MESSAGE_TEXT;

 
Chapitre 9 Interface applicative  20

printf("erreur no: %d %d \n", i, no_cond);


printf("\n le message est le suivant: %s \n", message);
}
}
EXEC SQL COMMIT WORK; -- idem à COMMIT
}
 
Pour simplifier la structure du programme, il est aussi possible de gérer les cas dʹexception par 
l’appel  d’une  fonction  C  qui  contient  lʹordre  EXEC  SQL  GET  DIAGNOSTICS.  Il  en  serait  de 
même avec la mise en place dʹune exception WHENEVER  SQLERROR. Il serait alors possible 
de faire directement  un branchement à la zone DIAGNOSTICS.  
Autre exemple avec SQL utilisant WHENEVER  
/*Programme  pour  lire  un  no_usine  et  afficher  la  ville,  la  capacité  de  stockage  et  la  surface  à 
partir de la table Usine */ 
/* Pour arrêter le programme l'utilisateur doit entrer 99999
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <oraca.h>
EXEC SQL BEGIN DECLARE SECTION;
char nom_utilisateur[20];
char motpasse[20];
int no_usine_v;
varchar ville_v[20];
int capacite_v;
int surface_v;
EXEC SQL END DECLARE SECTION;
EXEC SQL INCLUDE SQLCA;
int nb_total_quest;
int quitte;
int i;
void sql_erreur(void); /*Decl traitement des erreurs */
void main()
{
nb_total_quest = 0;
#define quitte "99999"
. . .
/* Activation de l'exception et appel de sql_erreur() */
EXEC SQL WHENEVER SQLERROR DO sql_erreur();
EXEC SQL CONNECT :nom_utilisateur IDENTIFIED BY :motpasse;
printf("\nConnecté sous le compte %s", nom_utilisateur);
printf("\nEntrez un numero usine(99999 pour quitter)");
scanf("%d", &no_usine_v);

 
Chapitre 9 Interface applicative  21

while( no_usine != quitte) {


EXEC SQL
SELECT ville, capac_stock INTO :ville_v, :capacite_v
FROM Usine
WHERE no_usine = :no_usine_v;
printf("\n\t ville\t Capacite\t \n");
printf("\t---\t-------- \n");
printf("\n%12s\t%8d\t%7d\n", ville_v.arr, capacite_v);
nb_total_quest ++;
printf("\nEntrez un numero usine(99999 pour quitter)");
scanf("%d", &no_usine_v);
}
printf("\nTotal des tuples affichés %d.\n", nb_total_quest);
printf("\nAu plaisir\n");
EXEC SQL COMMIT WORK RELEASE;
exit(0);
}
/* Routine de traitement d'erreur*/
void sql_erreur() {
EXEC SQL WHENEVER SQLERROR CONTINUE;
printf("\n erreurs détectées");
printf("\nLe type de l'erreur: %.70s\n",
sqlca.sqlerrm.sqlerrmc);
printf("\nNombre de tuples affichées: ",sqlca.sqlerrd[2]);
printf("\nErreur rencontrée sur la ligne %d of
%.*s.\n",oraca.oraslnr,oraca.orasfnm.orasfnml,oraca.orasfnm.
orasfnmc);
printf("\nDernièrecommande
SQL:%.*s\n",oraca.orastxt.orastxtl, oraca.orastxt.orastxtc);
Exit (1) ;
EXEC SQL ROLLBACK RELEASE;
exit(1); }
 
Curseur 
La  recherche  et  l’obtention  d’un  seul  tuple  avec  le  SQL  intégré  ne  poseaucun  problème 
particulier au niveau de la gestion de la réponse puisque le SGBD prend à sa charge la gestion 
du seul tuple trouvé dans la base et cela, en créant un curseur implicite quʹil ouvre et qu’il gère 
pour  et  au  nom  de  lʹapplication.  Chaque  valeur  est  mise  en  correspondance  avec  une  variable 
déclarée de l’application. 
Lorsquʹune recherche ou une autre action DML (insert , update, delete) sur la base fournit une 
réponse  de  plusieurs  tuples,  le  SGBD  stocke  cette  réponse  complète  dans  un  espace  appelé 
espace de curseur (fourni sous forme dʹun context area) et, sur demande de l’application (par un 
ordre  FETCH),  retourne  à  l’application  un  seul  tuple  à  la  fois.  Pour  obtenir  ces  tuples, 
l’application  doit  donc  exécuter  lʹordre  DML  FETCH  autant  de  fois  quʹil  y  a  de  tuples  dans  la 

 
Chapitre 9 Interface applicative  22

réponse.  Au  niveau  de  l’application,  le  curseur  est  déclaré  par  un  DECLARE  CURSOR,  qui 
définit un espace dans la zone PGA de la mémoire partagée de Oracle pour y ranger le texte de 
lʹordre SQL ainsi que les attributs corrspondant aux variables hôtes utilisées dans la clause SQL. 
Pour une application, le curseur est un mécanisme qui permet le traitement, un à un, des tuples 
de la réponse à une requête SQL  3. Cet ensemble réponse est le ʺactive setʺ ou le ʺresult setʺ dans 
la terminologie de lʹODBC, lequel ensemble est rangé au niveau du serveur.  
Curseur implicite et explicite 
Un curseur implicite nʹest pas déclaré dans une application; il est créé automatiquement par le 
SGBD  dès  lors  quʹun  seul  tuple  est  attendu  dans  la  réponse.  Si  la  réponse  contient  plus  dʹun 
tuple,  il  y  a  erreur.  Dans  le  cas  d’une  réponse  à  plusieurs  tuples,  le  curseur  explicite  doit  être 
déclaré et géré par lʹapplication. Celle‐ci doit lʹouvrir, le parcourir tuple par tuple et le fermer.  
 
Traduction dʹun ordre SQL et le rôle du curseur (Oracle) 
L’ouverture d’un curseur C1 par l’application lance l’opération de traduction et dʹexécution de 
l’ordre SQL associé au curseur.  
                   
                  
  ZMP(SGA)  SQL ou DML Active set  Espace  curseur  OC1 
  source (tuples    de  la  du curseur Curs1 
  SQL  ou  DML  réponse) 
  traduit (plan) 
  Pointeur(1 er)  

  Active set (suite) 
  (réponse) 
  Au niveau du serveur : 
   
  Réponse  formée  par  les  tuples 
  trouvés par le SGBD.  Active set (suite) 
    (réponse) 
 
 
 
Exemple de l’implémentation du curseur 
 9.4 
 
Avec Oracle, le texte de l’ordre SQL est transmis au serveur par l’application. Sur réception, le 
serveur de processus du SGBD alloue un espace dans le PGA  pour y ranger le texte de lʹordre et 
réserver  lʹespace  nécessaire  pour  y  placer  un  premier  tuple  de  lʹensemble  réponse  (USER  
CURSOR (UC), le Result Set).  
 
De plus, le module du serveur place une copie miroir de l’ordre dans un espace du PGA de la 
ZMP, appelé Oracle CURSOR (OC). À partir du curseur OC, l’ordre est traduit en métacode et 
rangé  aussi  dans  le  USER  CURSOR.  Ensuite,  l’exécution  est  lancée.  L’ensemble‐réponse  (active 

 
Chapitre 9 Interface applicative  23

set) calculé est formé de tuples ou de ROWID selon que la recherche fait appel à  une jointure ou 
qu’elle  fait  référence  à  une  seule  table.  Les  tuples  de  la  réponse  sont  stockés  dans  une  liste  de 
blocs, créée par lʹexécuteur, soit le curseur OC. La réponse est toujours calculée entièrement par 
le module du SGBD et les tuples qui la constituent sont placés dans un espace désigné par un 
pointeur sur OC. Si le nombre de tuples de la réponse l’exige, le premier curseur OC sera chaîné 
avec d’autres qui sont alloués dynamiquement par l’exécuteur (module du noyau). Finalement, 
le  premier  tuple  de  la  réponse  est  placé  dans  le  curseur  UC    du  PGA  et  sera  retourné  à 
l’application  distante  sur  lʹexécution  dʹun  premier  FETCH.  Évidemment,  il  faut  éviter  les 
opérations de balayage complet d’une table (full table scan) afin de ne pas avoir à créer un grand 
espace de curseur qui peut éventuellement exiger une écriture dans les fichiers de travail définis 
sur disque. Les espaces curseurs sont libérés par lʹexécution de lʹordre CLOSE C1. 
 
Le  curseur  UC  est  lié  à  un  seul  ordre  DML  dʹune  application.  Lorsque  le  curseur  est  associé  à 
une  clause  SQL  définie  sur  une  seule  table,  il  peut  être  utilisé  dans  une  clause  de  mise  à  jour. 
Dans  ce  cas,  le  tuple  courant  est  pointé  par  une  adresse  ROWID  et  celle‐ci  est  utilisée  pour 
accéder au tuple qui est le tuple à mettre à jour. 

Du seul point de vue de l’application, un curseur est un espace local dans lequel la requête SQL 
est rangée et nommée par un nom de curseur. Dans l’exemple de la section suivante, le curseur 
c1  est  associé  l’espace  nécessaire  pour  ranger  le  SELECT.  Par  extension  de  la  notion,  elle  est 
finalement associée à la requête SQL elle‐même.  
Remarque sur le métacode  
Il  s’agit  de  l’ordre  source  accompagné  de  sa  version  traduite  et  optimisée.  Cette  version  est 
composée de toute l’information nécessaire pour une exécution correcte de l’ordre : présence et 
localisation  des  index,  substitution  des  alias,  etc.  La  forme  interne  du  métacode  n’est  pas  du 
code  Assembleur,  mais  une  suite  ordonnée  dʹappels  pour  consulter  un  index,  accéder  à  un 
fichier  de  pages  de  données,  etc.  Si  une  autre  requête  utilise  exactement  la  même  clause,  elle 
nʹaura pas besoin dʹêtre optimisée par lʹexécuteur, mais plutôt reprise directement sous sa forme 
compilée. 
 
Exemple dʹun programme en C utilisant un curseur explicite 
/*Ce programme ouvre et utilise un curseur pour lire tous les
tuples de la
table Dotation */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
EXEC SQL BEGIN DECLARE SECTION;
char username[20];
char password[10];
int no_usine_v;
int no_poste_v;
int nb_titulaire;

 
Chapitre 9 Interface applicative  24

EXEC SQL END DECLARE SECTION;


EXEC SQL INCLUDE SQLCA;
void sqlerror(void);
main()
{
/* Connexion à la base de données Oracle */
printf("\nCode Oracle ");
gets(username);
printf("\nFournir mot de passe ");
gets(password);
/* Activation de l'exception */
EXEC SQL WHENEVER SQLERROR DO sqlerror();
EXEC SQL CONNECT :username IDENTIFIED BY :password;
printf("\nConnexion ok a Oracle sous le compte %s\n", username);
/* Declaration du courseur*/
EXEC SQL DECLARE c1 CURSOR FOR
SELECT NO_USINE, NO_POSTE, NB_TITULAIRE FROM Dotation;
/* Calcul de la réponse lors de l’exécution du OPEN */
EXEC SQL OPEN c1;
printf("\nNo Usine No Poste Nb Titulaires\n");--en-tête
EXEC SQL FETCH c1 INTO :no_usine_v, :no_poste_v, :nb_titulaire;
While (sqlca.sqlcode != 1403) –test si la réponse contient des
tuples
{
printf("\n%d\t\t%d\t\t%d\n",no_usine_v,no_poste_v, nb_titulaire ;
EXEC SQL FETCH c1 INTO :no_usine_v, :no_poste_v, :nb_titulaire;
}
SORTIE:
EXEC SQL CLOSE c1;
EXEC SQL COMMIT WORK RELEASE;
exit(0);
}
void sqlerror()
{
EXEC SQL WHENEVER SQLERROR CONTINUE;
printf("\nOracle erreur:\n");
printf("\n%.70s\n", sqlca.sqlerrm.sqlerrmc);
EXEC SQL ROLLBACK WORK RELEASE;
exit(1);}
Options de visibilité du curseur  
Les options ci‐dessous ne sont pas toutes implémentées dans les SGBD, bien que proposées dans 
la norme SQL‐92.  
 

 
Chapitre 9 Interface applicative  25

‐INSENSITIVE  :  les  tuples  de  la  réponse  sont  placés  dans  le  curseur  explicite  OC  défini  par  le 
système lors de l’ouverture du curseur. Ils sont accessibles à l’application sans refléter les mises 
à  jour  subséquentes  faites  par  l’application  elle‐même  ou  par  dʹautres  applications  qui  font 
directement des  mises à jour dans les tables de base. La réponse calculée est déconnectée de la 
BD et en est plus le reflet en temps réel. Pour réaliser cet isolement du curseur, les tables de base 
référées par la clause FROM de lʹordre sont dupliquées au besoin et le calcul de la réponse est 
effectué à partir de celles‐ci. Toutefois, la table de base dʹoù proviennent les tuples de la réponse 
peut  toujours  être  mise  à  jour  directement  par  un  ordre  UPDATE,  mais  ces  changements  ne 
seront pas visibles à travers le curseur déclaré. Un tel curseur peut référer à plusieurs tables de 
base, mais dans ce cas il ne peut pas y avoir une mise à jour. Cʹest le cas lorsque le curseur est 
associé à une jointure. 
 
‐  FOR  UPDATE  :  les  tuples  de  la  réponse  reflètent  la  mise  à  jour  faite  par  un  ordre  UPDATE 
subséquent,  exécuté  par  lʹapplication  propriétaire.  Pour  implémenter  cette  option,  la  clause 
SELECT doit être suffixée par le FOR UPDATE. Le curseur C1 est modifiable que sʹil est défini 
sur une seule table tout comme une vue.  
 
La  mise  à  jour  dʹune  table  de  base  avec  un  curseur  est  faite  par  le  DML  UPDATE  …WHERE 
CURRENT OF C1 pour signifier que le tuple courant du curseur C1 est celui dans la BD qui est 
lʹobjet de la mise à jour. Dans un tel cas, le curseur ne comprend que les ROWID des tuples qui 
forment  la  réponse  et  cʹest  avec  ce  rowid  que  le  SGBD  accède  directement  à  la  page  pour 
effectuer la mise à jour demandée. 
                                  
‐ FOR SCROLL : cette spécification, nouvelle dans le SQL/92, permet de parcourir les tuples de 
la réponse en avant et en arrière (déplacement relatif).  
 
‐ FOR READ ONLY : les tuples de la réponse ne sont lʹobjet que de lecture seulement. Lʹusage 
du WHERE CURRENT OF. 
Indicateur de variable 
Chaque  variable  du  langage  hôte  utilisée  dans  un  DML  peut  être  associée  à  un  indicateur  de 
variable  pour  véhiculer  une  information  complémentaire  concernant  la  donnée  placée  dans  la 
variable hôte. Cet indicateur de variable doit être aussi déclaré comme une variable hôte globale 
de type entier (donc déclaré dans le BEGIN DECLARE SECTION) et est manipulé comme toute 
autre variable du L3G.  
 
Cet  indicateur  est  particulièrement  utile  pour  le    traitement  des  valeurs  nulles.  En  effet,  si  la 
valeur  dʹune  variable  hôte  doit  être  mise  à  nulle,  quel  est  lʹentier  à  utiliser  pour  représenter  le 
null?  Comment  communiquer  au  SGBD  que  lʹattribut  réfère  à  une  valeur  nulle?  Cette 
information  sera  passée  par  lʹentremise  de  lʹindicateur  de  variable  associé  à  la  variable.  De 
même, quelle valeur doit être affectée à la variable hôte dʹentrée lorsque lʹattribut lu est le null ?  
Le zéro ne peut pas être retenu systématiquement, car il peut être une valeur significative pour 
un  domaine  dʹattribut.  Cʹest  alors  que  lʹindicateur  fournit  à  lʹapplication  le  signal  nécessaire 
pour que la variable hôte soit interprétée comme une valeur nulle.  

 
Chapitre 9 Interface applicative  26

 
Exemple :   ... 
SELECT adresse INTO :adresse_locale:ind_adresse
FROM Employe;
if (ind_adresse == -1)
printf(" adresse est absente");
ELSE printf("adresse connue");
...
 
où :ind_adresse  est lʹindicateur de la variable adresse_locale, qui est déclaré comme un entier de 
2  octets.  Lʹindicateur  est  adjoint  au  nom  de  la  variable  associée  en  le  préfixant  du  caractère  ʹ:ʹ. 
Pour tester le contenu dʹun indicateur de variable il faut y avoir référé en le préfixant du nom de 
la variable. 
 
Lorsquʹil  sʹagit  dʹune  variable  qui  reçoit  une  donnée  de  la  BD  suite  à  une  lecture,  lʹindicateur 
représente lʹinformation suivante : 
si lʹindicateur de variable est : 
‐1: la valeur de lʹattribut associé est un null et le contenu de variable hôte est indéterminé; 
 0 : la variable hôte a une valeur du domaine de lʹattribut; 
> 0 : la variable hôte contient une valeur tronquée de lʹattribut.       
 
Exemple: si la variable age a un indicateur i_age: 
if ( i_age = 0 ) {   }  => la var. hôte a reçu une valeur qui n’est pas le null 
if ( i_age = ‐1 ) {  } => la variable hôte a reçu un null de la BD 
 
Lorsquʹil  sʹagit  dʹune  variable  dʹentrée  pour  BD,  i.e.  de  l’écriture  dans  la  base,  lʹindicateur 
représente lʹinformation suivante : 
si indicateur i_age associé à la variable age est : 
‐1 : le SGBD ignore le contenu de la variable et assigne un null à lʹattribut; 
>= 0 : le SGBD assigne la valeur de la variable hôte à lʹattribut. 
 
Dans  lʹordre  SQL,  lʹindicateur  de  variable  suit  la  variable  hôte  comme  cela  est  illustré  dans 
lʹordre FETCH ci‐dessous. 
 
/*Ce programme ouvre un courseur pour lire les tuples à partir la table
Ventes et utilise l'indicateur de variable pour modifier le contenu de
la table */
#define SQLCODE sqlca.sqlcode
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
EXEC SQL BEGIN DECLARE SECTION;
char username[20];
char password[10];

 
Chapitre 9 Interface applicative  27

char v_article[10];
int v_qte_tot;
char message[128];
short i_qte_tot; //indicateur de variable
EXEC SQL END DECLARE SECTION;
EXEC SQL INCLUDE SQLCA;
main()
{
printf("\nCode Oracle ");
gets(username);
printf("\nFournir mot de passe ");
gets(password);
EXEC SQL CONNECT :username IDENTIFIED BY :password;
/*Déclaration,traduction et calcul */
EXEC SQL DECLARE curseur_1 CURSOR FOR
SELECT article, qte_tot FROM Ventes WHERE nom = 'serge';
EXEC SQL OPEN curseur_1;
EXEC SQL FETCH curseur_1 INTO :v_article, :v_qte_tot:i_qte_tot;
/*Traitement et mise à jour possible avec l'indicateur de variable*/
while(SQLCODE == 0)
{
printf("\nL'article et la quantite sont %s %d\n", v_article,
v_qte_tot);
if(i_qte_tot == 0)
{
EXEC SQL UPDATE Ventes SET qte_tot = qte_tot+:v_qte_tot
WHERE nom = 'serge';}
else v_qte_tot = 9999;
{EXEC SQL FETCH curseur_1 INTO :v_article, :v_qte_tot:i_qte_tot;}
} -- fin de la boucle
EXEC SQL CLOSE curseur_1;
EXEC SQL COMMIT WORK;
exit(0);

Accès aux tuples du curseur explicite interne (OC)  
Après l’ouverture d’un curseur par un ordre OPEN, l’ordre DML est exécuté et les tuples de la 
réponse  sont  placés  dans  le  curseur  du  serveur  Oracle  (OC)  associé.  L’ordre  en  cours  se 
terminant correctement, l’ordre FETCH  de l’application permet d’accéder au premier tuple de 
la réponse et de placer la valeur des attributs dans les variables du langage hôte.  
 
Dans lʹexemple ci‐dessous, la variable indicateur nʹest pas utilisée pour la lecture et le contrôle 
est fait par la mise en place dʹune exception déclaratoire. 
 
/* Curseur explicite pour accéder aux tuples de la table Ventes */ 

 
Chapitre 9 Interface applicative  28

#define SQLCODE sqlca.sqlcode 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
EXEC SQL BEGIN DECLARE SECTION; 
char username[20]; 
char password[10]; 
char v_article[10]; 
int v_qte_tot; 
char message[128]; 
short i; 
EXEC SQL END DECLARE SECTION; 
EXEC SQL INCLUDE SQLCA; 
main() 

printf(ʺ\nCode Oracle ʺ); 
gets(username); 
printf(ʺ\nFournir mot de passe ʺ); 
gets(password); 
EXEC SQL WHENEVER SQLERROR goto ERREUR1; 
EXEC SQL CONNECT :username IDENTIFIED BY :password; 
EXEC SQL DECLARE curs_1  CURSOR FOR 
SELECT article, qte_tot FROM Ventes WHERE nom = ʹsergeʹ; 
EXEC SQL OPEN curs_1; 
EXEC SQL FETCH curs_1 INTO :v_article, :v_qte_tot; 
while(1) 
{EXEC SQL UPDATE Ventes SET qte_tot = qte_tot+ :v_qte_tot  
WHERE nom = ʹSergeʹ; 
printf(ʺLʹarticle et la quantite sont %s%d\nʺ, v_article, v_qte_tot); 
EXEC SQL FETCH curs_1 INTO :v_article, :v_qte_tot; 
if(sqlca.sqlcode == 1403) goto SORTIE; 

ERREUR1:printf(ʺErreur Oracleʺ); 
printf(ʺ\n%.70s\nʺ, sqlca.sqlerrm.sqlerrmc); 
exit(1); 
SORTIE: EXEC SQL CLOSE curs_1; 
EXEC SQL COMMIT WORK; 
exit(0); 

Avec  la  structure  itérative  du  WHILE,  chaque  transaction  effectuée  par  le  client  ʹSergeʹ  sera 
traitée  une  à  une.  En  raison  de  la  spécification  INSENSITIVE,  les  ventes  faites  à  ʹSergeʹ 
pourraient ultérieurement être mises à jour par la même application ou par une autre sans que 
cela modifie lʹensemble réponse déjà calculée, soit les tuples déjà visibles par le curseur curs_1. 

 
Chapitre 9 Interface applicative  29

Toutefois, à chaque fermeture et à chaque ouverture, la réponse est recalculée et les mises à jour 
deviennent  alors visibles à l’application.  
Curseur FOR UPDATE 
Lorsque le curseur est déclaré FOR UPDATE, les modifications effectuées par lʹapplication en se 
référant  au tuple  courant  du  curseur  (WHERE  CURRENT  OF  )  sont  possibles.  Pour  rendre les 
modifications  visibles  aux  autres  applications,  il  est  important  de  faire  périodiquement  et  dès 
que possible un COMMIT  et cela, afin de supprimer les verrous posés par le SGBD sur tous les 
tuples lus en anticipant leur mise à jour. 
 
Par exemple, le segment de code ci‐dessous traite une transaction dʹun client effectuant ainsi une 
mise jour de la table Ventes. La modification est visible dans le curseur nommé curs_1. Il s’agit 
de  remplacer  les  articles  de  type  ‘a1’  acquis  par  ʹsergeʹ  par  lʹarticle  ‘a9’.  La  mise  à  jour  est 
effectuée au moyen dʹun curseur FOR UPDATE. 
 
/*Accède aux tuples de la table Ventes et effectue une mise  
* à jour de la table avec le tuple courant du curseur courant */ 
#define SQLCODE sqlca.sqlcode 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
EXEC SQL BEGIN DECLARE SECTION; 
char username[20]; 
char password[10]; 
char v_article[10]; 
varchar v_nom[25]; 
int v_qte_tot; 
EXEC SQL END DECLARE SECTION; 
EXEC SQL INCLUDE SQLCA; 
main() 

  printf(ʺ\nCode Oracle ʺ); 
  gets(username); 
  printf(ʺ\nFournir mot de passe ʺ); 
  gets(password); 
  EXEC SQL CONNECT :username IDENTIFIED BY :password; 
  EXEC SQL WHENEVER SQLERROR goto ERREUR1; 
  strcpy((char*)v_nom.arr, ʺsergeʺ); 
  v_nom.len = strlen((char*)v_nom.arr); 
  EXEC SQL DECLARE curs_2  CURSOR FOR  
  SELECT article, nom, qte_tot FROM  VentesD 
  WHERE nom = :v_nom and article = ʹa1ʹ FOR UPDATE OF article; 
  EXEC SQL OPEN curs_2; 
  EXEC SQL FETCH curs_2 INTO  :v_article, :v_nom, :v_qte_tot; 

 
Chapitre 9 Interface applicative  30

  while(SQLCODE == 0) 
  {   
EXEC SQL UPDATE VentesD SET article = ʹa9ʹ WHERE CURRENT OF curs_2; 
EXEC SQL COMMIT; 
EXEC SQL FETCH curs_2 INTO :v_article, :v_nom, :v_qte_tot; 
printf(ʺLʹarticle et la quantite sont %s%d\nʺ, v_article, v_qte_tot); 
  } 
ERREUR1:printf(ʺErreur Oracleʺ); 
printf(ʺ\n%.70s\nʺ, sqlca.sqlerrm.sqlerrmc); 
exit(1); 
SORTIE:EXEC SQL CLOSE curs_2; 
exit(0); 

 
Si  la  même  application  pouvait  ouvrir  au  préalable  un  autre  curseur  curs_1    de  type 
INSENSITIVE    cette  mise  à  jour  ne  serait  pas  possible  avec  le  CURRENT  OF.  Toutefois,  elle 
pourrait être faite directement sur une table de base sans référence au tuple courant du curseur 
ouvert,  et  le  changement  ne  serait  pas  visible  dans  le  curseur  courant.  Ce  même  changement 
deviendrait visible après la fermeture du curseur curs_1 et sa réouverture.  
                   
9.3.2 Lʹapproche par SQL dynamique 
Les  techniques  précédentes  de  programmation  en  SQL  convenaient  aux  situations  où  les 
attributs de l’ordre DML et SELECT étaient connus à l’avance lors du parsing et de la génération 
de lʹarbre de requête. 
 
Il en est autrement avec une application pour laquelle il existe de nombreuses formes  pour un 
ordre,  notamment  pour  la  clause  WHERE,  en  raison  des  choix  possibles  proposés  aux 
utilisateurs  au  moment  de  lʹexécution.  Si  chaque  choix  correspondait  à  un  ordre  SQL,  leur 
nombre pourrait devenir trop important pour les implémenter tous à priori et les compiler avant 
exécution.    Il  est  plus  pratique  de  formuler  un  ordre  SQL  partiel,  de  pouvoir  le  compiler 
partiellement sous cette forme de manière à ce quʹil puisse être complété au stade de l’exécution 
de lʹapplication. Il s’agit donc de compiler partiellement des ordres SQL spécifiés, pour obtenir 
un code exécutable adaptable, auquel lʹapplication fournit les informations manquantes pendant 
lʹexécution de lʹordre SQL.  
 
Par exemple, un utilisateur peut avoir le choix de lancer une opération sur une base de données 
au  moyen  de  pictogrammes  d’une  interface  simple  gérée  par  un  GUI.  Ceux‐ci  permettent,  par 
exemple, de spécifier les paramètres d’une requête en cliquant sur un nombre variable dʹicones 
présentés à lʹécran.   
          
           
  Réservation de chambres d’hôtel
 
Appuyez sur le bouton de votre choix :

 
Chapitre 9 Interface applicative  31

 
  Localisation urbaine : En périphérie :
 
 
Lit : Prix : $$ $
 
  OK 
 
 
Il  y  a  donc  23  choix  possibles,  correspondant  à  autant  dʹordres  SQL  distincts,  quʹil  faudrait 
prévoir  dans  lʹapplication.  Si  les  choix  sont  encore  plus  nombreux,  le  nombre  dʹordres  ajoute 
une complexité importante à lʹapplication. Voici quelques‐uns des ordres possibles avec lʹécran 
ci‐dessus : 
SELECT *  FROM  Hotel WHERE local = ʹcentreʹ; 
SELECT *  FROM  Hotel WHERE local = ʹcentreʹ and nb_lit = 2; 
SELECT *  FROM Hotel WHERE local = ʹcentreʹ and nb_lit = 2 and prix <100$ 
SELECT *  FROM  Hotel WHERE local = ʹbanlieueʹ; 
 
Les  clauses  SQL  de  ces  exemples  sont  différentes  par  le  prédicat,  mais  dans  cet  exemple 
similaires  par  un  même  schéma  de  la  réponse,  i.e.  tous  les  attributs  sont  affichés.  Si  une 
application est développée avec un SQL intégré et utilise des variables hôtes, il lui faut prévoir 8 
ordres  différents  avec  un  choix  géré  par  lʹapplication  au  moment  de  lʹexécution.  Le  SQL 
dynamique va accroître encore davantage cette souplesse dans la programmation en permettant 
lʹusage de variables dynamiques qui sont traitées de façon spéciale par le compilateur, de sorte 
que  les  partie  manquantes  de  lʹordre  puissent  être  spécifiées  à  lʹexécution.  Le  SQL/86  offre  ce 
type de compilation qui a été cependant simplifiée par la version SQL/92 . 
 
Quelques exemples simples du langage de SQL/86 seront examinés afin d’apprécier la puissance 
du SQL dynamique.  Ensuite, un exemple illustrera une application utilisant la norme  SQL/92.  
 
Il y a quatre usages possibles du SQL dynamique (ORACLE) : 
1‐ DML  autre que le SELECT et sans variable hôte; 
2‐ DML  autre que le SELECT avec un nombre connu de variables hôtes; 
3‐ SELECT avec un nombre connu de variables hôtes; 
4‐ SELECT avec un nombre inconnu de variables hôtes. 
 
Les deux premiers permettent dʹimplémenter les ajouts, suppressions et modifications de tuples, 
tandis que les deux derniers implémentent les recherches. 
1‐ SQL dynamique SQL/86 
Le cas le plus simple correspond au cas où lʹordre DML au complet est fourni à lʹexécution suite 
à la lecture dʹune chaîne de caractères. Cette chaîne, que lʹon sait à lʹavance représenter un DML 
SQL  complet,  ne  sera  analysée  entièrement  quʹau  moment  de  sa  saisie  au  terminal  par 
lʹutilisateur.  Lʹordre  SQL  spécifié  sera  par  la  suite  exécuté.  Cette  approche  a  lʹinconvénient 
dʹexiger  lʹanalyse,  lʹoptimisation  et  lʹexécution  dʹun  tel  ordre  chaque  fois  quʹil  est  utilisé  dans 

 
Chapitre 9 Interface applicative  32

une application. Cette méthode ne sʹapplique pas au traitement du SELECT. Lʹexemple simple 
ci‐dessous  implémente  un  INSERT  dynamique  dont  lʹordre  est  fourni  entièrement  par 
lʹutilisateur au moment de lʹexécution. 
 
/*Exécution dʹun ordre DML fourni au complet par lʹutilisateur*/ 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
EXEC SQL BEGIN DECLARE SECTION; 
char username[20]; 
char password[10]; 
char ordrelu[80]; //stocke la chaîne de car. qui représente lʹordre DML 
EXEC SQL END DECLARE SECTION; 
EXEC SQL INCLUDE SQLCA; 
void sqlerror(void); 
main() 

printf(ʺ\nCode Oracle ʺ); 
gets(username); 
printf(ʺ\nFournir mot de passe ʺ); 
gets(password); 
EXEC SQL WHENEVER SQLERROR DO sqlerror();
EXEC SQL CONNECT :username IDENTIFIED BY :password; 
printf(ʺ\nConnexion ok a Oracle sous le compte %s\nʺ, username); 
printf(ʺ\nFormulez votre ordre DML ʺ); 
gets(ordrelu);/*ex.:
INSERT INTO Ventes values('claude','a3',4)*/
EXEC SQL EXECUTE IMMEDIATE :ordrelu;
EXEC SQL COMMIT WORK RELEASE; 
exit(0);} 
void sqlerror() 

EXEC SQL WHENEVER SQLERROR CONTINUE; 
printf(ʺ\nOracle erreur:\nʺ); 
printf(ʺ\n%.70s\nʺ, sqlca.sqlerrm.sqlerrmc); 
EXEC SQL ROLLBACK WORK RELEASE; 
exit(1);}  
 
La  variable  hôte  ordrelu    est  nécessaire  pour  obtenir  la  chaîne  de  caractères  qui  représente 
l’ordre DML fourni par l’utilisateur. L’ordre DML  obtenu via une interface quelconque comme 
celle  générée  par  un  GUI  ou  un  écran  ASCII  comprend  tous  les  éléments  pour  le  calcul  de  la 
réponse.  Lʹordre  EXECUTE  indique  au  compilateur  SQL  quʹil  doit  insérer  le  code  nécessaire 

 
Chapitre 9 Interface applicative  33

pour  lʹanalyse,  la  génération  et  lʹexécution  dʹun  ordre  SQL  complet  à  lʹétape  de  lʹexécution  de 
lʹapplication.  
 
 A chaque exécution de lʹapplication, il y aura analyse, traduction et exécution de lʹordre qui sera 
éventuellement différent à chaque exécution. Cet ordre peut être un SELECT , update, INSERT  
ou  un  delete.  Finalement,  lʹexécution  immédiate  ne  règle  en  rien  le  problème  qui  apparaît 
lorsque les choix possibles sont nombreux comme cʹétait le cas avec lʹapplication pour le choix 
dʹune  chambre  dʹhôtel.  En  effet,  chaque  choix  doit  être  associé  à  un  ordre  DML  particulier 
complet. 
 
2‐ Ordre DML  avec des paramètres partiellement connus à la compilation (cas 1 tuple, sans 
curseur) 
Cette approche permet de compiler un ordre DML dont les paramètres sont en partie connus à 
la compilation, tandis que les autres seront fournis à l’exécution. Elle ne permet pas de traiter un 
SELECT.  Dans  lʹexemple  qui  suit,  il  sʹagit  dʹune  mise  à  jour  de  lʹattribut  coût  dont  la  nouvelle 
valeur reste à spécifier et cela pour un article qui ne sera spécifié quʹau moment de lʹexécution 
de lʹapplication. Les paramètres symboliques identifiés par les ʹ:ʹ sont des place holders,  et  leur 
valeur  respective  ne  sera  connue  quʹà  lʹexécution.  Lʹénoncé  PREPARE  ...  USING  instancie  les 
variables symboliques par les valeurs des variables hôtes de même type et lance lʹanalyse de la 
variable ordrelu[]  pour générer une requête appelée dml1.  
 /*Exécution dʹun ordre DML avec paramètres partiellement connus à la compilation */ 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlca.h>
EXEC SQL BEGIN DECLARE SECTION;
char username[20];
char password[10];
char ordrelu[80];
char a[30];
int c;
EXEC SQL END DECLARE SECTION;
EXEC SQL INCLUDE SQLCA;
void sqlerror(void);
main()
{
int encore = 0;
printf("\nCode Oracle ");
gets(username);
printf("\nFournir mot de passe ");
gets(password);
EXEC SQL WHENEVER SQLERROR DO sqlerror();
EXEC SQL CONNECT :username IDENTIFIED BY :password;

 
Chapitre 9 Interface applicative  34

/* :c et :a sont des caractères symboliques remplacés au momentde la


compilation par une variable C*/
strcpy(ordrelu, "UPDATE Piece_inv SET cout = :c
WHERE article = :a");
printf("\n Introduisez le nom d'article dontle cout est à
modifier ");
gets(a);
printf("\nIntroduisez le nouveau cout ");
scanf("%4d",&c);
EXEC SQL PREPARE dml FROM :ordrelu;
EXEC SQL EXECUTE dml USING :c, :a;
EXEC SQL COMMIT WORK;
exit(0);
}
void sqlerror()
{
EXEC SQL WHENEVER SQLERROR CONTINUE;
printf("\nOracle erreur:\n");
printf("\n%.70s\n", sqlca.sqlerrm.sqlerrmc);
EXEC SQL ROLLBACK WORK RELEASE;
exit(1);
}

La  première  instruction  du  SQL  dynamique  PREPARE  compile  un  ordre  SQL  avec  deux 
paramètres  symboliques  :a  et  :c  dont  les  valeurs  seront  connues  seulement  à  l’exécution.  Cet 
ordre est nommé dml1. Cette technique est plus souple que la première parce qu’elle permet de 
traiter  un  ordre  dont  un  ou  plusieurs  attributs  sont  inconnus  lors  de  la  compilation.  Ainsi,  il 
devient  possible  de  faire  la  mise  à  jour  d’une  table  avec  comme  premier  paramètre  le  nom  de 
l’article et comme deuxième paramètre son coût. Toutefois, la même instruction SQL dynamique 
ne  peut  pas  faire  un  ajout  (  INSERT)  qui  comporte  deux  paramètres,  car  lʹordre  UPDATE  est 
déjà connu au moment de la compilation.  
 
INSERT INTO Pieces_inv  INSERT (:p1,:p2) ‐‐action incompatible 
 
Avec cette même approche, il est possible de personnaliser les outils de gestion de la BD pour 
un  DBA  en  implémentant  avec  cette  technique  les  ordres  CREATE  TABLE,  DROP  TABLE, 
INSERT,  UPDATE,  DELETE,  GRANT  dont  les  paramètres  dynamiques  permettent  de  créer, 
supprimer  diverses  tables  et  dʹy  ajouter  divers  tuples  sans  avoir  à  reformuler  complètement 
lʹordre  DML.  Le  EXECUTE  ...  USING  est  obligatoire  pour  donner  une  valeur  aux  paramètres 
symboliques.  
 
3‐ Ordre SELECT avec schéma du résultat connu à la compilation (cas de plusieurs tuples) 
Le  schéma  du  résultat    du  SELECT  est  connu  lors  du  développement  et  de  la  compilation  de 
même que la ou les relations du FROM. Les clauses WHERE et ORDER BY peuvent être aussi 

 
Chapitre 9 Interface applicative  35

définies dynamiquement. La connaissance des paramètres signifie que leur nombre et leur type 
sont  spécifiés  au  moment  de  la  compilation  et  qu’ils  devront  être  compatibles  avec  les  valeurs 
fournies  à  l’exécution.  La  connaissance  du  schéma  du  résultat  au  moment  de  la  compilation 
signifie  que  le  type  des  attributs  de  la  réponse  est  connu  (variables  hôtes).    Dans  un  tel  cas, 
lʹordre  SELECT  est  construit  et  exécuté  après  concaténation  avec  la  chaîne  fournie  par 
lʹutilisateur.  
 
/*Exécution dʹun SELECT avec un schéma réponse connu à la compilation*/ 
#define SQLCODE sqlca.sqlcode 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
EXEC SQL BEGIN DECLARE SECTION; 
char username[20]; 
char password[10]; 
char metadml[200]; 
char v_article[30]; 
int v_cout; 
char no[6] ; 
EXEC SQL END DECLARE SECTION; 
EXEC SQL INCLUDE SQLCA; 
void sqlerror(void); 
main() { 
printf(ʺ\nCode Oracle ʺ); 
gets(username); 
printf(ʺ\nFournir mot de passe ʺ); 
gets(password); 
EXEC SQL WHENEVER SQLERROR DO sqlerror(); 
EXEC SQL CONNECT :username IDENTIFIED BY :password; 
Strcpy(metadml, "SELECT article, cout
FROM :tableSource WHERE no_article = :no");
EXEC SQL PREPARE dml2 FROM :metadml;
EXEC SQL DECLARE curs CURSOR FOR dml2;
printf(ʺ\nFournir le numero dʹarticle ʺ ); 
gets(no); 
gets (tableSource) 
EXEC SQL OPEN curs USING :tableSource,:no, ;
while (sqlca.sqlcode !=1403) – résultat non vide  
{EXEC SQL FETCH curs  INTO :v_article, :v_cout ;   
printf(ʺ\nArticle et son cout %s %d ʺ, v_article, v_cout); 
}   
EXEC SQL CLOSE curs; 
EXEC SQL COMMIT WORK; 
exit(0);} 

 
Chapitre 9 Interface applicative  36

void sqlerror() 

EXEC SQL WHENEVER SQLERROR CONTINUE; 
printf(ʺ\nOracle erreur:\nʺ); 
printf(ʺ\n%.70s\nʺ, sqlca.sqlerrm.sqlerrmc); 
EXEC SQL ROLLBACK WORK RELEASE; 
exit(1); } 
 
Les  énoncés  PREPARE  et  EXECUTE  de  ce  programme  ont  lʹavantage  de  lancer  une  seule 
compilation de lʹordre SQL même si celui‐ci est exécuté autant de fois quʹil y a de tuples dans la 
table choisie par l’utilisateur au moment de l’exécution de l’application.  
 
4‐ SELECT avec schéma de réponse  inconnu  à la compilation (cas pour 1 tuple) 
C’est un cas plus général où un SELECT peut être exécuté sans connaître à priori le schéma de la 
réponse  donc  du  nombre  et  du  type  des  variables  hôtes  nécessaires.  Cette  approche  est  aussi 
valable  pour  une  jointure  et  une  sous  requête  dans  une  clause  SELECT.  Pour  contourner 
lʹabsence  dʹinformation  sur  le  schéma  de  la  réponse,  le  programme  hôte  spécifie  une  nouvelle 
structure  de  données  variable  appelée  Descripteur  SQLDA  (SQL  Descriptor  Area)  destinée  à 
contenir  des  informations  concernant  le  schéma  de  la  réponse  pour  chaque  exécution.  La 
structure de cette zone peut varier selon le SGBD. Cette information est stockée dans cette zone 
au moment de l’exécution de la commande DESCRIBE.  
 
Avec la norme ISO SQL/86, la structure de SQLDA de chaque SGBD  devait être déclarée dans 
lʹapplication.  Comme  le  contenu  de  ce  Descripteur  variait  selon  le  SGBD,  il  était  difficile  de 
développer des applications portables, dʹautant plus que le descripteur utilisait des pointeurs et 
lʹallocation  dynamique  qui  ne  sont  pas  disponibles  dans  tous  les  langages  de  développement. 
Avec  la  norme  ISO  SQL/92,  la  définition  et  la  gestion  de  la  structure  SQLDA  devenait  la 
responsabilité  du  SGBD  et  non  plus  celle  de  lʹapplication,  qui  cependant  doit  toujours  en 
demander  lʹallocation  par  un  ordre  particulier  ALLOCATE.  Cette  zone  accessible  au  SGBD est 
utilisée  pour  stocker  toute  lʹinformation  concernant  les  paramètres  utilisés  par  un  énoncé  SQL 
dynamique.  La  norme  SQL/92  spécifie  une  structure  composée  dʹarticles  appelés  ITEM 
DESCRIPTOR  dont  chacun  renferme  toutes  les  informations  relatives  à  un  attribut  ou  à  un 
paramètre dʹun ordre  SQL dynamique. Cette partie variable de la structure est précédée dʹune 
partie fixe (lʹen‐tête). La spécification est complétée pendant l’exécution d’une clause SQL grâce 
aux informations fournies par le SQLDA et cela pour chaque attribut impliqué dans une clause 
SQL dynamique.  
 
Partie fixe du SQLDA :  
SQLN : le nombre maximum dʹattributs dans un ordre SELECT ou DML; 
SQLD : le nombre réel de colonnes dans l'ordre instancié par le SGBD
au moment de l'exécution.
 
Partie variable : 
SQLTYPE : code interne de type utilisé par un SGBD;

 
Chapitre 9 Interface applicative  37

SQLLEN : longueur en octets de chaque type;


SQLDATA : pointeur sur une zone de l'application où sera rangée
la valeur de l'attribut;
SQLIND : indicateur de variable pour chaque attribut;
SQLNAME : nom de l'attribut.
 
Une instance du descripteur (ITEM DESCRIPTOR) est allouée pour chaque colonne du résultat 
ou pour chaque paramètre symbolique de l’énoncé SQL dynamique à exécuter. La création de la 
zone  SQLDA  et  l’allocation  de  l’espace  nécessaire  pour  stocker  les  descripteurs  sont  faites  par 
une commande ALLOCATE. Celle‐ci ne fait que l’allocation de l’espace pour n descripteurs. 
 
EXEC SQL ALLOCATE DESCRIPTOR nom_descript WITH MAX n
où : 
n : nombre maximum de descripteurs prévus; le concepteur estime cette borne supérieure; 
nom_descripteur  :  nom  donné  à  l’ensemble  de  l’espace  réservé  pour  le  SQLDA  associé  à  une 
clause dynamique. 
 
Chaque  descripteur  SQLDA  étant  nommé,  une  application  peut  en  créer  autant  qu’il  y  a  de 
clauses  SQL  dynamiques.  L’espace  de  cette  zone  peut  être  libéré  par  un  ordre  DEALLOCATE 
DESCRIPTOR  nom_du_descripteur.  
 
La métacompilation de l’ordre SQL dynamique (le SELECT formulé sans INTO) à partir d’une 
chaîne  de  caractères  d’une  variable  hôte  est  réalisée  par  l’instruction  PREPARE.  Il  est  aussi 
possible  d’y  traiter  de  la  même  façon  les  clauses  INSERT,  UPDATE  et  DELETE.  Lʹénoncé 
dynamique compilé est stocké dans une structure dont le nom est nom_dyn. 
EXEC SQL PREPARE nom _dyn FROM :variable_hôte
 
Au  stade  de  l’exécution,  l’information  concernant  le  résultat  d’un  dml  ou  dʹune  requête  est 
placée dans le descripteur par le DESCRIBE. 

EXEC SQL DESCRIBE [OUTPUT] nom _dyn USING SQL DESCRIPTOR nom_descript; 
EXEC SQL DESCRIBE [INPUT] nom_dyn USING SQL DESCRIPTOR nom_descript; 
La mise à jour du descripteur par cette instruction se fait de la façon suivante : 
a) Pour un descripteur de lecture, COUNT contient le nombre dʹattributs dans le schéma de la 
réponse; 
b)  Pour  un  ordre  INSERT  ou  UPDATE,    le    COUNT  contient  le  nombre  de  paramètres 
symboliques; 
c) Le  champ DATA contient la valeur de lʹattribut renseigné par un FETCH. 
 
Pour un énoncé de lecture, l’application a accès aux informations pour exploiter correctement les 
données  obtenues  par  une  clause  SELECT  dynamique  par  lʹentremise  du  descripteur.  Le  GET 
DESCRIPTOR  fournit  aux  variables  hôtes  les  informations  nécessaires  pour  traiter  les  données 
reçues.  Le  nombre  de  colonnes  (attributs)  est  obtenu  par  le  champ  COUNT,  tandis  que  le 
descripteur de chaque attribut fournit les informations sur l’attribut. 

 
Chapitre 9 Interface applicative  38

 
Lʹaccès aux données de lʹen‐tête se fait par lʹénoncé suivant : 
EXEC SQL GET DESCRIPTOR nom _descript :i :variable = COUNT;
-- accès à l'en-tête
EXEC SQL GET DESCRIPTOR nom _descript VALUE :i :variable = NAME; 
où :  
:i est une variable hôte spécifiant le descripteur; 
:variable  est une variable hôte de type approprié. 
Par exemple, le choix dʹun hôtel peut se faire selon certains critères dont le nombre nʹest connu 
quʹau  moment  où  le  client  effectue  son  choix.  Ainsi,  pour  un  client  le  seul  critère  peut  être  la 
localisation géographique peu importe le prix et le nombre de lits. Pour une autre personne, les 
trois critères sont spécifiés pour la recherche. 
 
L’exécution  d’une  clause  SQL  dynamique  qui  ne  retourne  qu’un  seul  tuple  (sinon,  le  curseur  
explicite est obligatoire) est lancée par l’instruction EXECUTE. 

EXEC SQL EXECUTE :var_sql_dyn USING SQL DESCRIPTOR descript;


 
La valeur de chaque attribut résultat est placée dans le descripteur  descript et lʹapplication y a 
accès par le GET DESCRIPTOR. 
Traitement en SQL dynamique dʹune réponse comportant plusieurs tuples 
Dans ce cas aussi, il faut utiliser un curseur de la façon exposée précédemment. La déclaration 
du  curseur  peut  se  faire  en  connaissant  le  nom  du  curseur  à  la  compilation  ou  en  l’ignorant, 
puisqu’il ne sera obtenu qu’au moment de l’exécution. 
a) cas du curseur dynamique dont lʹexpression SQL n’est connue quʹà la compilation 
                                  
EXEC SQL DECLARE curseur1 [INSENSITIVE] [SCROLL] CURSOR FOR
:instruct2;
 
b) cas du curseur inconnu à la compilation (curseur dynamique étendu) 
 
EXEC SQL ALLOCATE :nom_curs [INSENSITIVE] [SCROLL] CURSOR FOR
:instruct2;
 
c) ouverture et fermeture dʹun curseur dynamique 
EXEC SQL OPEN nom_curseur;
EXEC SQL CLOSE nom_curseur;
   
d)  l’obtention des tuples et des valeurs  est réalisée par l’exécution d’un ordre FETCH.  
 
EXEC SQL FETCH FROM curseur1 INTO :var1, :var2; 
 
Exécution dʹune clause SELECT retournant plusieurs tuples  

 
Chapitre 9 Interface applicative  39

Dans ce cas, il faut aussi utiliser un curseur dynamique. La tâche demandée est beaucoup plus 
complexe.  Le  programme  doit  prévoir  des  structures  pour  recevoir  subséquemment  des 
informations  absentes  lors  de  la  compilation.  Cela  se  reflète  par  une  augmentation  de  la 
complexité de l’application. 
 
Exemple dʹune application développée avec SQL/92 
/*SQL Dynamique: Ordre Select avec shéma de réponse inconnu à la
compilation. Ce programme permet la connexion à Oracle et ensuite
l'interrogation de la base de données. L'utilisateur a la possibilité
de préciser les données à afficher et ainsi que les critères de
sélection. Le nombre maximal des éléments affichés est fixé à 40*/
#include <stdio.h>
#include <string.h>
#include <setjmp.h>
#include <stdlib.h>
#include <malloc.h>
#include <sqlcpr.h>
/* Nb maximal d'attributs et de paramètres admissibles dans la requête */
#define MAX_ITEMS 40
/* Longueur maximale admissible des attributs et de paramètres */
#define MAX_VNAME_LEN 30
#define MAX_INAME_LEN 30
#ifndef NULL
#define NULL 0
#endif
EXEC SQL BEGIN DECLARE SECTION;
char dyn_statment[1024];
EXEC SQL VAR dyn_statment IS STRING(1024);
EXEC SQL END DECLARE SECTION;
EXEC SQL INCLUDE sqlca;
EXEC SQL INCLUDE sqlda;
SQLDA *variables; //pointeur vers le descripteur de paramètres
SQLDA *attributs; //ponteur vers le descripteur de sélection
extern SQLDA * sqlald();
extern void sqlnul();
jmp_buf jmp_continue;
int flag = 0;
int connexion(); /* Connexion à Oracle */
int allouer_descripteurs(); /*Allocation des descripteurs pour les attributs et
les paramètres*/
int get_dyn_statment(); /* Construction de la requête dynamique SQL */
int set_variables(); /* Initialisation des descripteurs*/
int parcourir_table(); /* Traitement de la base de données*/
void sql_error(); /* Traitement des erreurs */
main()

 
Chapitre 9 Interface applicative  40

{
int i;
/* Connexion à Oracle. */
if(connexion() != 0)
exit(1);
/* Allocation de memoire pour les descripteurs*/
if(allouer_descripteurs(MAX_ITEMS,MAX_VNAME_LEN,MAX_INAME_LEN) !=0 )
exit(1);
for(;;)
{
/*setjmp() sauvegarde l'état actuel du programme.*/
i = setjmp(jmp_continue);
/* Test pour une requête SQL valide */
if(get_dyn_statment()!=0) goto FIN;
/* Mise en place du test d'exception */
EXEC SQL WHENEVER SQLERROR DO sql_error();
flag = 1;
/* Traitement de la requête et déclaration du courseur */
EXEC SQL PREPARE dml FROM :dyn_statment;
flag = 0;
EXEC SQL DECLARE curs CURSOR FOR dml;
set_variables();
/* Ouverture du curseur et affichage de données*/
EXEC SQL OPEN curs USING DESCRIPTOR variables;
parcourir_table();
} //end for
/*Libérer la memoire allouée pour les descripteurs */
for(i=0; i<MAX_ITEMS; i++)
{
if(variables->V[i] != (char*)0)
free(variables->V[i]);
free(variables->I[i]);
if(attributs->V[i] != (char*)0)
free(attributs->V[i]);
free(attributs->I[i]);
}//endfor
/* Libérer l'espace alloué pour les descripteurs */
sqlclu(variables);
sqlclu(attributs);
FIN: EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL CLOSE curs;
EXEC SQL COMMIT WORK RELEASE;
puts("\nA bientot\n");
EXEC SQL WHENEVER SQLERROR DO sql_error();
return 0;

 
Chapitre 9 Interface applicative  41

}//main
 
connexion()
/* Connexion à Oracle en utilisant le ID et le mot de passe fournis par
l'utilisateur. En cas d'echec affiche le message d'erreur
correspondant*/
{
EXEC SQL BEGIN DECLARE SECTION;
char usager[15];
char motdepasse[15];
EXEC SQL END DECLARE SECTION;
printf("\nCode Oracle ");
gets(usager);
printf("\nFournir mot de passe ");
gets(motdepasse);
EXEC SQL WHENEVER SQLERROR goto ERREUR;
EXEC SQL CONNECT :usager IDENTIFIED BY :motdepasse;
printf("*------------------------------------------*\n");
printf("*------------------------------------------*\n");
printf("*-Vous êtes connectés a la base de données-*\n");
printf("*------------------------------------------*\n");
printf("*------------------------------------------*\n");
printf("*Pour obtenir de l'aide entrez <aide> *\n");
printf("*pour quitter entrez exit *\n\n");
return 0;
ERREUR: printf("\nOracle erreur:\n");
printf("\n%.70s\n", sqlca.sqlerrm.sqlerrmc);
return -1;
} //end connexion

Allouer_descripteurs(size, max_vname_len, max_iname_len)


/*Allocation des descripteurs pour les attributs et les paramètres
symboliques,ainsi que d'un pointeur vers la zone des descripteurs */
int size;
int max_vname_len;
int max_iname_len;
{
int i;
if((variables = sqlald(size, max_vname_len, max_iname_len))==
(SQLDA*)0)
{
printf("\n Impossible d'allouer memoire pour les variables");
return -1;
} //endif
if((attributs = sqlald(size, max_vname_len, max_iname_len))==(SQLDA*)0)

 
Chapitre 9 Interface applicative  42

{printf("\nImpossible d'allouer de la memoire pour les variables");


return -1;
} //endif
attributs->N = MAX_ITEMS;
variables->N = MAX_ITEMS;
/* Allocation du pointeur vers la zone de données*/
for( i = 0; i<MAX_ITEMS; i++)
{
variables->I[i] = (short*)malloc(sizeof(short));
variables->V[i] = (char*)malloc(1);
attributs->I[i] = (short*)malloc(sizeof(short));
attributs->V[i] = (char*)malloc(1);
} //end for
return 0;
}//end alloc_descriptors
 
Get_dyn_statment()
/* Retourne la requête SQL dynamique formée avec les données fournies
par l'utilisateur. Il a la possibilité de choisir plusieurs attributs à
afficher selon les critères choisis */
{
int aide();
char token[] = " ,\n\0";
char *attribut;
char donnes[256];
char clause[256];
char localisation[10];
char occupation[10];
char categorie[10];
char prix[10];
int nb = 0
COMM: printf("\nDonnees a afficher> ");
dyn_statment[0] = '\0';
strcpy(clause, "SELECT ");
gets(donnes);
attribut = strtok(donnes,token);
if(!(strncmp(donnes,"TOUT",4))||!(strncmp(donnes,"tout",4))) {
strcat(clause,"* FROM Hot");
strcat(dyn_statment,clause);
goto FIN;
}
if(!(strncmp(donnes,"aide",4))) {aide();
goto COMM;}
if(!(strncmp(donnes,"exit",4)))
{return 1;}

 
Chapitre 9 Interface applicative  43

while(attribut)
{
strcat(clause,attribut);
attribut = strtok(NULL,token);
if(attribut!=NULL)
strcat(clause,",");
}
strcat(clause, " FROM Hot");
strcat(dyn_statment,clause);
printf("\nLocalisation(centre/banlieue/Enter):");
gets(localisation);
printf("\nOccupation(simple/double/Enter): ");
gets(occupation);
printf("\nCategorie(econo/premiere/Enter): ");
gets(categorie);
printf("\nPrix(>/=/</Enter): ");
gets(prix);
if(!(strncmp(localisation,"\0",1)))
goto OC;
else
{
nb++;
strcat(clause," WHERE localisation='");
strcat(clause,localisation);
strcat(clause,"'");
dyn_statment[0]='\0';
strcat(dyn_statment,clause);
}
OC: if(!(strncmp(occupation,"\0",1))) goto CA;
else
{
if(nb==0)
strcat(clause," WHERE occupation='");
else strcat(clause, " and occupation='");
strcat(clause,occupation);
strcat(clause,"'");
dyn_statment[0]='\0';
strcat(dyn_statment,clause);
}
CA:
if(!(strncmp(categorie,"\0",1)))
goto PR;
else
{
if(nb==0)

 
Chapitre 9 Interface applicative  44

strcat(clause," WHERE categorie='");


else strcat(clause, " and categorie='");
strcat(clause,categorie);
strcat(clause,"'");
dyn_statment[0]='\0';
strcat(dyn_statment,clause);
}
PR:
if(!(strncmp(prix,"\0",1))) goto FIN;
else
{
if(nb==0)
if(!(strncmp(prix,">",1))||!(strncmp(prix,">",1)))
strcat(clause," WHERE prix ");
else
strcat(clause," WHERE prix=");
else
if(!(strncmp(prix,">",1))||!(strncmp(prix,">",1)))
strcat(clause," and prix ");
else
strcat(clause," and prix=");
strcat(clause,prix);
dyn_statment[0]='\0';
strcat(dyn_statment,clause);
}
printf("%s",clause);
FIN:
return 0;
}//end get_dyn_statement
 
Set_variables()
/*Initialisation des descripteurs pour les paramètres. L'information
concernant le résultat de la requête est placée dans le descripteur par
DESCRIBE */
{int i, n;
char bind_var[64];
EXEC SQL WHENEVER SQLERROR DO sql_error();
variables->N = MAX_ITEMS;
EXEC SQL DESCRIBE BIND VARIABLES FOR dml INTO variables;
variables->N = variables->F;
for(i=0; i< variables->N; i++)
{
/* Saisie des valeurs des paramètres sous la forme d'une chaîne de
caractères.

 
Chapitre 9 Interface applicative  45

C[i] contient la longueur du nom de paramètre utilisé dans dans la


requête SQL ; S[i] contient le nom du paramètre utilisé dans la requête
*/
printf("\nEntrez la valeur de %.*s ", (int)variables->C[i], variables-
>S[i]);
fgets(bind_var, sizeof(bind_var), stdin);
/* L[i] contient la longueur de la donnée saisie comme paramètre */
n = strlen(bind_var) - 1;
variables->L[i] = n;
/*V[i] contient l'adresse de la donnée
* I[i] est un pointeur vers la valeur d'indicateur -1 si la requête ne
* contient pas de paramètres, c'est à dire s'ils sont nuls*/
variables->V[i] = (char*)realloc(variables->V[i], variables->L[i]+1);
strncpy(variables->V[i], bind_var, n);
if((strncmp(variables->V[i],"NULL",4)==0)||(strncmp(variables->V[i],
"null", 4) == 0))
*variables->I[i]=-1;
else
*variables->I[i]=0;
/* T[i] est le type de la donnée des paramètres. Dans ce cas il est 1
car tous les paramètres sont saisis comme chaînes de caractères.*/
variables->T[i] = 1;
}
return 0;
}
 
Parcourir_table()
{int i, fin, precision, scale;
/*Si la clause SQL est une clause SELECT la fonction DESCRIBE analyse
les données à afficher et retourne l'information qui les concerne*/
if((strncmp(dyn_statment, "SELECT", 6)!=0)&&(strncmp(dyn_statment,
"SELECT ", 6)!=0))
{
attributs->F = 0;
return 0;
}
attributs->N = MAX_ITEMS;
EXEC SQL DESCRIBE SELECT LIST FOR dml INTO attributs;
/* Le nombre max de colonnes à afficher égale le nb trouvé par
DESCRIBE*/
attributs->N = attributs->F;
/* Allocation de la memoire pour chaque attribut à sélectionner*/
for(i = 0; i<attributs->F; i++)
{
sqlnul(&(attributs->T[i]), &(attributs->T[i]), &fin);
switch(attributs->T[i])

 
Chapitre 9 Interface applicative  46

{
/* Type retourné CHAR. La modification de longueur n'est pas requise*/
case 1: break;
/* sqlprc extrait la précison et l'échelle d'un nombre ; Type retourné
NUMBER. On alloue la longueur maximale possible. Si l'échelle >0 alors
on convertit NUMBER vers un type FLOAT, sinon vers un INT */

case 2: sqlprc(&(attributs->L[i]), &precision, &scale);


if(precision == 0) precision = 40;
if(scale > 0)
attributs->L[i] = sizeof(float);
else
attributs->L[i] = sizeof(int);
break;
/*Type retourné LONG*/
case 8: attributs->L[i] = 18; break;
/*Type retourné DATE */
case 12:attributs->L[i] = 9; break;
} //end switch
if(attributs->T[i]!=2)
attributs->V[i]=(char*)realloc(attributs->V[i],attributs->L[i]+1);
else
attributs->V[i]=(char*)realloc(attributs->V[i],
attributs->L[i]);
/* Affichage de nom de colonnes */
if(attributs->T[i]==2)
if(scale > 0)
printf("%-.*s ", attributs->L[i]+3, attributs->S[i]);
else
printf("%-.*s ", attributs->L[i], attributs->S[i]);
else
printf("%-.*s ",attributs->L[i], attributs->S[i]);
/*Conversion vers char des types de données à l'exception de int*/
if(attributs->T[i]!=2) attributs->T[i]=1;
/*Conversion vers float ou int les données numériques avec valeur de l'échelle*/
if(attributs->T[i] ==2 )
if(scale > 0)
attributs->T[i] = 4;
else
attributs->T[i] = 3;
} //endfor
printf("\n\n");
/*Impression des données */
EXEC SQL WHENEVER NOT FOUND goto FIN;
for(;;)

 
Chapitre 9 Interface applicative  47

{
EXEC SQL FETCH curs USING DESCRIPTOR attributs;
for(i = 0; i<attributs->F; i++)
{
if(*attributs->I[i] < 0 )
if(attributs->T[i] ==4)
printf("%-*c ", (int)attributs->L[i]+3, ' ');
else
printf("%-*c ", (int)attributs->L[i], ' ');
else
if(attributs->T[i] == 3)
printf("%-*d ",(int)attributs->L[i],*(int*)attributs->V[i]);
else if(attributs->T[i] == 4)
printf("%*.2f ",(int)attributs->L[i],*(float*)
attributs->V[i]);
else
printf("%-.*s ", (int)attributs->L[i],
attributs->V[i]);
}
printf("\n");
}
FIN: return 0;
}
 
void sql_error()
/* Routine de traitement des erreurs*/
{ EXEC SQL WHENEVER SQLERROR CONTINUE;
printf("\nOracle erreur:\n");
printf("\n%.70s\n", sqlca.sqlerrm.sqlerrmc);
EXEC SQL ROLLBACK WORK RELEASE;
exit(1);
}
Aide()
/*Routine informative*/
{
printf("\n------------------INFO!!!------ -------\n");
printf("\n Pour visualiser le contenu de la table tapez le mot cle:
<tout>\n");
printf("\nPour visualiser des donnees specifiques entrez-les, separes
par virgule\n");
return 0;

9.3 Interface API (ODBC ou SAG) 

 
Chapitre 9 Interface applicative  48

Avec cette approche, une librairie de fonctions normalisées est utilisée par le concepteur dans le 
contexte  d’une  architecture  client/serveur,  indépendamment  du  logiciel  SGBD  distant  avec 
lequel lʹapplication doit interagir pour obtenir des données. Deux groupes travaillent dans cette 
optique : celui de Microsoft (ODBC)3 et celui du SAG (SQL Access Group. La solution ODBC est 
la plus répandue.  

  Application utilisant le driver ODBC
 
 
Gestionnaire de drivers
 
 
 
 
driver 1  driver 2 driver 2 driver n
 
 
 
 
  SGBD1  BD1 BD2 SGBD2 
 
 
 9.5 
L’utilisation  d’une  librairie  API  permet  d’exécuter  des  clauses  ODBC  SQL  par  une  librairie  de 
fonctions  standardisées  masquant  les  différences,  parfois  légères,  qui  caractérisent  les  diverses 
implémentations  de  SQL.  Par  exemple,  voici  deux  ordres  SQL  de  jointure  externe  spécifiée 
différemment par deux SGBD. 
 
a) Jointure externe du SQL Oracle : 
SELECT Personnel.nom, Usine.nom
FROM Personnel, Usine
WHERE (Personnel.no_projet=544) and
(Personnel.no_usine = Usine.no_usine(+));
-- notez le + pour la jointure externe
 
b) Jointure du SQL Server :
SELECT Personnel.nom, Usine.nom 
FROM  Personnel, Usine 
WHERE Personnel.no_projet = 544 and  
(Personnel.no_usine =* Usine.no_usine);   
‐‐ notez l’étoile pour indiquer la jointure externe 

Une  application  qui  doit  accéder  aux  deux  bases  de  données  devrait  donc  être  modifiée  pour 
prendre  en  considération  les  différences  de  syntaxe  pour  un  même  opérateur.  L’interface  API 
contourne  cette  difficulté  et  favorise  la  réutilisation  du  code  en  imposant  au  concepteur  une 

 
Chapitre 9 Interface applicative  49

syntaxe  SQL  dite  ODBC,  qui  fait  disparaître  les  différences  pour  les  reporter  au  niveau  de  la 
traduction  par  un  pilote  (driver)  spécialisé  du  SGBD  cible.  Un  des  arguments  de  ces  fonctions 
représente  lʹordre  SQL  ʺnatifʺ  qui  sera  traduit  par  le  driver  compatible  avec  le  SGBD  cible.  Le 
pilote est bien sûr dévloppé et fourni par le manucfacturier du SGBD. 
 
Les fonctions de l’API utilisées dans lʹapplication sont traduites par le pilote (driver ) particulier 
qui réalise l’accès à un SGBD quelconque sans modification de l’application si le SQL respecte la 
syntaxe  de  ODBC.  LʹODBC  de  Microsoft  est  disponible  sous  forme  dʹune  librairie  dynamique 
(DLL).  
 
En  utilisant  les  fonctions  de  lʹODBC,  une  même  application  peut  donc  accéder  aux  données 
d’une  autre  base  gérée  par  un  logiciel  différent,  sans  modifier  son  code.  Il  suffira  de  spécifier 
une autre base (data source) pour que le gestionnaire de pilotes (GD) utilise celui (la bibliothèque 
de fonctions) qui est approprié pour établir la communication et réaliser l’accès aux données de 
la base.  
Liste de quelques fonctions ODBC 
Il existe plusieurs niveaux de pilotes selon la nature du jeu de fonctions disponibles. Le plus bas 
niveau,  le  CORE,  rassemble  toutes  les  fonctionnalités  communes  à  tous  les  SGBD.  Le  module 
ODBC de Microsoft contient environ 60 fonctions.  
 
Voici quelques‐unes de ces fonctions : 
SQLConnect : établissement d’une connexion avec la BD spécifiée;
SQLDISCONNECT: fermeture de la connexion établie précédemment;
SQLDataSources : énumération des bases disponibles incluses dans le pilote ODBC.INI;
SQLPrepare : préparation d’une clause SQL dynamique pour une exécution ultérieure;
SQLExecDirect : exécution de la clause SQL placée dans un tampon (buffer) de l’application;
SQLNumParams : retour du nombre de paramètres dans une clause SQL;
SQLRowCount : retour du nombre de tuples traités par une clause SELECT, INSERT , UPDATE et
DELETE;
SQLFetch : accès au prochain tuple disponible de la réponse à une requête;
SQLTransact : pour la validation ou le rollback de la transaction courante;
SQLCancel : annulation du traitement de l’ordre SQL en cours;
SQLError : obtention de l’erreur et des informations connexes.

Ces fonctions sont normalisées par le groupe ODBC et leur traduction avec un pilote approprié 
donne naissance à une suite d’ordres DML pour le SGBD cible particulier. L’interopérabilité est 
alors assurée dans un milieu hétérogène.  
Sommaire 
Un  logiciel  SGBD  doit  avoir  une  gamme  d’interfaces  de  programmation  qui  va  de  la  plus 
simple, avec un module autonome pour lʹexécution des clauses SQL, à la plus complexe, avec un 
langage  de  programmation  utilisant  le  SQL  dynamique.  Le  module  autonome  SQLPlus  n’est 
souvent utile qu’au concepteur, lui permettant de formaliser et de tester les clauses SQL dont il a 
besoin pour extraire les informations de la BD  et cela, avant le développement dʹune application 
complexe. L’accès à la base par une application est de loin le mode le plus utile, car il permet à 

 
Chapitre 9 Interface applicative  50

l’application  de  faire  un  traitement  complexe  sur  les  données  obtenues  et  de  les  afficher  avec 
une  interface  conviviale  adaptée  à  son  environnement  d’exploitation.  Le  ODBC  rend  possible 
l’accès aux données par une application développée en un langage quelconque pour peu que le 
pilote de l’ODBC soit rendu disponible. Lʹaccès à des bases de données hétérogènes peut aussi 
être fait par le moyen des fonctions de la librairie ODBC.  

 
Chapitre 10 Théorie de la normalisation   51

Chapitre 10   
Théorie de la normalisation d’une base de données 
 
Nous  avons  abordé  la  normalisation  du  schéma  relationnel  lors  de  l’étude  du  modèle 
relationnel. Dans cette section, nous ferons un rappel des définitions de base en les formalisant 
davantage  et  nous  étudierons  avec  plus  de  détails  les  fondements  théoriques  de  la 
normalisation. Les formes normales supérieures, la FN4 et FN5 sont introduites et illustrées par 
des  exemples  complets.  Les  notions  présentées  dans  ce  chapitre  constituent  les  principaux 
éléments du corpus théorique sur le processus de normalisation des bases de données et sur la 
validation des formes normales 4 et 5.    
Schéma canonique d’une table 
Dans  la  conception  d’une  base  de  données  (BD),  les  schémas  de  relation  obtenus  et  constitués 
par  les  attributs  ne  sont  pas  uniques.  Certains  schémas  qui  regroupent  les  attributs  sont 
préférables à d’autres! Ils doivent être recherchés pour avoir une base de données relationnelle 
dite canonique4. La normalisation fondée sur les dépendances a comme objectif d’identifier ces 
schémas canoniques. 
10.1 Dépendance fonctionnelle dans une relation R 
Soit  une  relation  R(X,  Y,  Z)  dans  laquelle  X,  Y,  Z  sont  des  ensembles  d’attributs  avec    X  ∪ Y 
∪ Ζ = R.  L’extension de la relation de schéma R est notée r (minuscule r). Les variables de tuple 
t1  et  t2    servant  à  définir  une  dépendance  fonctionnelle  (DF)  dans  R  entre  X  et  Y.  Cette 
dépendance  est    notée  ainsi  DF :  X‐‐> Y  ce  qui  signifie  que  Y  est  en  dépendance  fonctionnelle5 
avec X ou, en d’autres mots, X détermine Y avec X⊆ R et Y ⊆ R, ∀t1, t2 de l’extension r : 
 
  si t1[X] = t2[X] ⇒ t1[Y] = t2[Y]  (cette définition est une implication logique) 
 
NB :  si  X  ‐‐> Y  dans  R,  cela  nʹimplique  pas  que  Y  ‐/‐> X,  car  la  définition  de  la  dépendance 
fonctionnelle nʹest pas une équivalence logique. Le X est appelé le déterminant (antécédent) et Y 
le  déterminé  (conséquent).  Il  faut  noter  que  si  l’antécédent  est  faux  quel  que  soit  la  paire  de 
tuples, alors l’implication est vraie et donc la DF est validée. Pour infirmer une dépendance, il 
suffit  d’un  seul  contre  exemple  formé  avec  une  paire  de  tuples  trouvés  dans  une  extension 
valide. En revanche, lʹexistence d’une DF dans une relation relève de la sémantique. Il est rare 
voire impossible dans la réalité, d’examiner une extension volumineuse de tuples dans laquelle 
on retrouverait toutes les dépendances possibles de la relation.  
 
Par  contre,  l’analyse  informatique  fournit  l’information  nécessaire  pour  spécifier  certaines 
contraintes  et  de  ce  fait,  valider  ou  pas  certaines  dépendances  fonctionnelles  ou  les  autres  que 
nous étudierons plus loin dans ce chapitre. 
                                                    

 
Chapitre 10 Théorie de la normalisation   52

Projet :  noProjet  budget  site 


  P2  4,0M$  Québec 
  P3  2,7M$  Montréal 
  P4  2,7M$  Québec 
  P1  3,2M$  Lévis 
ajout refusé    ‐‐‐>P2   4,0M$  Gaspé 
 10.1 
                                                            
Si cette extension est représentative de tous les états possibles pour cette relation fort simple, il 
est possible après examen d’énoncer les deux DF suivantes : 
F = { noProjet ‐‐> budget;  noProjet ‐‐> site } 
 
Le  même  ensemble  de  DF  peut  être  aussi  formulé  autrement  en  fusionnant  les  deux 
dépendances qui partagent le même antécédent pour obtenir la seule DF suivante : 
F = { noProjet ‐‐> budget,  site } 
Cette dernière dépendance fonctionnelle est équivalente aux deux premières. 
 
Lʹensemble des DF dans une BD est représenté par le symbole F qui regroupe que les énoncés de 
dépendance  valides.  Par  exemple,  lʹajout  dʹun  tuple  (P2,  4,0M$,  Gaspé)  ne  pourra  se  faire, 
puisque lʹétat résultant contredirait une DF de F : le projet P2 correspondrait à deux sites.  
Importance des DF dans une base de données 
Les  dépendances  fonctionnelles  imposent  des  contraintes  aux  extensions  de  relation ;  elles 
seront mises à contribution pour qualifier les schémas qui évitent les anomalies potentielles dans 
lʹexploitation de la BD. Ces anomalies sont des états possibles qui découlent de la mise à jour, de 
la  suppression  et  de  l’ajout  de  tuples.  Certaines  contraintes  peuvent  âtre  intégrées  dans  le 
dictionnaire  de  données  et  utilisées  par  le  noyau  du  SGBD  pour  valider  l’ajout  ou  la 
modification  d’un  tuple.  Par  exemple,  cela  peut  être  une  contrainte  d’intégrité    de  type  clé 
primaire  ou  clé  candidate.  Certaines  autres  contraintes  ne  peuvent  être  implémentées  aussi 
facilement. 
Contrainte d’intégrité de type dépendance fonctionnelle. Une contrainte dʹintégrité est donc une 
assertion  logique  exprimée  par  une  formule  sans  variable  libre  et  dont  l’interprétation  est 
toujours vraie. Elle est aussi une formule logique fermée qui doit être satisfaite pour toutes les 
interprétations ou état de la relation. 
La dépendance fonctionnelle (DF) est une contrainte dʹintégrité exprimée par la formule logique 
fermée suivante :  
∀ti, ∀tj (ti[X] = tj[X] ⇒ ti[Y] = tj[Y])  
 
Et  cela  pour  ∀r  (c’est‐à‐dire  pour  toutes  les  interprétations  de  R).  Un  tuple  est  acceptable  par 
rapport  à  l’extension  r  de  R  s’il  vérifie  toutes  les  formules  fermées  qui  expriment  les 
dépendances fonctionnelles définies dans F dont le déterminant ∪ déterminé ⊆ R. 
Dépendance fonctionnelle totale (DFT) 

 
Chapitre 10 Théorie de la normalisation   53

Si X‐‐> Y et si ¬∃ X’ tel que X’ ‐‐> Y et que X’ ⊆ X, alors X‐‐> Y est une dépendance totale (DFT) 
qui est notée X ‐t‐> Y ou X==> Y.  
Une DFT est aussi une dépendance fonctionnelle dont le déterminant est minimal. Au contraire, 
s’il  existe  X’⊆  X,  alors  Y  est  en  dépendance  fonctionnelle  partielle  sur  X,  car  Y  dépend  dʹune 
partie de X.  
Voici quelques exemples sur les DFT : 
a) R (A, B, C) avec F = {A‐‐> B; A‐‐> C; A, B ‐‐> C} 
Quelles sont les DFT dans R ?  
Réponse :  
Les  DFT  sont  {A==>  B  et  A  ==>  C},  car  le  déterminant  est  transitivité.  Cependant,  A,  B  =/=>  C, 
n’est pas une DFT car A ‐‐> B  dans F.  
b) R(A, D, E) avec F = {A‐‐> D; A ‐‐> E; D ‐‐> E} 
Quelles sont les DFT parmi les DF suivantes : D, A ‐‐> E; A, E ‐‐> D; A ‐‐> E ?  Il nʹy a que A‐t‐> E 
comme  dépendance  fonctionnelle  totale,  car  les  autres  ont  un  sous‐ensemble  qui  détermine  le 
déterminé. 
Normalisation du modèle relationnel 
Considérons  le  modèle  E/R  simple  ci‐dessous  qui  est  transformé  en  modèle  tabulaire 
comprenant  trois  tables  avec  leurs  extensions  respectives.  La  troisième  table,  Machine  est 
nécessaire en raison du lien d’association n‐m entre les deux classes du MCD E/A. 
 
  Operateur  Machine 
  nas*  1..*  1..* noMachine
  nom  * 
  marque 
  EstAutorise  debit 
  dateValidation totServ 
  ate o ie
Operateur (nas*, nom) 
Machine (noMachine*, marque, debit, totServ, categorie) 
EstAutorise (nas*, noMachine*, dateValidation) 
 
 10.2 : Modèle E/A simple et le MRD correspondant 
 
La  relation  Machine  a  une  extension  comme  celle  ci‐dessous  que  nous  supposerons 
représentative  de  tous  les  états  possibles  de  R.  i.e.  dans  laquelle  toutes  les  dépendances  sont 
vérifiées. 
        
machine :  noMachine  marque  debit  totServ  categorie 
   M812   Husky  60  25   tour 
   M975   Husky  60  34   tour 
   M756  Cincinnati  75  50   perceuse 
 10.3 
Les DF de F sont alors les suivantes :   

 
Chapitre 10 Théorie de la normalisation   54

F = {noMachine ‐‐> marque, debit, totServ, categorie;  marque, categorie ‐‐> debit} 
                      
La  clé  de  cette  relation  est  formée  avec  l’attribut  noMachine  parce  que  tous  les  attributs  de  la 
relation sont en dépendance fonctionnelle avec celui‐ci. Est‐ce le meilleur schéma de table pour 
représenter  l’Entité  Machine ?  Par  exemple,  si  la  machine  M756  est  mise  à  la  ferraille,  le  tuple 
correspondant  doit  être  supprimé  de  l’extension  de  la  relation;  il  y  a  perte  d’information,  à 
savoir  que  cette  machine    M756  de  marque  Cincinnati  est  une  perceuse  dont  le  débit  de 
production  est  de  75  unités  à  l’heure.  Si  cette  information  a  une  importance  pour  l’organisation,  la 
suppression constitue une perte de la donnée rattachée à une faiblesse de ce schéma. Ce dernier 
peut‐il être remplacé par un autre, plus efficace dans la représentation des faits de la base, i.e. en 
évitant  cette  perte  d’information ?  La  réponse  est fournie  par  la  normalisation  qui  séparera  les 
données factuelles sur l’équipement de celles spécifiques aux activités de l’usine. 
Voici deux nouveaux schémas construits avec les mêmes attributs : 
  R1 (noMachine, marque, categorie, totServ) 
    avec la DF : noMachine ‐‐> marque, categorie, totServ 
  R2 (marque, categorie, debit)  
    avec la DF : marque, categorie ‐‐> debit 
 
Chaque  schéma  de  relation  est  formé  avec  les  DF  de  F  qui  partagent  le  même  déterminant. 
L’extension de chaque relation est alors obtenue par la projection de la relation initiale Machine 
sur les attributs des deux nouvelles relations. 
 
r1 :   noMachine*  marque  categorie  totServ 
  M812  Husky  tour  25h 
  M975  Husky  tour  34h 
  M756  Cincinnati  perceuse  50h 
                                            
r2 :  marque*  categorie*  debit 
  Husky  tour  60 
  Cincinnati  perceuse  75 
  Husky  perceuse  60 
 10.4 
 
Les deux relations obtenues rendent le schéma de la BD redondant, mais diminue la redondance 
des  données.  La  redondance  du  schéma  n’est  pas  en  soi  un  défaut  puisqu’elle  est  due  quʹà 
l’apparition  d’une  clé  étrangère  composée,  {marque,  categorie}.  Cette  clé  étrangère  peut  être 
l’objet d’un contrôle automatique par le SGBD lors de la vérification par des procédures internes 
du SGBD de la contrainte dʹintégrité référentielle.  
Équivalence des relations 
Est‐ce que les deux relations R1 et R2 de la  10.4 sont équivalentes à la relation initiale Machine, 
et cela  tant du point de vue du schéma que de celui des données? Du côté du schéma, l’union 
des attributs de R1 et R2 fournit le schéma d’origine pour la table Machine. Donc le schéma est 
équivalent. Examinons maintenant l’équivalence au plan des données. 

 
Chapitre 10 Théorie de la normalisation   55

 
Soit R1, R2 et R3, il y a équivalence entre R3 et les deux autres relations, R1 et R2, si et seulement 
si la condition suivante est vérifiée :   R3 = R1 |x| R2 où |x| est l’opérateur de jointure naturelle. 
En d’autres mots, la relation initiale doit correspondre au résultat de la jointure qui doit inclure 
toutes les données de R3 sans ajouter d’autres données. Donc, dans l’exemple ci‐dessus, le calcul 
de la jointure reconstruit la table Machine. Il existe cependant d’autres relations définies avec les 
mêmes  attributs  qui  ne  sont  pas  équivalentes  à  Machine,  car  leur  jointure  ne  reconstitue  pas 
l’extension d’origine.  
Décomposition dʹune relation 
Soit une relation R(Α, Β, C) où le schéma de la relation est A ∪ B ∪ C avec A ∩ B ∩ C = vide et 
les dépendances F = {A‐>B ; A‐>C}. En dʹautres mots, les ensembles d’attributs A, B et C sont des 
partitions dʹattributs. S’il existe une DF dans F, telle que A ‐‐> B, alors le schéma de la relation R 
peut être décomposé en deux relations et cela sans perte de données :  
R1(A, B) et R2 (A, C)  
               
Le schéma de la deuxième relation est obtenu en supprimant les attributs du déterminant de la 
DF,  soit  {A,  B,  C}  –  {déterminant  dans  A‐>B}.  Cette  décomposition  est  valide,  seulement  si  la 
jointure des relations obtenues donne la relation initiale comme résultat de leur jointure : 
R(A, B, C) = R1(A, B) |x| R2(A, C) 
 
Or  la  jointure  est  sans  perte  si  et  seulement  si    l’attribut  commun  des  deux  relations  obtenues 
détermine  tous  les  autres  attributs  dans  au  moins  une  relation.  Dans  l’exemple  précédent 
l’attribut commun dans R1 et R2 est A et dans F, A ‐‐> B. 
    
Dans lʹexemple qui suit, la relation Gestion possède deux dépendances fonctionnelles : 
Gestion (noProjet, budget, site) avec F= {noProjet ‐‐> budget;  noProjet ‐‐> site} 
La clé est définie par les DF de F, soit noProjet puisque cet attribut détermine tous les attributs. 
 
La relation Gestion pourrait cependant être décomposée sans perte en deux relations : 
R1 (noProjet, budget)  R2 (noProjet, site) 
Cette division de la relation Gestion nʹest pas souhaitable dans ce cas‐ci, puisque toutes les DF 
de F ont le même déterminant. En théorie, toute l’information qu’il était possible de représenter 
dans  la  relation  initiale  Gestion  peut  aussi  l’être  avec  les  nouvelles  relations  R1  et  R2.  S’il  est 
utile pour des fins de performances ou de confidentialité de représenter les budgets de projets 
séparément  de  leurs  sites  alors  ces  dernières  peuvent  être  renommées  pour  être  plus 
significatives: BudgetProjet et SiteProjet. Une jointure sera cependant nécessaire pour obtenir le 
budget et le site d’un projet particulier. 
                                   
Voici une décomposition invalide d’une relation au plan des données :  
R(A, B, C,) avec F = {A‐‐>B; A ‐‐>C} 
  
La  décomposition  de  R  en  deux  relations :  R1(A,  B)  et  R2(B,  C)  est  invalide,  car  l’attribut 
commun  B  ne  détermine  pas  A  ou  C  dans  lʹune  ou  lʹautre  des  deux  relations  obtenues  par 

 
Chapitre 10 Théorie de la normalisation   56

décomposition  :  B  ‐/‐>  A  et  B  ‐/‐>  C.  Bien  que  la  décomposition  préserve  l’équivalence  du 
schéma, elle ne garde pas l’équivalence des données. 
Théorème de Heath  
Si lʹon a  une relation R (A, B, C) avec A, B, et C les ensembles d’attributs du schéma. Si R vérifie 
la dépendance fonctionnelle A ‐> B, alors R est aussi égale à la jointure des projections de R soit 
R1[A, B] et R2[A, C]. En d’autres mots, l’attribut commun au deux nouveaux schémas détermine 
tous les attributs dans une des relations6.  Notons aussi que la décomposition peut se faire avec 
une DF obtenue par fusion de toutes les DF de F qui partagent le même déterminant. 
 
Démonstration :  Il  faut  démontrer  que  les  deux  projections  R1  et  R2  obtenues  de  R(A,  B,  C) 
peuvent  faire  l’objet  d’une  jointure  sans  perte  et  cela  pour  obtenir  l’extension  initiale.  En 
premier, nous démontrons que chaque projection d’un tuple de R donne un fragment de tuple 
qui  appartient  à  R1  ou  à  R2  et  rien  de  plus.  Ainsi  aucun  tuple  de  R  nʹest  perdu  par  la 
décomposition.  Soit un tuple quelconque (a, b, c) ∈ r, alors un tuple quelconque de la projection 
de R sur (A, B), c’est‐à‐dire (a, b) ∈ R1 et la projection de R sur (A, C ) donne aussi un tuple  (a, c) 
∈  R2.  Donc  le  tuple  (a,  b,  c)  appartient  à  la  jointure  R1  |x|  R2.  Donc  pour  tout  tuple  de 
l’extension  initiale  r,  il  existe  deux  projections  qui  se  recomposent  pour  donner  un  tuple  de  la 
jointure.  
 
Il  faut  maintenant  démontrer  que  chaque  tuple  de  la  jointure  calculée  est  obligatoirement  un 
tuple de r et qu’aucun tuple étranger n’est créé par cette jointure. Soit (a, b, c) un tuple du calcul 
de  R1  |x|  R2.  La  formation  d’un  tel  tuple  suppose  que  les  tuples  (a,  b)  ∈  R1  et  (a,  c)  ∈ R2. 
Supposons  maintenant  quʹil  existe  un  tuple  quelconque  s  de  R,  soit  (a,  b’,  c),  avec  une  valeur 
quelconque pour b’, la projection de s sur (A, C) donne un tuple (a,c) ∈ R2. Par conséquent, la 
projection (a, b’)∈ R1 devrait être aussi dans l’extension de R1. Alors, (a, b) ∈ R1 et (a, b’) ∈ R1. 
Si  tel  est  le  cas,  il  y  a  infirmation  de  la  DF  donnée  en  hypothèse  qui  stipule  que  A‐‐>  B.  Par 
conséquent, il faut que b = b’ pour que seul le tuple (a, b, c) ∈ R. Le théorème inverse est faux. 
Lorsqu’une relation R(A, B, C) est obtenue par la jointure de deux relations R1(A, B) et R2(A, C), 
il ne s’en suit pas que la dépendance fonctionnelle A ‐‐> B soit valide dans R.                 
Propriétés des dépendances fonctionnelles  
Soit  une  relation  R(X,  Y,  Z);  cette  dernière  est  formée  de  trois  ensembles  d’attributs 
mutuellement  exclusifs.  Cette  relation  possède  certaines  propriétés   qui  seront  mise  en  œuvre 
dans la normalisation et/ou le design d’une base de données. 
1‐ Réflexivité et dépendances fonctionnelles triviales 

Si Y ⊆ X ==>   X‐‐>Y. Les éléments d’un ensemble déterminent ceux du sous-ensemble quelle que
soit la relation hôte, pour autant que le déterminé et le déterminant soient dans la même
relation. On dit que la DF est indépendante du contexte, c’est-à-dire qu’il suffit que les attributs
qui composent le déterminant et le déterminé soient présents dans un même schéma de relation,
pour que la dépendance existe entre ces attributs.
 

 
Chapitre 10 Théorie de la normalisation   57

R :  A  B  C 
  a1  b1  c1 
  a2  b2  c2 
  a3  b3  c3 
  a1  b2  c1 
  a1  b1  c4 
 10.5 
 
Un attribut A, du schéma de R est un sous‐ensemble de {A, B} et ce dernier détermine chacun 
des ses éléments. 
Puisque A ⊆ {Α, Β} on a que  Α, Β ‐‐> A et A, B ‐‐> B 
Démonstration : Notons que si t1[A, B] = t2[A, B] cela implique que t1[A] = t2[A] et t1[B] = t2[B] 
pour toute paire t1 et t2 d’une extension quelconque r de R.  
Si t1[A, B] = t2[A, B], alors t1[A] = t2[A] et, selon la définition de la DF, A, B ‐‐> A . 
Cas particulier :  DF triviale 
Si A ⊆ A alors A ‐‐> A.  Les DF dérivées par réflexivité sont dites triviales. 
2‐ Augmentation du déterminant dʹune DF  
Il est toujours possible dʹaugmenter un déterminant d’une DF avec un attribut quelconque de R 
sans invalider la dépendance fonctionnelle initiale. 
Supposons le schéma R composé des attributs X, Y, Z.  
  Avec Z ⊆ R, si X ‐‐> Y est valide dans R, alors X, Z ‐‐> Y. 
Démonstration  
Avec la prémisse X ‐‐> Y, alors pour toute paire de tuples  t1 et t2 ∈ r, nous avons que  
t1[X] = t2[X] ⇒ t1[Y] = t2[Y]. Si la valeur t1[Z] est ajoutée au tuple t1[X], alors (t1[X], t1[Z]) = t1[X, 
Z]. Il en est de même pour t2[X, Z].  Cet ajout ne change pas la valeur de vérité de lʹimplication 
initiale qui peut être reformulée ainsi : 
t1[X, Z] = t2[X, Z] ⇒ t1[Y] = t2[Y]     
Par  conséquent,  en  référence  à  la  définition  de  la  DF,  on  a  alors  que  X,  Z  ⇒  Y.  Avec  une  DF 
valide,  on  peut  toujours  ajouter  un  attribut  quelconque  au  déterminant  pour  obtenir  une 
nouvelle DF toujours valide. 
Cas particulier : addition de la dépendance triviale Z ‐‐> Z 
Lʹaugmentation du déterminant et du déterminé avec une DF triviale symétrique est possible et 
fournit une DF valide. Supposons la DF triviale Z‐‐> Z avec la DF X ‐‐> Y, alors X, Z ‐‐> Y, Z. 
Démonstration 
De  l’augmentation  avec  Z,  on  a  que  X,  Z  ‐‐>  Y,  ce  qui  est  équivalent  à  l’assertion  de  la  DF :  si 
t1[X, Z] = t2[X, Z], alors t1[Y] = t2[Y]. Si le deuxième membre est augmenté de la même valeur de 
Z,  cette  égalité  demeure :  t1[Y,  Z]  =  t2[Y,  Z]  En  se  reportant  à  la  définition  de  la  DF,  il  faut 
conclure que X, Z ‐‐> Y, Z. 
3‐ Transitivité  
Si les dépendances X ‐‐> Y et Y ‐‐> Z sont valides dans une même relation R, alors X ‐‐> Z lʹest 
aussi dans R. 

 
Chapitre 10 Théorie de la normalisation   58

Démonstration  
Supposons une extension r qui vérifie les DF de l’hypothèse et deux tuples quelconques t1 et t2 
∈ r . La projection d’un tuple sur un attribut donne une valeur. Donc les propositions suivantes 
sont vraies : 
t1[X] = t2[X] ⇒ t1[Y] = t2[Y] et 
t1[Y] = t2[Y] ⇒ t1[Z] = t2[Z] sont vraies par définition. 
Donc  t1[X] = t2[X] ⇒ t1[Z] = t2[Z] est une formule logique vraie. Par définition cette implication 
représente la DF X ‐‐> Z, qui est donc valide dans l’extension r de R. 
4‐ Pseudo transitivité 
Si X ‐‐> Y et Y, W ‐‐> Z dans R, alors X, W ‐‐> Z est aussi valide dans R. 
Démonstration  
  (1) X ‐‐> Y      par hypothèse 
  (2) W ‐‐> W      une triviale 
  (3) X, W ‐‐> Y, W  par augmentation de (1) 
  X, W ‐‐> Z    par transitivité avec une DF de F.  
5‐ Union des déterminés (ayant le même déterminant) de F 
Si X ‐‐> Y et X ‐‐> Z sont valides dans le schéma R, alors X ‐‐> Y, Z. 
Démonstration  
  (1) X ‐‐> Y     une DF de F 
  (2) Z ‐‐> Z    par réflexivité    
  (3) X, Z ‐‐> Y, Z   par augmentation de (1) avec (2) 
  (4) X ‐‐> Z    une DF de F 
  (5) X ‐‐> X     une réflexive 
(6) X ‐‐> X, Z     par augmentation de (4) avec (5) 
  (7) X ‐‐> Y, Z    par transitivité de (6) et (3). 
Exemple :  
MachineOutil (noMachine*, marque*, debit) 
avec les dépendances fonctionnelles suivantes:  
      F = {noMachine ‐‐> marque;  noMachine ‐‐> debit} 
 
  machineOutil:  noMachine *  marque  debit 
  M812  Husky  60 
  M975  Husky  60 
  M756  Cincinnati  75 
 10.6 
 
Par la propriété de l’union des déterminés, on peut annoncer à partir de F que : 
noMachine ‐‐> marque, debit 
L’examen de l’extension de la table MachineOutil ne contredit pas cette nouvelle DF dérivée : 
 

 
Chapitre 10 Théorie de la normalisation   59

Décomposition dʹune DF 
Si X ‐‐> Y, Z est valide dans l’extension r du schéma R, alors X ‐‐> Y et X ‐‐> Z le sont aussi dans 
le  même  schéma  R.  On  a  alors  {X  ‐‐>  Y,  Z}  ≡    {X  ‐‐>  Y  et  X  ‐‐>  Z}.  On  peut  aussi  écrire  cette 
décomposition sous la forme d’une conséquence logique :  
X ‐‐> Y, Z |= X ‐‐> Y  
X ‐‐> Y, Z |= X ‐‐> Z  
Démonstration  
  (1) X ‐‐> Y, Z      par hypothèse : une DF de F 
  (2) Y, Z ‐‐> Y (car Y ⊆ Y, Z)  par réflexivité : DF triviale    
  (3) X ‐‐> Y      par transitivité avec (1) et (2). 
 
  (1) X ‐‐> Y, Z      par hypothèse : une DF de F   
  (2) Y, Z ‐‐> Z      par réflexivité : DF triviale 
  (3) X ‐‐> Z.      par transitivité avec (1) et (2). 
 
Le  déterminé  complexe  (ou  multiattribut)  peut  être  transformé  pour  obtenir  un  déterminé 
simple,  transitivité,  au  prix  d’une  augmentation  du  nombre  de  DF  dans  F.  Certains  auteurs 
utilisent  lʹexpression  de  dépendance  élémentaire  lorsque  le  déterminé  est  formé  dʹun  seul 
attribut.   
7‐ Projectabilité dʹune dépendance fonctionnelle 
Si X ‐‐> Y est valide dans R(W), où W est l’ensemble des attributs du schéma alors X ‐‐> Y’ dans 
R’(Ω’) lorsque les conditions suivantes sont vérifiées : 
  W’ ⊆ W et Y’ ⊆ W 
  X ⊆ W’ et Y’ ⊆ W’∩ Y )  <‐‐ 
En  d’autres  mots,  une  projection  de  r  sur  les  attributs  de  W’  notée  rʹ  comporte  une  DF  valide 
pour X sur les attributs communs entre Y et les attributs de rʹ.  
 
Exemple : Soit la relation  r (A, B, C, D, E) avec A ‐‐> B, C donnée comme une DF valide. 
La projection r’(A, B, D) obtenue par la projection de r sur le ou les attributs communs entre le 
déterminé de la DF de r et le nouveau schéma de rʹ. Les attributs communs entre les projections 
{B, C} et {A, B, D} sont {B}. Donc, la DF A ‐‐> B est aussi valide dans le schéma de la nouvelle 
relation, R’.  
 
Autre exemple :   
Soit  R(A, B, C, D) avec A ‐‐> C, D. La projection r’ (A, B, C) de r vérifie la DF : A ‐‐> C.  
L’intersection entre (C, D) et (A, B, C) est C et donc A‐‐> C est donc valide. 
8‐ Addition de deux dépendances 
Si X ‐‐> Y et W ‐‐> Z sont deux DF valides dans R, alors X, W ‐‐> Y, Z est aussi valide dans R. 
Démonstration  
X, W ‐> W   une triviale  
W ‐‐> Z  une hypothèse 
X, W ‐> Z  **  par transitivité  

 
Chapitre 10 Théorie de la normalisation   60

X, W ‐> X  une triviale 
X ‐‐> Y une hypothèse  
X, W ‐‐> Y **  par transitivité  
X, W ‐‐> Y, Z  par addition  
 
Sommaire des propriétés des DF 
Les propriétés les plus importantes des dépendances fonctionnelles sont les suivantes: 
a) Tout déterminant d’une DF peut être augmenté avec n’importe quel attribut; 
b) Tout déterminé peut être augmenté avec les attributs du déterminant; 
c) Un ensemble quelconque d’attributs détermine chacun de ses sous‐ensembles; 
d)  Un  déterminé  composé  de  plusieurs  attributs  peut  toujours  être  réduit  en  un  déterminé 
monoattribut. Il en résulte une augmentation du nombre de DF dans F. 
 
Exemples : Soit R(A, B, C, D), une relation avec des DF valides: 
a) Si A ‐‐> B est valide dans R, par augmentation avec une triviale, C ‐‐> C,  
on obtient : A, C ‐‐> B, C qui est aussi valide pour R. De même par décomposition pour obtenir 
les df équivalentes : A, C ‐‐> B et A, C ‐‐> C . 
 
b) Si A, C ‐‐> D est valide dans R, alors A, C ‐‐> A, D  est aussi valide dans R par augmentation 
du déterminé : 
  A, C ‐‐> D, C  ajout de C ‐‐> C 
  A, C ‐‐> A, D par ajout de A‐‐> A 
  A, C ‐‐> A et A, C ‐‐> D sont les dépendances équivalentes. 
On peut donc conclure que tout attribut du déterminant peut être ajouté au déterminé. 
                                   
c) Les triviales suivantes sont aussi valides dans R : 
  A, B, C ‐‐> A;  A, B, C ‐‐> B;  A, B, C ‐‐> C (réflexivité) 
 
Les  propriétés  étudiées  précédemment  ne  sont  pas  toutes  essentielles,  ni  minimales.  Certaines 
peuvent être déduites à partir de trois propriétés essentielles que William Armstrong (1974)  7 a 
présentées comme étant en quelque sorte les axiomes d’une théorie du premier ordre. 
Axiomes dʹArmstrong 
Les axiomes dʹArmstrong sont les propriétés des DF à partir desquelles toutes les autres peuvent 
être dérivées : 
a) la réflexivité : Y ⊆ X ⇒ X ‐‐> Y (cas particulier : X ‐‐> X); 
b) lʹaugmentation : X ‐‐> Y et que Z ⊆ W ⇒ X, W ‐‐> Y, Ζ; 
c) la transitivité : X ‐‐> Y et Y ‐‐> Z dans R, alors X ‐‐> Z; 

 
Chapitre 10 Théorie de la normalisation   61

Dérivabilité d’une dépendance fonctionnelle : F |‐ f 
Une  dépendance  fonctionnelle  (DF)  particulière  notée  f  est  dérivable  de  l’ensemble  F  par  les 
propriétés des dépendances fonctionnelles (notée F|‐ f) si et seulement s’il existe une suite (f1, f2, 
f3,  ...  fn)  de  dépendances  fonctionnelles,  chacune  vérifiant  l’une  ou  l’autre  des  propriétés 
suivantes : 
a) fn est la dépendance recherchée, soit le résultat; 
b) pour ∀i où 1<=i<=n, fi ∈ F où fi peut être obtenue à partir de F en utilisant les trois axiomes 
comme seules règles d’inférence. 
 
Exemples d’une suite de d’inférences :  
Dériver la DF suivante :  I, C, D ‐‐> Z de F = {A, B, C‐‐>Z; D, B ‐‐> A; I, C ‐‐> B}.  
Il faut  démontrer que  F |‐ I, C, D ‐‐> Z. 
(1) I, C ‐‐> B  par hypothèse 
(2) D ‐‐> D  triviale 
(3) I, C, D ‐‐> D, B  par augmentation de (1) par (2) 
(4) D, B ‐‐> A  par hypothèse 
(5)  I, C, D ‐‐> A  par transitivité 
(6) C ‐‐> C 
(7) I, C, D ‐‐> A, C   par augmentation 
(8) I, C ‐‐> B  par hypothèse 
(9) I, C, D ‐‐> A, C, B   par addition de deux DF : 7 et 8 
(10) A, B, C ‐‐> Z  par hypothèse 
(11) I, C, D, ‐‐> Z  de 9     
 
En supposant que F = {A ‐‐> B;  B, C ‐‐> D;  B, D, E ‐‐> J}, dérivez la DF suivante  A, C, E ‐‐> J 
(1)  A ‐‐> B  par hypothèse 
(2)  C, E ‐‐> C, E  triviale 
(3) A, C, E ‐‐>B, C, E   par addition de deux DF 
(4) B, C ‐‐> D  par hypothèse 
(5) B, E ‐‐> B, E  augmentation avec la triviale E‐‐> E 
(6) B, C, E ‐‐> B, E, D   par  addition de deux  DF 
(7) A,C,E ‐‐> B,E,D  transitivité (3) et (6) 
(8) B, D, E ‐‐> J par hypothèse 
(9) A, C, E ‐‐> J   par transitivité avec (7) et (8);    
Conséquence logique F |= f 
Soit une relation R(W), où W est l’ensemble des attributs du schéma de R et F un ensemble de 
DF  définies  dans  W.  Une  dépendance  fonctionnelle  f  appelée  aussi  DF  est  une  conséquence 
logique  de  F  (notée  F  |=  f)  si  toute  extension  (analogue  à  l’interprétation  )  qui  vérifie  F  vérifie 
aussi f.  

 
Chapitre 10 Théorie de la normalisation   62

Validité 
Si F |‐ f, alors F |= f dans  R. Si lʹon peut formellement dériver la dépendance fonctionnelle f de  
F, alors toute  extension de la relation vérifie f. Une dérivation formelle d’une DF correspond à 
une contrainte sémantique dans les données de R. 
Complétude sémantique 
Si F |= f, alors F |‐ f. Si la dépendance fonctionnelle f est vérifiée par toute extension de R, alors f 
est une formule qui peut être formellement dérivée par les règles dʹinférence dʹArmstrong. 
Exemple  
Soit la relation R(A, B, C, D) avec  F= {A‐‐> B; A‐‐>C; B, C ‐‐> D}, démontrez que la DF A, C ‐‐>D 
peut être dérivée de F.  
(1) A  ‐‐> B    hypothèse  
(2) A, C ‐‐> B, C   par augmentation avec C ‐‐> C 
(3) B, C ‐‐> D    hypothèse 
(4) A, C ‐‐> D    par transitivité  
(5) F |= A, C ‐‐>D     
                        
Les axiomes de Armstrong sont valides et complets par rapport à F. Valides parce que toute DF 
qui  peut  être  dérivée  de  F  est  aussi  vérifiée  dans  toute  extension  de  R  qui  vérifie  F.  Ils  sont 
complets  parce  que  toute  dépendance  vérifiée  ou  découvertes  dans  toutes  les  extensions  de  R 
peuvent être dérivées à partir de F.  
 
10.2 Fermeture F+ dʹun ensemble de dépendance fonctionnelles 
La  fermeture  est  définie  comme  étant  l’ensemble  de  toutes  les  dépendances  fonctionnelles  f 
dérivables de F à partir des axiomes de Armstrong. Elle est notée F+. 
  F+ = {f | F |‐ f}  
Sur  le  plan  sémantique,  la  fermeture  peut  être  considérée  comme  l’ensemble  des  DF,  qui  sont 
des conséquences logiques de F :  F+ = {f | F |= f}                            
Cas particulier  
Si F+ = F, alors F est une famille complète de DF. 
Le  schéma  relationnel  d’une  base  de  données  doit  vérifier  toutes  les  DF  de  la  fermeture.  Plus 
particulièrement,  toute  relation  R  doit  vérifier  toute  DF  de  F+  dont  les  attributs,  déterminés  et 
déterminants, sont dans son schéma.  

Calcul détaillé de la fermeture F+ 
Le  calcul  de  la  fermeture  de  F  est  fini,  mais  peut  devenir  rapidement  un  calcul  long  avec 
plusieurs dépendances fonctionnelles. La procédure détaillée est la suivante :   
              
Procédure FERMETURE 
1‐  Dérivation  de  toutes  les  triviales  à  partir  de  F  après  sa  transformation  en  dépendances 
élémentaires; 
2‐ F1 = F ∪ triviales; 
3- Dérivation de toutes les DF par augmentation des déterminants
de F1 avec les attributs de F. On obtient F2 = F1 ∪ augmentées;

 
Chapitre 10 Théorie de la normalisation   63

4‐ Dérivation de toutes les DF par transitivité  
F3 = F2 ∪ transitives. 
Lʹensemble résultant de lʹunion des DF dérivées par les axiomes donne la fermeture de F. 
 
Exemple : R(A, B, C) avec F ={A ‐‐>B; A ‐‐> C} 
a) les DF réflexives fournissent les DF triviales :  
    A ‐‐> A;  A, B ‐‐> A, B;  A, B, C ‐‐> A, B, C; 
    B ‐‐> B; A, C ‐‐> A, C;  A, B, C ‐‐> A, B; 
    C ‐‐> C;  B, C ‐‐> B, C;  A, B, C ‐‐> A, C; 
    A, B ‐‐>A;  A, B, C ‐‐> B, C; 
    A, B ‐‐>B;  A, B, C ‐‐> A; 
    A, C ‐‐>A;  A, B, C ‐‐> B; 
    A, C ‐‐>C;  A, B, C ‐‐> C; 
    B, C ‐‐> B; 
    B, C ‐‐> C; 
Il  faut  remarquer  la  redondance  introduite  par  les  DF  triviales  qui  ne  sont  pas  élémentaires 
(italique gras). Les premières n’ont plus à être produites; seules les df élémentaires obtenues par 
augmentation du déterminant suffisent au calcul de la fermeture. 
 
b) les DF augmentées :   
A, B‐‐> A;   A, B,C ‐‐>A ;   
A, C ‐‐> A;  A, B, C ‐‐>B   
B, A ‐‐> B;   etc 
B, C  ‐‐> B;   
Les  dépendances  augmentées  avec  les  triviales  ne  produisent  pas  de  nouvelles  DF ;  elles  sont 
aussi redondantes. Seules les augmentées obtenue avec les DF de F sont à conserver.              
 
c) Les DF transitives : {Toutes les DF dérivées par implication logique à partir des dépendances 
de  F  }  ne  sont  dans  cet  exemple  que  des  triviales.  Dans  certains  cas  de  F,  la  transitivité  peut 
générer de nouvelles DF qui sont à conserver dans la fermeture. 
                                      
Le  calcul  de  la  fermeture  peut  devenir  laborieux  avec  cette  approche  naïve  fondée  sur 
l’énumération exhaustive. Une méthode plus rapide, de préférence linéaire, est souhaitable pour 
que  la  fermeture  soit  pratique!  Pour  y  arriver,  il  faut  utiliser  la  notion  de  fermeture  d’attribut, 
notamment d’un attribut ou ensemble d’attributs déterminant.  

10.2.1 Fermeture dʹun ensemble dʹattributs  A+  
La fermeture d’un ensemble d’attributs A est notée A+  et est définie comme l’union de tous les 
déterminés  par  A  dans  F.  Une  telle  procédure  proposée  par  Ullman8  permet  dʹéviter  le  calcul 
détaillé de F+.  Le calcul de la fermeture des attributs de l’ensemble A est plus rapide puisqu’il 
est linéaire en fonction de la cardinalité de A.   

 
Chapitre 10 Théorie de la normalisation   64

 
Calcul de la fermeture dʹun ensemble dʹattributs A+
Il  s’agit  de  former  graduellement  une  suite  S  par  la  construction  de  suites  intermédiaires 
composées, au départ de la DF réflexive dont le déterminant est l’attribut dont on recherche la 
fermeture, et d’y ajouter ses déterminés dans F. 
Faire i = 0 
Si = A ‐‐ Initialisation de la suite initiale avec A 
Si+1  =  Si  ∪  d ‐‐  où  d  est  l’union  des  déterminés  dans  F,  dont  le  déterminant  est  inclus 
dans Si. 
Faire tant que Si+1 != Si  
i = i + 1 
Si+1 = Si ∪ d 
Afficher A+ = Si  
Fin. 
 
Exemple : Soit une relation R(A, B, C, D) avec F = {A, B ‐‐> C; D ‐‐> B; A, B ‐‐> D}.  
Calculez AD+: S0 = A, D 
    S1 = {A, D} ∪ Β = {Α, D, Β} pour  i = 0  
     
S2 = {Α, D, Β} ∪ {C, B, D} = {A, B, C, D} pour i = 1  
    S2 = {A, B, C, D} ∪ {C, B, D} = {A, B, C, D} pour i = 2 
 
La  procédure  se  termine  car  la  dernière  suite  est  identique  à  la  précédente  soit  {A,  B,  C,  D}  et 
donc A, D ‐‐> B ∈ F+. Ainsi, puisque B est contenu dans AD+, il faut en conclure que A, D ‐‐> B ∈ 
F+. 
Couverture de F 
Une couverture C de F pour laquelle C ⊆ F est un ensemble de DF tel que la fermeture de F est 
identique  à  celle  de  C :      C+  =  F+  .  La  couverture  C  de  F  est  un  sous‐ensemble  de  F  qui  a  une 
cardinalité moindre ou égale à F et qui  fournit la même fermeture. Lʹensemble F qui résulte de 
lʹanalyse doit avoir des  déterminés parmi lesquels tous les attributs identifiés sont représentés.   
                                        
 
 
F+
  F(2)
C C  F(1) 
 
analyse 1 analyse 2
 
 
 
F10.7 
 
La couverture C de F n’est pas unique et appartient à une famille de couvertures. De plus, elle 
n’est  pas  nécessairement  minimale  par  rapport  à  F.  La  notion  de  couverture  ne  sous‐tend  pas 
que celle‐ci ait le plus petit nombre de dépendances. 

 
Chapitre 10 Théorie de la normalisation   65

10.2.2 Couverture non redondante C* 
Cette  couverture  non  redondante  de  F  est  notée  C*  et  est  définie  comme  l’ensemble  des  DF 
strictement nécessaires pour avoir la même fermeture de F : 
  C* = C si et seulement si ¬∃ C’⊆ C tel que (C’)+ = F+ 
 
Exemple :  Soit R(X, Y, Z) avec F = {X ‐‐> Y; Y ‐‐> Z; X ‐‐> Y, Z}. 
Calcul de F+ : 
F+ = {X ‐‐> Y; Y ‐‐>Z; X ‐‐> Y, Z + les réflexives + les augmentées} 
C = {X ‐‐> Y; Y ‐‐> Z; X ‐‐> Z }  ‐‐ les réflexives et  les augmentées sont dérivables 
C* = {X ‐‐> Y; Y ‐‐> Z} 
 
La  couverture  non  redondante  ne  fournit  que  les  DF  strictement  nécessaires  pour  assurer  la 
dérivation de la fermeture de F.   
Quelques propriétés de la fermeture 
On note quelques propriétés de la  fermeture :  La première (a) énonce que  chaque  dépendance 
de F est comprise dans la fermeture et dans la couverture et à la limite, celle‐ci se confond  avec 
l’ensemble F.  
a) F ⊆ F+ ⊆ C+     
La deuxième (b) stipule que la fermeture de la fermeture de F donne F. 
b)  (F+)+ = F+ 
 
c) Une autre propriété a trait à la fermeture d’un attribut W de R. En effet, si celle‐ci est égale au 
schéma de la relation, alors W est une superclé. 
W+= R alors W est une superclé qui contient une clé primaire de R. 
Exemple : si R(W, Z, U) est une relation dans laquelle W+ = {W, Z, U} alors W est une superclé à 
partir  de  laquelle,  il  est  donc  possible  de  trouver  la  clé  par  des  suppressions  successives  des 
attributs de W. 
 
Algorithme pour le calcul de C*
On peut calculer C* par lʹalgorithme suivant :  
Faire  C  =  F;  /*hypothèse  que  C  est  identique  à  F  dont  les  déterminés                  sont 
monoattribut */ 
Faire tant que F != vide; /*F vide lorsque chaque DF sera testée*/ 
Choisir une f (i.e. X‐‐> A) dans F (f est une DF de F) 
Faire F = F ‐ f;  ‐‐ enlèvement de f de F 
Si f ∈ (C ‐ f)+ alors C = C ‐ f  
/*f appartient à la fermeture de C‐f ? */ 
Afficher C; 
Fin. 

 
Chapitre 10 Théorie de la normalisation   66

Le test f ∈ (C ‐ f)+ est connu comme le problème de l’appartenance à la fermeture et qui peut être 
résolu par un autre algorithme linéaire, c’est‐à‐dire dont le temps dʹexécution est proportionnel 
à la cardinalité de F. 
10.2.3 Problème de lʹappartenance à une fermeture  
Soit  F  un  ensemble  de  DF  spécifiées  au  terme  d’une  analyse  et  f∈ F.  La  question  est  de  savoir 
rapidement si f ∈ (F ‐ f)+. Pour y répondre, il faut calculer la fermeture de (F‐f) et de vérifier si f 
y est incluse. Le calcul est de complexité O|F|2. Il est possible d’utiliser un algorithme linéaire 
proposé et amélioré par Beeri et Bernstein9. 
Théorème de la fermeture dʹun ensemble dʹattributs 
Soit F un ensemble de DF parmi lesquelles une DF est choisie. Supposons que la DF soit du type 
A‐‐> B. Si B ⊆ A+ (définie par rapport à F) alors A‐‐> B ∈ F+,    sinon A‐‐> B ∉ F+.  Le symbole A+ 
est la fermeture de A définie comme étant l’union de tous les déterminés de A dans la fermeture 
de  F  par  les  axiomes.  Donc,  on  peut  calculer  l’appartenance  d’une  DF  à  une  fermeture  sans  la 
calculer explicitement grâce à la notion de fermeture d’attributs. 
Calcul de la couverture minimale de F 
Une manière naïve d’obtenir la couverture minimale de F est de calculer toutes les couvertures 
non  redondantes  et  de  choisir  celles  de  cardinalité  minimale.  Il  existe  toutefois  d’autres 
algorithmes plus rapide pour le calcul de la couverture minimale, dont celui de Mummenmaa et 
Tanisch10 qui reprend celui de Maier11. 
Exemple 
Transformer chaque DF de F en une dépendance élémentaire ayant un déterminé monoattribut 
et ensuite exécuter lʹalgorithme suivant : 
Tant qu’il y a une DF de F qui est non testée : 
a)Choisir une DF de F du type X‐‐>A en parcourant les DF dans un ordre fixe; 
b)Si Z⊆X tel que F ⊆((F ‐ {X‐‐>A})⊆{Z‐‐> A})+  alors remplacer immédiatement X‐‐> A par Z‐‐>A 
dans F; 
c) Tant qu’il y une DF dans le F initial qui n’a pas été testée : 
‐ Choisir une DF de F soit X‐‐> A; 
‐ Si X‐‐> A∈F et X ‐‐> A∈(F ‐{X‐‐>A})+,alors enlever X‐‐> A de F. 
 
L’ensemble résiduel de DF est la couverture minimale de l’ensemble F initial. 
Voici un exemple de l’application de cet algorithme pour trouver la couverture minimale :  
S = {R1(A, B, C)} et F = {A, B ‐‐> C}  
 
La DF est remplacée par A ‐‐> C, car A ‐‐> C |‐ A, B ‐‐> C par augmentation.  
L’ensemble  résiduel  n’est  composé  que  de  A  ‐‐>  C  soit  la  couverture  minimale.  Il  est  facile  de 
s’en convaincre, puisque la DF initiale peut être obtenue par augmentation dans la fermeture de 
Cmin.  Un  algorithme  encore  plus  performant  a  été  proposé  par  Diederich12  en  utilisant  le 
concept de la fermeture gauche et de la fermeture droite. 

 
Chapitre 10 Théorie de la normalisation   67

Recherche des clés : primaire, candidate et étrangère dans une relation 
Soit  une  relation  R  (A1,  A2,  A3,  ...  An).  La  spécification  d’une  clé  quelconque  X  de  R  définit 
automatiquement un ensemble de DF et en exclut d’autres : 
a) X ‐‐> Ai pour i = 1 à n  
b) ¬∃ X’ tel que X’ ‐‐> Ai pour i = 1 à n. 
 
Soit la relation R(A, B, C, D, E) avec le F suivant : 
  F = {A ‐‐> B, C;  C, D ‐‐> E;  B ‐‐> D;  E ‐‐> A} 
Le problème de trouver les clés candidates pour cette relation est de complexité NP, i.e. que le 
temps  dʹexécution  de  lʹalgorithme  de  recherche  de  la  clé  augmente  exponentiellement  avec 
lʹaugmentation  du  nombre  dʹattributs  dans  le  schéma.  Avec  un  petit  nombre  dʹattributs  et  en 
précisant les clés simples ou monoattribut, le nombre de tests demeure raisonnable. Pour un F 
de  petite  cardinalité,  il  est  alors  possible  de  faire  les  tests via  la  fermeture  des  attributs.  Voici 
quelques exemples :   
a) Test de A comme clé : Si A ‐‐> B, C, D,E  alors B, C, D,E ⊆ A+. 
Or A+ = A, B, C, D, E. Donc, A est une clé candidate de R; 
 
b) Est‐ce que E une clé ? Si E ‐‐> A, B, C, D;  alors A, B, C, D ⊆ E+. 
Or E+ = A, B, C, D. Donc, E est une clé de R; 
 
c) Test avec C, D :  C, D ‐‐> A, B, E; 
(CD)+ = C, D, E, A, B 
Donc C, D est une clé; 
                      
d) Test avec B: B ‐‐> A, C, D, E? 
B+ = B, D 
Donc, B n’est pas une clé de R; 
 
e) Test avec B, C : B, C ‐‐> A, D, E ? 
(B, C)+ = B, C, D, E, A 
Donc, B, C est une clé de R.  
 
La  recherche  exhaustive  des  clés  sous‐tend  une  série  exhaustive  de  tests  avec  toutes  les 
combinaisons possibles d’attributs. Il devient évident alors que le processus peut être très long 
dès lors que le nombre d’attributs est grand.    
10.2.4 Attribut redondant dans une DF 
Un attribut A est redondant dans la DF X ‐‐> Y de F s’il peut être enlevé du déterminant ou du 
déterminé de la DF, sans modifier la fermeture de F. Soit F un ensemble de DF et F’ = F ‐ {X ‐‐> 
Y}  ∪  X’  ‐‐>  Y}  où  X’  est  le  déterminant  obtenu  en  enlevant  un  attribut  A  à  X.  La  recherche 
d’attributs  redondants  permet  de  simplifier  les  déterminants  et  les  déterminés  des  DF  pour 
alléger le calcul détaillé de la fermeture d’un ensemble de DF ou d’attributs : 

 
Chapitre 10 Théorie de la normalisation   68

  Si (F)+ = (F’ )+ alors X ‐ X’ est redondant dans X ‐‐> Y.  
 
Soit une relation R(A, B, C, D) avec F = {A ‐‐> B, C; B ‐‐> C; A, B ‐‐> D}. Il faut trouver lʹattribut ou 
les attributs redondants dans les DF de F. 
Y a‐t‐il redondance dʹun attribut dans A, B ‐‐> D de F ? 
 
Cas  1  Si  l’attribut  B  est  redondant  alors  F’  =  {{A  ‐‐>  B,  C;  B  ‐‐>  C;  A  ‐‐>  D}  donne  la  même 
fermeture que F+. Il faut donc démontrer que (F’)+  = (F)+. Pour éviter de calculer explicitement 
les fermetures, il suffit de démontrer que : F |‐ A ‐‐> D et F’ |‐ A, B ‐‐> D. 
F |‐ A ‐‐> D i.e. que de F, il est possible de dériver la DF A ‐‐> D 
D ⊆ (A) + 
Or A+ = {A, B, C, D}. 
et 
F’ |‐ A, B ‐‐> D  
D ⊆ (Α, Β) + dans F’  
{A, B}+ = {A, B, C, D} 
La première dérivation souligne que B est redondant puisque  A‐‐> D est dérivable. La deuxième 
est facile à démontrer puisquʹelle découle directement dʹune propriété de la DF.  
Donc, l’attribut B est redondant dans la DF A, B ‐‐> D.  
                            
Cas 2  Vérifions maintenant si l’attribut A est étranger dans A, B ‐‐> D. 
Si tel est le cas, alors les deux ensembles F’ = {A ‐‐> B, C; B ‐‐> C; B ‐‐> D} et 
F = {A ‐‐> B, C; B ‐‐> C; A, B ‐‐> D} donneront la même fermeture : (F’)+ = F+  
 
Il suffit donc de démontrer les dérivations suivantes : 
F’ |‐ A, B ‐‐> D  F |‐ B ‐‐> D 
D ⊆ (A, B)+  D⊆B)+ 
(A, B)+ = A, B, C, D  (B)+ = {B, C } 
donc F’ |‐ A, B ‐‐> D  F |‐ (B ‐‐> D) n’est pas vérifiée. 
                    
Donc, A n’est pas étranger dans la DF  A, B ‐‐> D. On peut faire la même vérification avec les 
autres cas pour s’assurer de la nécessité de la présence de B dans la DF  A ‐‐> B, C et de C dans la 
dépendance A ‐‐> B, C. 
10.3 Conception dʹun schéma de BD 
La  conception  d’une  base  de  données  doit  aboutir  à  la  formulation  dʹun  ensemble  de  schémas 
relationnels  qui  a  plusieurs  propriétés,  à savoir  la  conservation  des  données  et  la  conservation 
des  dépendances.  Ces  deux  qualités  de  la  base  de  données  sont  parfois  difficiles  à  faire 
cohabiter! Au besoin, il faudra choisir et opter pour la conservation des données. 
La  conservation  des  données  (CD)  privilégie  l’invariance  de  l’information  de  la  BD :  quel  que 
soit le schéma, l’information représentée restera la même. Il ne peut y avoir ni création ni perte 
d’information en passant d’un schéma à un autre et cela, avec les mêmes données. 

 
Chapitre 10 Théorie de la normalisation   69

                            
La  conservation  des  dépendances  fonctionnelles  (CDF)  privilégie  le  respect  des  contraintes 
d’intégrité  avec  le  minimum  de  calcul  de  la  part  du  SGBD,  c’est‐à‐dire  sans  que  celui‐ci  ait  à 
faire des jointures ou dʹautres calculs à chaque mise à jour ou à chaque suppression de données 
dans la BD.    
Une conception inappropriée d’une base de données se traduit par les phénomènes suivants : 
a) Redondance non justifiée par la recherche d’un niveau élevé de performance; 
b)  Présence  des  anomalies  d’ajout,  de  suppression  et  de  mise  à  jour  qui  mettent  en  péril 
l’intégrité de la BD. 
 
Il  faut  très  souvent  faire  un  compromis  entre  la  conservation  des  données  et  celle  des 
dépendances.  Dans  plusieurs  cas,  il  faudra  choisir  la  première,  quitte  à  confier  au  SGBD  la 
mission de vérifier le respect des dépendances au moyen de déclencheurs (triggers) de la base. 
C’est  une  pratique  courante  observée  dans  le  domaine  des  SGBD.  En  revanche,  il  n’est  pas 
souhaitable de privilégier la conservation des DF et de laisser au SGBD seul la vérification de la 
conservation des données. Il faut rappeler qu’une bonne conception qui favorise ces propriétés, 
simplifie la tâche de tout logiciel SGBD et que certaines faiblesses dans la conception du schéma 
du  schéma  de  la  base  peuvent  être  compensées  en  cours  dʹexploitation,  par  le  traitement  du 
SGBD. La conservation des données est considérée comme capitale pour assurer la cohérence et 
sera prise en considération en cours de la normalisation des relations de la base de données. 
Algorithme simplifié de synthèse du schéma de BD avec CDF 
Calculer une couverture minimale Cmin de F :   
 
‐ Pour chaque déterminant X partagé par toute DF de F :  
Créer une relation Ri avec comme schéma; {X ∪ A1 ∪ A2 ∪...} qui est ajoutée au schéma S de la 
BD    avec  pour  Ri,  F  =  X‐‐>A1  et  X‐>A2,...  comme  les  seules  DF  dans  la  couverture  C  ayant 
comme déterminant X; 
‐Regrouper les autres attributs du schéma S non incorporés dans une Ri  dans une relation Rj; 
Fin. 
 
Cet algorithme ne fournit pas toujours un schéma acceptable et/ou optimal parce qu’il ne tient 
pas compte de la redondance des dépendances fonctionnelles (couverture minimale ignorée), la 
présence des attributs étrangers et les clés équivalentes. 
10.4 Hypothèse de la relation universelle  
La  conception  d’un  schéma  de  base  de  données  ne  débute  pas  toujours  avec  le  traitement  des 
dépendances fonctionnelles. Il y a souvent un existant constitué de relations. C’est à partir de ces 
dernières qu’il faudra vérifier le degré de normalisation de chacun et au besoin, le transformer 
pour  le  normaliser  davantage.  La  décomposition  d’une  relation  est  faite  en  présumant  que 
chaque attribut a une sémantique propre qui demeure la même quelle que soit la relation hôte 
(invariance  sémantique au regard du contexte).  C’est l’hypothèse de la relation universelle (U) 
qui  énonce  que  dans  un  schéma  tout  identificateur  de  chaque  attribut  est  global  et  doté  d’une 

 
Chapitre 10 Théorie de la normalisation   70

sémantique unique. Dans le cas contraire, certaines ambiguïtés peuvent fausser l’interprétation 
des relations voire des dépendances fonctionnelles. 
 
Exemple : Soit les relations Client (nas*, nom, date) avec F1 = {nas ‐‐> date ; nas ‐‐> nom} pour R1 
et Livraison (article*, qte, date*, nas) avec  
F2 = {article, date ‐‐> qte ; article, date ‐‐> nas }. 
 
client :  nas*  nom  date   
  n12  Cyr  12‐2‐85   
  n9  Vonet  3‐7‐90   
  n34  Xircon  7‐1‐95      
                                        
livraison :  article*  qte  date*  nas   
  a2  4  4‐8‐91  n9   
  a2  2  8‐1‐95  n12  * 
  a2  3  22‐7‐95  n12  * 
 
La  DF  de  F2  signifie  que  pour  un  type  d’article  et  pour  une  date  donnée,  il  a  été  livré  qu’une 
seule  quantité.  Cette  dernière  pourrait  être  par  exemple  la  production  du  jour,  et  la  livraison 
faite  à  une  seule  personne!  Si  lʹon  considère  les  dépendances  de  F1  et  de  F2  et  la  pseudo 
transitivité, on a alors :   
 
nas ‐‐> date  (de F1) 
article, date ‐‐> qte  (de F2) 
{nas, article} ‐‐> qte (par pseudo transitivité) 
 
Cette  DF  n’est  pas  validée  dans  l’extension  de  la  relation  Livraison  (voir  les  tuples  2  et  3).  La 
raison tient à la double sémantique de l’attribut date. Dans la relation Client, il s’agit de la date 
d’inscription du client,  tandis que  dans la relation Livraison la date est celle de  la livraison  de 
l’article. Pour éviter de faire  une fausse inférence, il faut  que les deux  attributs soient distincts 
par  leur  libellé  puisque  qu’ils  ont  une  sémantique  différente.  Par  exemple,  dateClient  et 
dateLivraison sont des libellés différents et les dépendances fonctionnelles deviennent alors les 
suivantes : 
 
F1 = {nas ‐‐> dateClient ; nas ‐‐> nom 
F2 = {article, dateLivraison ‐‐> qte ;    article, dateLivraison ‐‐> nas }. 
 
Il devient impossible de faire la dérivation impliquant la l’attribut qte. 
La relation universelle U est celle formée avec tous les attributs identifiés et dont l’extension est 
composée avec les données de ceux‐ci. Ainsi, les attributs de l’exemple Client donne la relation 
universelle suivante à partir de laquelle il doit être possible d’obtenir par simple projection les 
extensions de la base :   
                       

 
Chapitre 10 Théorie de la normalisation   71

U :  nas *  nom  date*  article*  qte 


  n12  Cyr  12‐2‐85  a7  2 
  n9  Vonet  3‐7‐90  a2  4 
  n12  Cyr  12‐2‐85  a2  3 
  n9  ‐‐  4‐8‐91  a2  4 
  n12  ‐‐  8‐1‐95  a7  2 
  n12  ‐‐  22‐7‐95  a2  3 
              
On  constate  que  les  projections  ne  fournissent  pas  les  extensions  recherchées  et  ce  parce  que 
l’attribut  date  est  présumé  avoir  une  seule  sémantique.  Ainsi,  les  dates  dateLivraison  et  
dateClient sont confondues.  
 
Voici un autre exemple dans lequel la même difficulté découle de lʹambiguïté des libellés.  
Empl (nas*, telephone) et Gerant (nas*, telephone). 
 
Empl :   nas*  telephone  Gerant :   nas*  telephone 
  n2  566‐3425    n45  656‐2398 
  n4  645‐8954    n22  656‐2678 
  n6  756‐0986       
  n8  549‐5648       
La  relation  universelle  U  de  cette  base  a  le  schéma  (nas,  telephone)  et  son  extension  est  la 
suivante, résultat de la jointure : 
U :   nas  telephone 
  n2  566‐3425 
  n4  645‐8954 
  n6  756‐0986 
  n8  549‐5648 
  n45  656‐2398 
  n22  656‐2678 
 
Il  nʹest  plus  possible  de  recalculer  par  projection  les  extensions  initiales  respectives  des  tables 
Empl  et  de  Gerant  parce  que  l’attribut  telephone  de  la  relation  universelle  U  confond  celui  de 
l’employé et celui de son gérant.     
Base de dépendances dʹune relation : G(Ri) ou Gi 
La base de dépendances de la relation Ri est notée Gi  et elle est formée des dépendances de F+ 
dont le déterminé et le déterminant sont composés seulement avec des attributs appartenant à 
Ri. Pour chaque Ri d’une base, il y aura un Gi. Si F = ∪Gi, alors il y a conservation des DF dans 
cette base de données.  
Gi = { df ∈ F+ | df = X ‐‐> Y et X ∪ Y ⊆ Ri} 
La Gi de Ri ne prend en compte que les DF intra relationnelles. 

 
Chapitre 10 Théorie de la normalisation   72

Définition formelle de la conservation des dépendances (CDF) 
Lorsqu’il y a conservation des dépendances, le schéma de la base S = {R1, R2, ... Rn} obtenu est 
tel que toutes les dépendances de F (donc de F+) peuvent être vérifiées sans calcul de jointure. 
Dans ce cas, F = ∪ Gi.  

Algorithme de vérification de la conservation des dépendances dans S 
Lʹalgorithme fait appel au calcul graduel et progressiste de la base de dépendances de S :   
Gtotal = vide 

Calcul de F+ 
Pour chaque Ri ∈ S 
  Calculer Gi 
  Gtotal = Gtotal ∪ Gi 

Calcul de (Gtotal)+ 

Si (Gtotal)+ = F+ alors return(True) else return (False); 
Fin. 
Lorsque l’algorithme renvoie True, il y a conservation des dépendances dans le schéma de la BD.                           
Algorithme de décomposition du schéma avec CDF 
Soit  une  relation  R  avec  ses  DF  regroupées  dans  l’ensemble  F.  La  relation  R  est  k‐décomposée 
avec conservation des dépendances fonctionnelles (CDF) en R1, R2, R3, ... Rk avec les bases de 
dépendances G1, G2, G3 ... Gk si et seulement si {G1 ∪G2 ∪ G3∪ ... Gk}+ = F+ et avec {G1∪ G2∪ 
G3∪ ... Gk} ⊆ F. 
Exemple   
R(A, B, C, D) avec F ={A‐‐> B; A ‐‐> C; C‐‐> D}.  
Décomposition de R avec CDF : 
 
R1(A, B, C) avec G1= {A ‐‐>B;  A ‐‐> C} 
R2 (C, D) avec G2 = {C ‐‐> D}. 
Toutes  les  dépendances  de  F  sont  à  nouveau  vérifiées  dans  les  deux  relations  obtenues  et 
partant, toute DF de la fermeture le sera aussi. Il nʹy a pas de DF inter‐relation.  
Décomposition sans  CDF d’une relation  
Soit  une  2‐décomposition  de  R(A,  B,  C)  avec  F  =  {A  ‐‐> B;  A  ‐‐> C;  B  ‐‐> C}.  Elle  comprend  les 
relations : R1(A*, B) avec A ‐‐> B et R2(A*, C) avec A ‐‐> C. 
Les  deux  extensions  correspondantes  sont  obtenues  par  projections  de  R  sur  R1  et  ensuite  sur 
R2.  La  2‐décomposition  n’est  pas  acceptable  au  regard  de  la  conservation  des  DF,  car  pour 
effectuer la vérification de B ‐‐> C il faut faire un calcul de jointure. La conservation des données 
est  cependant  assurée.  En  effet,  la  dépendance  B‐‐> C  n’est  pas  dérivable  à  partir  des  deux 
dépendances  présentes  dans  le  nouveau  schéma.  Cette  dernière  DF  n’appartient  donc  pas  à  la 
fermeture des DF de la nouvelle BD. 

 
Chapitre 10 Théorie de la normalisation   73

Autre exemple :   
R(A*, B*, C*,  D) avec F = {A, B, C ‐‐> D; D ‐‐> A} 
 
Cette relation n’est pas en FNBC, car un attribut primaire A dépend dʹun attribut qui nʹest pas 
une  clé.  La  2‐décomposition  suivante  est  effectuée :  R1(D*,  A)  et  R2(B*,  C*,  D*).  Ces  deux 
relations sont maintenant en FNBC. De plus, la 2‐décomposition conserve les données, mais pas 
les dépendances. En effet, A, B, C ‐‐> D ∈F ne peut pas être vérifiée directement sans calculer la 
jointure (R1 |x| R2). Au contraire, la conservation des données est assurée, car R1 ∩ R2 ‐‐> R1 ‐ 
R2, i.e. que D ‐‐> A. 
 
Remarque 
Une  relation  R  peut  toujours  être  k‐décomposée  en  FN3  avec  conservation  des  données  et  des 
dépendances. Toutefois, il n’existe pas toujours une décomposition en FNBC avec CDF et CDD.  
 
Conservation des données (CD) 
Un schéma R = {R1 ∪ R2 ∪ ... Rn} où Ri est une relation est n‐décomposée avec conservation des 
données (CDD) si et seulement si :   
 
  R = R1 |x| R2 |x| R3 |x| ... |x| Rn 
Les conditions nécessaires pour que R = (R1 |x| R2 ...) sont les suivantes :   
   R1 ∩ R2 ≠ vide ;   ‐‐  absence d’attribut commun 
  et (R1 ∩ R2) ‐‐> (R1 – (R1 ∩ R2)) ou (R1 ∩ R2) ‐‐> (R2 – (R1 ∩ R2)) 
 
Exemple R = r1 |x|   r2 avec F = {B ‐‐> C, D + les DF de la clé A, B} 
 
r :  A*  B*  C  D  r1:  A*  B*  r2:  B*  D 
  1  5  8  9    1  5    5  9 
  6  5  8  9    6  5    3  7 
  2  3  4  7    2  3       
 
Dans cet exemple, r = r1 |x| r2, car R1 ∩ R2 = B et B ‐‐> D dans dans la relation  r2. 
Jointure sans conservation des données 
Soit la relation R(A, B, C, D, E, H, G} et F = {A, B ‐‐> C;  A, B ‐‐> D;  B,  E ‐‐> H;  A, E ‐‐> G}. La clé 
composée de R est A, B, E, car la fermeture de ABE donne le schéma de R. Supposons que cette 
relation soit décomposée pour obtenir les relations ci‐dessous par projection :   
R1 (A, B, C, D)  R3 (A, E, G) 
R2 (B, E, H)    R4 (A, B, E) 
 
Quelles  sont  les  clés  des  relations  suivantes ?  R1(A*,  B*,  C,  D) ;  R2(B*,  E*,  H) ;  R3(A*,  E*,  G)  et 
finalement R4(A*, B*,E*).    
Il faut en outre démontrer que le résultat R1 |x| R2 |x| R3 |x| R4 = R le schéma de la base. Pour 
cela, il faut trouver une jointure naturelle intermédiaire de deux relations valide qui conserve les 

 
Chapitre 10 Théorie de la normalisation   74

données. Prenons les relations R1 et R2 : le calcul de la jointure R1 |x| R2 conserve les données 
si et seulement si R1∩R2 = B et que B ‐‐> R1 − (R1 ∩ R2) ou que B ‐‐> R2 ‐ (R1 ∩ R2). Or, est‐ce 
que B ‐‐> A, C, D ou encore B ‐‐> E, H ? Sinon, R1 |x| R2 génère des incohérences transitoires. 
On peut démontrer que ces deux DF n’appartiennent pas à F+ et que la jointure ne conserve pas 
les  données.  Le  problème  avec  le  résultat  de  cette  décomposition  repose  sur  le  fait  qu’avec  la 
première  décomposition,  la  condition  de  la  conservation  des  données  n’était  pas  vérifiée. 
Certaines  formes  de  relation  peuvent  être  construites  pour  conserver  les  données  et  les  DF, 
tandis que d’autres relations ne conservent que les unes ou les autres. Un choix s’impose donc 
en  fonction  du  contexte  de  l’analyse  et  de  la  qualité  de  la  représentation  attendue.  Le  plus 
souvent, il sera choisi de conserver les données. 
10.5 Contraintes dʹinclusion (DIN) 
Une  dépendance  d’inclusion  (DIN)  implique  deux  relations  de  la  même  base  de  données.  Une 
telle  contrainte  énonce  des  conditions  concernant  l’occurrence  de  valeurs  dans  une  relation  au 
regard des valeurs dans une autre. C’est une contrainte entre relations dont un cas particulier est 
concrétisé par la contrainte référentielle. 
Définition de la dépendance dʹinclusion (DIN) 
Soit une base comprenant les relations P et W. Une dépendance d’inclusion (DIN) dans P est une 
expression  P[X]  ⊆W  [Y],  où  X  et  Y  sont  respectivement  des  ensembles  d’attributs  de  même 
cardinalité.  
Soit les ensembles d’attributs X = {A1, A2, ... An} et Y = {B1, B2, ... Bn} et p et w, deux extensions 
du schéma qui satisfont la dépendance d’inclusion P [X] ⊆ S [Y]. Pour chaque tuple ti ∈ p, il y a 
un tuple tj ∈w tel que ti [X] = tj [Y] et cela pour tout 1<= i <= n.  
 
Exemple : Supposons une BD pour modéliser un concours artistique : 
Spectacle (date*, titre) Artiste (nom*, adresse*) 
Score (datEval*, critique*, cote, commentaires} 
 
L’attribut critique représente un artiste qui agit comme critique artistique et il est exigé qu’il soit 
aussi un artiste et la date de la critique corresponde bien à un spectacle. Les DF définies par les 
clés du schéma et les DIN sont les suivantes : 
  Score [dateEval] ⊆ Spectacle[date]  Score[critique] ⊆ Artiste[nom] 
 
Cette DIN est une assertion qui stipule que, chaque fois qu’une combinaison de valeurs apparaît 
dans une ou plusieurs relations du schéma, elle doit obligatoirement apparaître aussi dans une 
autre relation du schéma. Cette condition est courante dans un schéma parce que les entités d’un 
modèle logique sont associées les unes aux autres par une association. La DIN établit aussi une 
précédence dans la mise à jour : la date de l’évaluation artistique doit être postérieure à celle du 
spectacle.                      

 
Chapitre 10 Théorie de la normalisation   75

Contrainte dʹintégrité référentielle 
Une  contrainte  dʹintégrité  référentielle  est  une  contrainte  DIN  particulière  dans  laquelle 
lʹinclusion R1[A] ⊆ R2[A] est vérifiée dans le schéma R et où A est une clé dans R2 et A est aussi 
une clé étrangère dans R1.  
Cas particulier: contrainte DIN incluant une clé 
La  dépendance  DIN,  R1[W]  ⊆  R2[V]  est  du  type  clé  si  V  est  une  clé  de  R2  et  W  un  attribut 
quelconque  de  R2  ayant  le  même  type  que  celui  de  V.  Toutes  les  valeurs  de  V  ne  sont  pas 
présentes dans la projection W, mais les valeurs de W ne sont que des valeurs de V.   
Propriétés des dépendances dʹinclusion (DIN) 
Les dépendances dʹinclusion suivantes sont valides dans R1[A1, A2, ..., An] et R2[B1, B2, ..., Bn]: 
a) R [A1, A2, ..., An] ⊆ R [A1, A2, ..., An] est une DIN réflexive valide quel que soit R. 
 
b) Si R1[A1, A2, ..., An] ⊆ R2[B1, B2, ..., Bn] alors R1[Ai, Ai +1 ..., Aj] ⊆ R2[Bi, Bi+1 ..., Bj] 
où i et j ∈ [1,n];  la DIN est toujours vérifiée avec un sous‐ensemble ordonné des attributs pour 
autant  quʹil  soit  aussi  défini  dans  la  partie  gauche  et  dans  la  partie    droite  de  la  même 
dépendance d’inclusion; 
 
c) Transitivité de la DIN 
Si R1[A1, A2, ..., An] ⊆ R2[B1, B2, ..., Bn] et R2[B1, B2, ..., Bn] ⊆ R3[C1, C2, ..., Cn], alors  
R1[A1, A2, ..., An] ⊆ R3[C1, C2, ..., Cn]. 
 
d) Le graphe des DIN est acyclique 
La présence dʹun cycle dans le graphe semble causer des problèmes lors de la mise à jour  des 
relations. Le graphe est construit en plaçant un arc orienté de R vers W lorsque R[X]  ⊆  W [Y]. 
Exemple dʹune contrainte DIN 
 
Soit les relations suivantes :   
Employe (nasEmpl*, nomDep) Gestion (nomDep*, nasGerant)
 
La DIN suivante est définie dans cette base :  
  Gestion[nasGerant, nomDep] ⊆ Employe[nasEmpl, nomDep] 
 
Cette  DIN  contraint  chaque  gérant  dʹun  département  à  être  un  employé  de  ce  département  et 
constitue une contrainte dʹinclusion plus stricte que la contrainte de clé étrangère formulée avec 
seulement l’attribut nasEmpl. 
10.6 Formes normales des relations (Cette partie est un rappel des notions déjà vues) 
Le schéma d’une base de données relationnelle S est formé des relations suivantes : {R1, R2, R3, 
... Rn} où chaque Ri ∈ S est normalisée afin d’éviter des anomalies d’ajout, de mise à jour et de 
suppression  dans  l’exploitation  de  la  BD.  Ainsi,  la  cohérence  de  la  BD  est  conservée  de  par  la 
structure même du schéma sans que le noyau du SGBD ait à déployer des efforts de calcul de 
jointure  parfois  importants  pour  garantir  l’intégrité  de  la  base13.  Règle  générale,  plus  la 

 
Chapitre 10 Théorie de la normalisation   76

normalité de la relation est grande, plus les risques d’anomalies sont faibles. La normalité d’une 
relation est définie essentiellement au regard de certaines dépendances dont les fonctionnelle, la 
multivaleur et celle de jointure14.   
 
Il  existe  d’autres  dépendances  qui  n’interviennent  pas  dans  la  définition  des  formes  normales 
(voir la dépendance d’inclusion), mais qui ont leur importance dans la cohérence des données. A 
partir  de  maintenant,  nous  reviendrons  sur  les  notions  vues  antérieurement  pour  les  préciser 
davantage au regard de la théorie. 
Première forme normale (FN1) 
La  première  forme  normale  (FN1)  est  caractérisée  comme  une  relation  dans  laquelle  tous  les 
domaines  des  attributs  sont  du  type  atomique.  Cette  forme  normale  interdit  les  attributs 
composés ou complexes dans la même relation. Avec certains nouveaux SGBD, les relations non 
normalisées se révèlent parfois intéressantes pour des raisons de performance et parce qu’elles 
se  rapprochent  de  la  réalité  dans  certains  domaines  d’application  comme  le  domaine  du 
multimédia et de la CAO.  
 
Une relation est en FN1 si chaque ses attribut a un domaine atomique. 
 
Ceci se manifeste à l’intersection de chaque colonne et de chaque ligne par une seule valeur et 
dʹun même type atomique. 
 
Exemple :Voici  une  table  ou  relation  de  données  avec  des  valeurs  atomiques  pour  chaque 
attribut.                                         
      
carnet:  noClient*  noPiece*  norme  credit 
  Aubert  p500  n10  A 
  Dumas  p502  n20  B 
  Dussault  p500  n10  A 
  Pageau  p640  n30  C 
  Aubert  p502  n40  A 
F10.8 
 
Les domaines correspondants aux attributs sont les suivants : 
  dom (client) = {‘Aubert’, ‘Dumas’, ‘Dussault’, ‘Pageau’, ...} 
  dom (noPiece) = {‘p500’, ‘p502’, ‘p504’, ‘p506’, ‘p600’, ‘p610’, ‘p620’, ‘p640’, ...}      
dom (norme) = {‘n10’, ‘n20’, ‘n30’, ‘n40’, ‘n50’} 
  dom (credit) = {‘A’, ‘B’, ‘C’, ‘X’} 
                          
Le  type  des  attributs  est  défini  comme  une  structure  de  données  primitive  dont  la  valeur  est 
comprise dans l’ensemble énuméré ou défini. Ce type est aussi connu comme un domaine, car 
les opérations sur le type ne sont pas spécifiées dans le modèle relationnel. 
Dans cet exemple, la relation Carnet est FN1 et les DF sont : 
F = {noClient, noPiece ‐‐> norme, credit;  noClient ‐‐> credit} 

 
Chapitre 10 Théorie de la normalisation   77

Anomalies possibles  
Il est évident que des anomalies persistent en FN1 : 
a)  Anomalie  de  suppression  :  si    la  commande  du  client  Pageau  est  annulée,  il  y  a  perte  de  la 
cote de crédit de ce client. Lorsque cette personne redevient alors client il faudra rechercher sa 
cote en dehors du système. 
b) Anomalie de mise à jour : La cote de crédit du client Aubert qui a commandé la pièce p500 est 
lʹobjet dʹune décote de A à B. La mise à jour du premier tuple avec la nouvelle cote engendre une 
incohérence, car le crédit de Aubert apparaît comme ayant la cote B dans le premier B et A dans 
le dernier tuple. Il y a donc violation de la dépendance fonctionnelle : noClient ‐‐> credit. 
c)  Anomalie  dʹajout  :  Il  est  impossible  dʹajouter  une  cote  de  crédit  pour  le  client  Gagnon,  si  ce 
dernier ne passe pas en même temps une commande.  Ces anomalies seront absentes lorsque les 
relations passeront à une autre forme normale dite FN2. 
Deuxième forme normale (FN2) 
Une relation Ri du schéma S est en FN2 si et seulement si les conditions suivantes sont vérifiées 
pour toute extension de Ri :   
a) si son extension est en FN1 
b) et si chaque attribut non primaire est en dépendance fonctionnelle totale 
par rapport à chaque clé (primaire et candidate) de Ri. 
                                  
En d’autres mots : ri est FN1 et pour ∀j si Aj est non primaire, alors ∀k tel que clék est une clé et 
que  clék  ⇒ Aj  dans  ri.  En  d’autres  mots,  une  extension  est  en  FN2  si  chaque  attribut  non 
primaire  de  ri  est  en  dépendance  fonctionnelle  totale  par  rapport  à  chaque  clé  (primaire  et 
candidate) de la relation. Dans la relation Carnet (client, noPiece, norme, credit), il y a quʹune clé 
candidate et les DF de F sont les suivantes : 
F = {noClient, noPiece ‐‐> norme, credit;  noClient ‐‐> credit} 
 
La seule clé est primaire et elle est composée de deux attributs primaires  : {noClient*, noPiece*}.  
Les  attributs non primaires sont norme et credit. Cette relation est‐elle en FN2 ? Elle ne l’est pas, 
car  il  existe  une  DF,  soit  noClient  ‐‐>  credit,  dont  lʹattribut  non  primaire  (credit)  n’est  pas  en 
dépendance totale sur la clé primaire. Par conséquent, noClient, noPiece =/=> norme, credit.  
Transformation  de la relation en FN2  
Pour faire une transformation afin dʹavoir une FN2, il faut choisir une première DF qui contredit 
la forme FN2 et l’isoler dans une nouvelle relation R1. Ensuite, enlever le déterminé de la DF en 
question  de  la  relation  initiale  et  vérifier  si  le  schéma  résiduel  est  en  FN2.  Un  libellé  plus 
significatif peut être donné à la nouvelle relation après examen de ses attributs. Il faut appliquer 
cette procédure de façon récursive. Le passage à la forme FN2 sous‐tend donc une division de la 
relation et la multiplication de celles‐ci dans la base. Dans l’exemple ci‐dessus, il faut enlever la 
DF  {noClient  ‐‐>  credit}  de  F  pour  obtenir  une  relation  résiduelle :  R2  (noClient*,  noPiece, 
norme). Les DF de R1 et R2 sont respectivement :    
noClient ‐‐> credit dans R1 
  noClient, noPiece ‐‐> norme dans R2 

 
Chapitre 10 Théorie de la normalisation   78

 
La  décomposition  ou  la  division  du  schéma  correspond  à  un  éclatement  correct  de  l’extension 
(sans perte de données), car R1 ∪ R2 ‐‐> Aj dans au moins une Ri.  Les extensions sont obtenues 
par projection de l’extension d’origine sur les attributs recherchés. 
 
r1 :  noClient  credit 
  Aubert  A 
  Dumas  B 
  Dussault  A 
  Pageau  C 
                                         
r2 :  noClient*  noPiece*  norme 
  Aubert  p500  n10 
  Dumas  p502  n20 
  Dussault  p500  n10 
  Pageau  p640  n30 
  Aubert  p502  n40 
F10.11 
 
Ce nouveau schéma permet maintenant d’inscrire une commande de pièce par un client, même 
si ce dernier nʹa pas encore de cote de crédit. En revanche, il est toujours impossible d’inscrire 
une pièce produite selon une norme donnée, si cette pièce nʹest pas commandée par un client. La 
relation en FN2 a apporté une certaine correction, mais certaines anomalies persistent dans les 
relations. 
                            
Exemple  : R (fournisseur*, article*, ville, qte)   
avec F = {fournisseur ‐‐> ville; + les DF de la clé}  i.e.  (fournisseur, article ‐‐> ville, qte) 
 
La  relation  R  n’est  pas  en  FN2  en  raison  de  la  présence  de  la  1ère  DF.  Il  suffit  donc  de 
transformer R en deux relations par décomposition ou division de la relation d’origine : 
R1 (fournisseur*, article*, qte)  R2 (fournisseur*, ville) 
F1 = {fournisseur, article ‐‐> qte}  F2 = {fournisseur ‐‐> ville}. 
Troisième forme normale (FN3) 
Certaines  anomalies  persistent  dans  une  relation  même  si  cette  dernière  est  en  FN2.  Ces 
anomalies  résiduelles  sont  illustrées  dans  l’exemple  suivant  concernant  les  constructeurs 
spécialisés assujettis à la convention des salaires des métiers de la construction.   
Lorsqu’un  constructeur  spécialisé  embauche  un  ouvrier,  son  salaire  est  déterminé  par  la 
convention.  Les  anomalies  présentes  sont  associées  à  la  présence  de  DF  transitives  dans  la 
relation Constructeur. La BD est constituée d’une seule relation :   
Constructeur (societe*, metier, salaire) 
F = {societe ‐‐> metier, salaire;  metier ‐‐> salaire} 
 

 
Chapitre 10 Théorie de la normalisation   79

L’extension  de la relation en FN2 est la suivante : 
 
constructeur :  societe*  metier  salaire 
  PEG Inc.  menuisier  35 K$ 
  Delna Ltée  menuisier  35 K$ 
  Les murs Inc  plâtrier  45 K$ 
  Eclair enr  électricien  40 K$ 
  WC inc  plombier  35 K$ 
  Beaux bois  menuisier  35 K$ 
  CGE Inc.  électricien  40 K$ 
  Lux inc.  électricien  40 K$ 
Figure 10.12 
Anomalies de lʹextension en FN2  
Les anomalies présentes en FN2 sont les suivantes : 
 
a)  Dans  un  ajout :  impossible  d’enregistrer  le  salaire  dʹun  électricien,  si  ce  dernier  nʹest  pas  à 
lʹemploi dʹun constructeur agréé. En effet, il y aurait violation de la contrainte de validité de la 
clé qui interdit les tuples avec une clé nulle.   
  
 b) Dans une suppression : si la société Les Murs Inc dépose son bilan, la suppression du tuple la 
décrivant fait perdre une information qui ne dépend pas de cette société, à savoir le salaire du 
plâtrier qui est prévu par la convention des métiers.  
 
c)  Dans  une  modification  :  si  la  société  CGE  inc  embauche  dorénavant  des  menuisiers  (en 
remplacement des électriciens) au nouveau salaire de 43K$ conventionné par le nouveau décret 
gouvernemental,  ce  changement  entraînera  une  mise  à  jour  du  tuple  concernant  CGE  inc.  En 
modifiant  le  salaire  de  40K$  à  43K$  pour  cette  seule  société,  il  y  a  apparition  dʹune  anomalie. 
Elle  invalide  la  DF  qui  sous‐tend  un  seul  salaire  pour  le  métier  dʹélectricien.  L’anomalie 
s’explique  par  la  présence  d’une  DF  transitive  dans  le  schéma  de  la  relation.  Il  suffira  d’isoler 
une des deux DF impliquées dans la transitivité pour corriger l’anomalie observée. En isolant la 
DF : metier ‐‐> salaire dans une nouvelle relation, deux relations sont alors créées, et cela, sans 
perte d’information par rapport à l’extension initiale de la relation Constructeur  (théorème de 
Heath) :   
R1 (societe*, metier) R2 (metier*, salaire)
 

r2 :  metier*  salaire 


  plâtrier  45 K$ 
  plombier  35K$ 
  électricien  40 K$ 
  menuisier  35 K$ 
     

 
Chapitre 10 Théorie de la normalisation   80

r1 :  societe*  metier 


  PEG Inc.  menuisier 
  Delna Ltée  menuisier 
  Les murs XIC  plâtrier 
  Eclair enr  électricien 
  WC inc  plombier 
  Beaux bois  menuisier 
  CGE Inc.  électricien 
  Lux inc.  électricien 
Figure 10.13 
 
Les extensions des nouvelles relations sont obtenues par projection de la relation initiale sur les 
nouveaux  schémas.  La  transformation  qui  élimine  la  transitivité  des  DF  est  sans  perte 
d’information, car R1 ∩ R2 = metier et metier ‐‐> salaire dans R2.  La condition étant respectée, la 
décomposition est valide et aucune perte de données n’est possible.                                        
Définition de la FN3 
Une relation Rj est en FN3 si et seulement si les conditions suivantes sont vérifiées : 
                             
a) l’extension rj ou le schéma Rj est en FN2;  
b) et tout attribut non primaire du schéma ne dépend dans F+  que d’une clé (primaire 
ou candidate) de  Rj . 
 
Il  apparaît  donc  que  le  premier  schéma  Constructeur  n’était  pas  en  FN3,  mais  qu’après  la 
décomposition, les relations résultantes de la décomposition sont en FN3.  
Forme FNBC (Forme normale de Boyce Codd)   
Certaines  anomalies  persistent  même  si  la  relation  est  en  FN3.  Ces  anomalies  tiennent  au  fait 
que la transitivité impliquait seulement des attributs non primaires. Or, il arrive que les attributs 
primaires interviennent dans la transitivité, comme cela est illustré par lʹexemple type proposé 
par Gardarin 15.                
 
Exemple : 
  Vin(vignoble*, pays*, region) 
avec F ={vignoble, pays ‐‐> region;  region ‐‐> pays} 
 
Le schéma de la relation Vin a une extension en FN3, mais affiche encore des anomalies. Elle est 
en FN3, car tous ses attributs non primaires (soit dans cet exemple region) sont en dépendance 
fonctionnelle que sur une clé. Toutefois, il y a une DF entre un attribut primaire (soit pays) et un 
attribut non clé (soit region) qui est à la source de  quelques anomalies résiduelles.  
 

 
Chapitre 10 Théorie de la normalisation   81

vin :  vignoble*  pays*  region 


  Chenas  Chili  Santiago 
  Chenas  France  Beaujolais 
  Juliénas  France  Beaujolais 
  Morgan  France  Bordelais 
  Brouilly  France  Beaujolais 
  Chablis  USA  Californie‐Est 
Figure 10.14 
 
Dans  cet  exemple,  si  le  vignoble  Chablis  se  retire  des  affaires,  le  tuple  correspondant  est 
supprimé et comme il est le dernier de cette extension, il y a perte d’information, à savoir que 
l’est  de  la  Californie  est  une  région  productrice  de  vin.  Cette  anomalie  est  corrigée  si  la  DF 
impliquant un attribut primaire est isolée dans une nouvelle relation. 
 
R1(region*, pays)  R2(vignoble*, region) 
avec F1= {region ‐‐> pays}   avec F2 = {vignoble ‐‐> region }  
                                 
La transformation est sans perte, puisque R1 ∩ R2 = region et que region ‐‐> pays est une DF de 
R1.        
Conservation des DF dans un schéma 
Dans le nouveau schéma de lʹexemple ci‐dessus, F1 ∪ F2 = F’ ⊆ F. Il y a donc perte d’une DF par 
la  transformation,  sauf  s’il  est  possible  de  démontrer  que  la  DF  absente  {vignoble,  pays  ‐‐> 
region} appartient à la fermeture de F’, soit (F’)+, ou que région ⊆ {vignoble, pays}+  dans F’. Si 
cette dérivation dans F’ est impossible, alors la transformation ne conserve pas les DF, mais elle 
est  sans  perte  de  données.  Toutefois,  il  serait  possible  de  renforcer  la  DF  perdue  par  une 
procédure de contrainte (par un déclencheur) qui calculerait la jointure R1 |x| R2 à chaque mise 
à jour de l’une ou l’autre des deux relations et cela, afin de vérifier si la DF {vignoble, pays ‐‐> 
région}  est  satisfaite  par  l’extension.  Dans  le  cas  contraire,  la  mise  à  jour  est  refusée  par  le 
déclencheur de BD (trigger). C’est un processus qui peut cependant devenir lourd pour le SGBD 
et pénaliser la performance du système.  
Une relation R est en FNBC si : 
a) elle est en FN1; 
b) et si chaque déterminant de toutes les DF de F pour la relation R est une 
clé  (primaire ou candidate). 

 
Il sʹagit dʹavoir les attributs primaires et non primaires qui dépendent que dʹune clé dans F. S’il 
existe une transformation (CDF) en FN3 qui permet de conserver les DF et les données, il peut 
ne pas y avoir de transformations d’une relation qui conduisent à la forme FNBC qui fournit  un 
schéma qui soit à la fois CDD et CDF. 
Algorithme de transformation dʹune relation R en  FNBC 
Lʹalgorithme de passage est le suivant :  

 
Chapitre 10 Théorie de la normalisation   82

Résultat = R ! le schéma initial contient que celui de R; 
Calcul de F+; ‐‐ non nécessaire si lʹappartenance est testée  
Tant que la relation Résultat n’est pas en FNBC 
choisir une DF, X ‐‐> Y dans F de R tel que X n’est pas une clé et donc que X ‐‐> R ∉F+ 
Resultat = {(Resultat ‐ R), (R ‐ Y), (X, Y)}  
‐‐ le résultat contient plusieurs schémas de relation  
Fin. 
 
Lʹalgorithme  est  aussi  valable  lorsque  lʹon  traite  un  ensemble  de  schémas  S  de  la  BD.    La 
première affectation est alors Résultat = S. 
Schéma FNBC de la BD  
Le  cas  d’un  schéma  de  BD  en  FNBC  correspond  à  celui  d’une  BD  composée  de  schémas 
relationnels en FNBC non redondants, c’est‐à‐dire qu’aucun schéma de relation ne correspond à 
une projection d’un autre schéma de la BD. Si un schéma contient entièrement un autre schéma, 
ce dernier peut être supprimé. 
Test de FNBC  (Problème de complexité P) 
Pour  tester  si  un  schéma  relationnel  R  est  en  FNBC,  il  suffit  de  vérifier  que  le  déterminant  de 
chaque  dépendance  dans  F  est  une  superclé  de  R,  et  cela,  en  calculant  la  fermeture  de  chaque 
déterminant par rapport à F. Lʹalgorithme est une fonction polynomiale par rapport au nombre 
dʹattributs dans R.                           
Dépendance entre les formes de normalité des relations 
Si R est une relation, alors  
a) si elle est en FN2 ⇒ FN1    b) si elle est en FN3 ⇒ FN2 
c) si elle est en BCFN ⇒ FN3                                                              
 
Une relation FNBC est en  FN4 (cette 4e forme normale sera étudiée plus loin dans ce chapitre) si 
son schéma relationnel a au moins une clé (primaire ou candidate) constituée d’un seul attribut. 
De  même,  il  a  été  démontré  quʹune  relation  en  FN3  est  aussi  en  forme  normale  de  jointure  si 
cette première nʹa que des clés formées avec un seul attribut.  
 
Autre exemple :  
 
Soit la relation Code(ville*, adresse*, codePostal) 
  avec F = {ville, adresse −−> codePostal; codePostal −−> ville} 
 
Quelle est la clé de cette relation ?  
Est‐ce  que  cette  relation  est  en  FN3 ?  Oui,  car  les  attributs  non  primaires  dépendent  que  de  la 
clé. Est‐elle en FNBC ? Non, car il y a un attribut primaire qui dépend dʹun attribut (codePostal) 
qui nʹest pas une clé de la relation. 
 
Voici une extension possible de cette relation : 

 
Chapitre 10 Théorie de la normalisation   83

 
code :  ville*  adresse*  codePostal 
  Québec  2 des Pins  F2P 
  Valcartier  8 des Cèdres  S4T 
  Québec  5 des Roses  F2W 
  Ste‐Foy  8 des Mélèzes  U5E 
 
Anomalies observées 
Les anomalies classiques sont encore présentes dans la relation Code : 
a)  la  suppression  du  tuple  <Ste‐Foy,  8  des  Mélèzes,  U5E>  fait  perdre  une  information  à  savoir 
que le code codePostel de Ste‐Foy est le U5E;   
 
b)  l’ajout  d’un  code  postal  pour  une  nouvelle  ville  n’est  pas  possible  sans  qu’il  y  ait  au  moins 
une adresse de définie avec ce nouveau code. 
En transformant cette relation pour obtenir la forme FNBC, on a : 
                      
r1 :  ville *  codePostal* 
  Québec  F2P 
  Valcartier  S4T 
  Québec  F2W 
  Ste‐Foy  U5E 
         
r2 :  adresse*  codePostal 
  2 des Pins  F2P 
  8 des Cèdres  S4T 
  5 des Roses  F2W 
    8 des Mélèzes  U5E 
                           
Exemple : Transformez la relation R (A, B, C, D, E) avec F = {A −> B, C;  C, D −> Ε; B −> D;  E −> 
A}.  
Quelles sont les clés de R ?  
La clé est A, car la fermeture de A, soit A+   est A,B,C,D,E. La relation R est en FN2, mais pas en 
FN3 en raison des DF transitives. Il faut donc la décomposer. Avec la DF, B −> D création de R1 
(B*,  D),  une  relation  en  FNBC  et  réduire  la  relation  initiale  du  schéma  à  R2(A,  B,  C,  E).  Cette 
relation R2 a les DF suivantes : {A −> B, C; E −−> A}.  
 
Cette relation R2 n’est pas en FN3, car la clé de R2 est E, A −−> B, C et E −>A, c’est‐à‐dire que ce 
sont des attributs non primaires qui dépendent de A qui n’est pas une clé. Il faut donc effectuer 
une nouvelle transformation pour obtenir les relations :   
  R21 (E*, A) avec F21 = {E −> A} 
  R22 (A*, B, C) avec F22 = {A −> B, C} 
 

 
Chapitre 10 Théorie de la normalisation   84

Ces  deux  dernières  relations  sont  alors  en  FNBC  et  les  clés  sont  respectivement  A  et  E.  La 
recherche  des  clés  est  un  processus  complexe,  allégé  par  la  sémantique  qui  vient  suggérer  des 
clés possibles. 
Algorithme de synthèse de Bernstein 16• 
On  a  proposé  un  algorithme  de  synthèse  du  schéma  relationnel  fondé  essentiellement  sur  la 
connaissance  des  dépendances  fonctionnelles.  La  base  de  lʹalgorithme  est  simple:  partitionner 
les  DF    pour  les  attributs  identifiés et  ce,  sur  la  base  du  même  déterminant.  Créer ensuite  une 
relation pour chaque partition obtenue dans lʹopération précédente.  
                          
Cette  version  minimale  de  lʹalgorithme  ne  résout  pas  tous  les  problèmes  qui  découlent  de  la 
présence dʹattributs redondants, de DF redondantes et de clés équivalentes dans F et F+. 
 
Considérons les exemples ci‐dessous qui illustrent bien les cas particuliers qui faut traiter pour 
préciser l’algorithme général de synthèse de Bernstein : 
a) Présence d’un attribut étranger dans une DF  
Soit R(A, B, C, G) si F = { A, B, C ‐‐> G ; A ‐‐> C}, alors on aurait par l’algorithme de synthèse 
deux relations : R1 (A*, B*, C, G) et R2(A*, C). Avec R1 qui n’est pas en FN2. 
 
La clé de R1 est (A, B) et non (A, B, C, G) puisque A, B ‐‐> C, G ∈ F+. La décomposition de R1 
fournit alors les relations FN2 : R11(A*, B*, G) et R12(A*, C). Lʹenlèvement de lʹattribut étranger 
(C) dans le dépendance A, B, C ‐‐> G conduit directement par la synthèse à R(A*, B*, G). 
 
b) Présence dʹune DF redondante cause aussi des problèmes lors de la synthèse. 
Si F = {W ‐‐> P; W ‐‐> Z; P ‐‐> Z} génère le schéma: R1(W*, P, Z) et R2(P, Z). Toutefois, R1 nʹest 
pas en FN3, car P ‐‐> Z. 
                    
c) Présence de clés équivalentes 
Soit F = { Y ‐‐> M; W ‐‐> G; W ‐‐> Y; Y ‐‐> W}. La synthèse qui en découle fournit le schéma R1(W, 
G, Y) et R2(Y, M, W). Or ce schéma peut être combiné en un seul R(W*, G, Y, M) qui est en FN3. 
La fusion est effectuée sur la base des clés équivalentes. 
Soit F ={X ‐‐> B; B ‐‐> Z; Z ‐‐> X} alors la synthèse fournit le schéma suivant qui inclut trop de 
relations  :  R1(X*,  B),  R2(B*,  Z),  R3(Z*,  X).  En  ne  tenant  pas  compte  des  clés  équivalentes  
présentes dans la fermeture de F, la synthèse fournit trop de relations. La relation R(X*, B, Z) est 
en FN3. 
 
d) Soit F = { A, B ‐‐> D, P;  C, D ‐‐> A, B;  P, B ‐‐> I;  I, A ‐‐> C;  C ‐‐> P}, alors le schéma généré est 
le suivant:  
R1 (A, B, C*, D*, P)  qui nʹest pas en FN2 puisque C ‐‐> P 
R2 (P*, B*, I) 
R3 ( I*, A*, C) 
R4 (C*, P) 
 

 
Chapitre 10 Théorie de la normalisation   85

La présence dʹun attribut étranger dans un déterminé invalide la condition de la FN2 pour R1.  
Ces problèmes sont gérés en tenant compte de la couverture minimale, de la suppression des DF 
et des attributs redondants. 
 
Version 2 de lʹalgorithme de synthèse de Bernstein 
Input : un ensemble de DF : 
F = {X‐>A;  X‐>B; A‐>Y;Y‐>B; Y‐>X; Y‐>C; C‐>B} 
Output : un ensemble de relations normalisées;    
 
a) Enlevez les attributs redondants ou étrangers dans chaque DF de F : 
  F1 = F‐{attributs étrangers) 
 
Alors,  il  y  a  diminution  du  nombre  d’attributs  dans  les  dépendances  ce  qui  les  simplifie  et 
facilite le calcul de la couverture. ans cet exemple, il y aucun attribut étranger, donc F1 = F; Le 
graphe des dépendances de F1 est le suivant : 
 
F = {X‐>A;  X‐>B; A‐>Y;Y‐>B; Y‐>X; Y‐>C; C‐>B} 
          
X  A B
 
 
 
 
 
  Y  C
 
 
 
b) Calculez la couverture minimale de F1 : (F1)* = F2 
Cela permet de diminuer le nombre de dépendances fonctionnelles du schéma. 
  F2 = {X‐>A; Y‐>C; A‐>Y; C‐>B; Y‐>X} (flèches en gras) 
 
Le graphe des dépendances de F est donné ci‐dessous :                                                      

 
Chapitre 10 Théorie de la normalisation   86

 
  X A B
 
                   
 
 
  Y  C
 
 
 
Le  graphe  de  la  couverture  est  celui  obtenu  en  enlevant  les  arcs  qui  peuvent  être  dérivés  des 
autres.  
 
  X A B
 
 
 
 
              Y  C
 
 
c)  Regroupez  les  dépendances  restantes  en  partitions  de  même  déterminant.  Cette  opération 
identifie la base des relations en FN3;   
p1    p2    p3    p4 
X −> A   Y −> C   A −> Y   C > B 
    Y −> X 
d)  Trouvez  les  clés  équivalentes  dans  la  fermeture  de  la  couverture  minimale  (F2)+.  Une  clé 
équivalente est définie entre deux ensembles dʹattributs X et Y dont les DF X −> Y et Y −> X sont 
dans  F.  Elles  correspondent  aux  arcs  doublés  dans  le  sens  inverse.  Les  trois  clés  équivalentes 
dans F sont les suivantes : 
   { (X −> A et A −>X);  (A −> Y et Y −> A);  (X −> Y et Y −> X)} 
e) Enlevez les dépendances fonctionnelles qui correspondent aux clés équivalentes explicitement 
formulées dans F pour obtenir ainsi F3 représenté par le graphe suivant : 
 
      X  B
A
 
 
 
 
 
  Y  C
 
 

 
Chapitre 10 Théorie de la normalisation   87

 
‐Formez l’ensemble H  avec les dépendances des clés équivalentes; 
    H = {X −> A; A −>X; A −> Y; Y −> A; X −> Y ; Y −> X}  
  ou  H = {X <−> A, Y <−> A, X <−> Y}, 
 
‐Calculez la couverture minimale de F3 afin de conserver le minimum de DF dans F3, soit(F3)* .  
 
Formez ensuite le nouvel ensemble de dépendances F4 = (F3)* ∪ H pour avoir obligatoirement 
les  clés  équivalentes  dans  l’ensemble  des  dépendances  de  départ.  Le  graphe  de  F4  est  alors  le 
suivant :    
                                     
 
  X  A B
  
  
 
 
       
                        Y  C
 
 
 
f) Regroupez les DF de F4 en partitions de même déterminant. Les partitions sont :   
p1    p2    p3    p4 
X −> A   Y −> A   A −> X   C −> B 
X −> Y   Y −> C   A −> Y 
Y −> X          
‐ Fusionnez les partitions dont le déterminant est une clé équivalente : 
  X −> A   C −> B 
  X −> Y 
  Y −> A 
  Y −> C 
  Y −> X 
  A −> X 
  A −> Y 
‐ Construisez une relation avec chaque partition des DF. 
  R1 (X, Y, A*, C)   R2 (C*, B) 
          
Les  relations  obtenues  sont  en  FNBC,  car  les  attributs  non  primaires  ne  sont  en  dépendance 
fonctionnelle que sur des clés. Mais comme X, Y et A sont des clés équivalentes, il est possible de 
choisir qu’une seule clé parmi les trois disponibles et ainsi simplifier le schéma : 
  R1 (A*, C)   R2 (C*, B) 
 

 
Chapitre 10 Théorie de la normalisation   88

Ces  deux  relations  en  FN3  sont  aussi,  comme  nous  le  verrons  plus  loin,  en  forme  FN4. 
L’algorithme  proposé  par  Bernstein  ne  fournit  pas  à  chaque  fois  un  schéma  de  relations  sans 
perte  d’informations.  Il  faut  donc  toujours  tester  les  relations  obtenues  par  synthèse,  pour 
vérifier s’il y a conservation des données (CDD).     
Optimisation du schéma  
La  normalisation  minimise  la  redondance  et  limite  celle‐ci  aux  seules  clés  (primaires  et 
étrangères)  qui  sont  alors  contrôlées  par  le  SGBD.  Dans  certains  cas,  pour  obtenir  une 
performance  élevée,  il  faudra  faire  le  processus  inverse  et  créer  volontairement  la  redondance 
pour  éviter  le  calcul  toujours  lourd  de  certaines  jointures.  Il  peut  en  être  même  pour  les 
contraintes d’intégrité qui peuvent être renforcées ou désactivées, voire abandonnées pour des 
fins  de  performance.  Cette  dénormalisation  ou  insertion  volontaire  de  la  duplication  des 
attributs  entraîne  un  travail  supplémentaire  lors  des  modifications  et  des  ajouts,  car  il  faudra 
propager  ces  changements  dans  les  relations  où  il  y  a  les  attributs  redondants  touchés  par  la 
mise à jour ou l’ajout.                                     
10.7 Dénormalisation et performance dans le calcul des requêtes 
Chaque étape dans la mise en oeuvre d’une base est concernée par l’optimisation17. C’est donc 
une préoccupation aussi bien du concepteur de la BD lors de la normalisation que du DBA qui 
en gère, par la suite, l’exploitation.   
                                                 
Atelier 
                                                          
noAt*  1..1 
 
nomAt 
  FicheApprov 
site  1..*  noFicheApprov* 
 
  dateFiche  
  1..* LigneFiche 
  noLigne*  
1..1
  qte  
  1..* 
1..1  Piece 
 
Stock  noPi*  
  noStock*  1..* 
categorie  
  qteStock  1..1 cout  
 
             
            Figure 10.16 
 
Bien  que  l’optimiseur  du  SGBD  soit  de  plus  en  plus  intelligent,  il  ne  peut  pas  remplacer  le 
concepteur de la BD qui est en mesure de mieux estimer les caractéristiques de l’exploitation des 
données  par  les  applications.  Ces  considérations  influenceront  les  étapes  de  design  et,  par  la 
suite façonneront les réorganisations éventuelles du schéma. La dénormalisation est l’opération 
qui consiste à insérer dans diverses relations certains attributs présents dans d’autres relations 
pour  éviter  le  calcul  de  quelques  jointures.  Cette  opération  sous‐tend  un  contrôle 
supplémentaire  par  déclencheur  afin  que  toute  mise  à  jour  d’un  tel  attribut  soit  propagée  aux 
autres relations ayant le même attribut. Elle dénormalise aussi le schéma relationnel.  

 
Chapitre 10 Théorie de la normalisation   89

 
Le schéma relationnel de ce modèle réseau est le suivant : 
Atelier (noAt*, nomAt, site)
FicheApprov (noFicheApprov*, dateFiche, noAt)
LigneFiche (noLigne*, noPi*, noFicheApprov*, qte)
Piece (noPi*, categorie, cout, noStock)
Stock (noStock*, qteStock)
Ce  schéma  est  normalisé  en  FN3.  Sa  structure  n’est  cependant  pas  adaptée  au  calcul  de  toute 
requête.    Par  exemple  la  requête  simple  ci‐dessous  exige  un  calcul  lourd  au  regard  du  schéma 
nomalisé : 
 
Quel  est  le  nom  des  ateliers  qui  ont  demandé  un  approvisionnement  en  pièces  de  la  catégorie  
ʹfreinʹ ? 
Pour calculer la réponse, il faut consulter quatre relations et faire trois jointures : 
SELECT nomAt /*nom atelier */
FROM Atelier A, FicheApprov F, LigneFiche L, Piece P
WHERE A.noAt = F.noAt AND
F.noFicheApprov = L.noFicheApprov AND
L.noPi = P.noPi AND P.categorie = 'frein';
 
Il  faudra  faire  trois  jointures  parce  que  la  réponse  nécessite  d’accéder  à  un  attribut  de  la 
relation Atelier et un autre de la relation Piece.   
 
  Projection sur nomAt
 
 
  Jointure 3
 
  Atelier  Jointure 2
 
 
  FicheApprov Jointure 1
 
  LigneFiche Piece
 
          Figure 10.18 
 
Plan général dʹexécution  
Le plan est établi par l’optimiseur, selon les règles suivantes : 
 
Consultation des index dans un ordre donné;  
Exploitation des cardinalités d’extension des tables visées (approche coût); 
Exécution  des  jointures  et  des  autres  opérateurs  selon  des  règles  syntaxiques  de 
l’optimiseur. 

 
Chapitre 10 Théorie de la normalisation   90

 
La  présence  de  trois  jointures  ralentit  sensiblement  le  calcul  de  la  réponse  à  cette  requête.  La 
première opération consiste à consulter la table Pièce pour trouver le numéro de la pièce libellée 
frein. Ensuite, ce numéro permet de faire une première jointure avec la table LigneFiche dont le 
résultat fournira le   numéro de fiche nécessaire pour la deuxième jointure. Ensuite, le résultat 
donnera le numéro d’atelier pour faire la dernière jointure qui fournira le nom de l’atelier.   
 
Plan détaillé du traitement de la requête  
Tous les attributs qui font lʹobjet dʹune référence dans les conditions de jointure sont présumés 
indexés. Le plan détaillé est le suivant :    
a) Sélection de la relation Pièce pour y trouver le numéro (noPi) de la catégorie des freins.  
              
b)  Avec  ce  noPi,  accès  à  l’index  sur  LigneFiche  pour  obtenir  les  numéros  de  fiche 
d’approvisionnement concernant cette pièce (1ère jointure).  
                 
c) Avec les numéros de LigneFicheApprov, accès à l’index de FicheApprov pour en obtenir les 
numéros des fiches qui ont demandé des freins;  
 
d) Avec les tuples associés à chaque noFicheApprov, accès à l’index de Atelier en vue dʹobtenir 
le numéro des ateliers (noAt) visés, pour accéder ensuite à la relation Atelier afin dʹavoir, par un 
des accès avec le ROWID, le nom des ateliers demandés. 
Dénormalisation pour accélérer le calcul  
La  dénormalisation  évite  de  faire  les  jointures  coûteuses  prévues  ci‐dessus  dans  le  plan 
d’exécution.  Cette  opération  est  l’inverse  de  la  normalisation  et  ne  doit  être  faite  qu’à  partir 
dʹune  BD  normalisée.  La  dénormalisation  est  d’autant  plus  intéressante  que  le  nombre  de 
requêtes de ce type augmentait avec le temps.  
cependant une simplification du plan d’exécution en supprimant la première jointure.       
 
 
Projection sur nomAt 
 
 
nomAt
  Jointure 3
 
 
noAt
  Atelier  Jointure 2
 
  FicheApprov LigneFiche1 
 
            Figure 10.18 

 
Chapitre 10 Théorie de la normalisation   91

Cʹest  donc  une  opération  à  faire  lorsque  les  accès  aux  données  par  les  applications  sont  bien 
connus et relativement stables : 
La jointure 1 peut être supprimée en dupliquant l’attribut categorie dans la relation LigneFiche 
dont le schéma devient alors le suivant : 
LigneFiche1(noLigne, noPi, noFicheApprov, qte, categorie) 
 
Cette nouvelle relation comporte une redondance pour l’attribut categorie, qui permet 
 
Requête modifiée : 
SELECT nomAt
FROM Atelier, FicheApprov, LigneFiche1 LF1
WHERE Atelier.noAt = FicheApprov.noAt AND
FicheApprov.noFicheApprov = LF1.noFicheApprov
AND LF1.categorie = 'frein';
 
b)  La  jointure  2  peut  aussi  être  supprimée  en  dupliquant  l’attribut  noAt  dans  la  relation 
LigneFiche1 pour obtenir une nouvelle relation : 
LigneFiche2  (noLigne*, noPi*, noFicheApprov*, qte, categorie, noAt) 
                           
La  requête  ne  comprend  alors  qu’une  seule  jointure,  soit  la  3.  Deux  attributs  redondants  sont 
maintenant  à  contrôler  par  des  déclencheurs  appropriés  (non  désactivables)  du  type 
préinsertion : 
SELECT nomAt
FROM Atelier, LigneFiche2 LF2
WHERE Atelier.noAt = LF2.noAt
AND LF2.categorie = 'frein';
 
c) La jointure 3 de la requête pourrait être également supprimée en dupliquant l’attribut nomAt 
dans LigneFiche2 pour ainsi obtenir une nouvelle relation LigneFiche3. 
LigneFiche3
(noLigne*,noPi*,noFicheApprov*,qte,categorie,noAt,nomAt)
 
Le nouveau schéma dénormalisé devient donc le suivant :   
  
Atelier (noAt*, nomAt, site) 
FicheApprov (noFicheApprov*, dateFiche, noAt ) 
LigneFiche3(noLigne*,noPi*,noFicheApprov,qte,categorie,noAt,nomAt)  
Piece (noPi*, design, cout, nomStock) 
La requête se réduit alors à une seule sélection sur la nouvelle relation : 
SELECT nomAt 
FROM LigneFiche3 as LF3 
WHERE LF3.categorie = ʹfreinʹ; 
 

 
Chapitre 10 Théorie de la normalisation   92

En  résumé,  le  processus  de  dénormalisation  consiste  à  enrichir,  dans  une  même  cascade  de 
jointures la relation de base de la jointure la plus profonde, et cela avec les attributs de jointure 
utilisés pour passer d’une relation à l’autre. 
Inconvénients de la dénormalisation des relations  
La  dénormalisation introduit une redondance qui sous‐tend son lot d’inconvénients : 
a) les actions d’ajout et de mise à jour sont ralenties par les déclencheurs (triggers) qui doivent 
propager les mises à jour et vérifier les contraintes. Ainsi, pour ajouter un nouveau tuple dans 
LigneFiche3  avec  une  valeur  pour  les  attributs  noLigne,  noFicheApprov,  noPi  et  qte,  il  faudra 
trouver  la  valeur  correspondante  à  design,  noAt  et  nomAt  en  effectuant  les  sélections  ou  les 
jointures  appropriées.  Sans  une  indexation  adéquate  des  relations,  il  est  possible  que  les  gains 
obtenus par la dénormalisation soient éclipsés par ces opérations inhérentes à la mise à jour; 
b) un espace disque supplémentaire est utilisé pour les données redondantes;    
c) augmentation des risques d’incohérence si les déclencheurs ne sont pas utilisés correctement 
(effet de cascade) pour assurer la propagation des ajouts et des mises à jour; 
d)  contrairement  à  la  création  des  index,  la  dénormalisation  d’une  base  de  données  n’est  pas 
neutre  pour  les  applications  qui  exploitent  les  données  de  la  base.  Il  faudra  les  modifier  pour 
tenir compte des nouveaux schémas et cela, afin qu’elles demeurent opérationnelles.  
10.8 Dépendances multivaleurs (DMV) 
La  présence  d’une  DMV  (multi‐valued  dependency)  permet  de  détecter  que  deux  ensembles 
d’attributs  sont  indépendants  dans  une  même  relation,  i.e.  non  contraints  l’un  par  l’autre.  Ils 
peuvent,  dans  certains  cas,  être  séparés  par  une  opération  de  projection  pour  diminuer  la 
redondance  et  les  anomalies  qui  en  découlent.  La  DMV  permet  de  diviser  une  relation,  même 
lorsqu’il y a absence de toute DF.  
Exemple :  R(employe*, diplome*, enfant*) avec l’ensemble F vide 
Cette  relation  R  est  en  forme  FNBC  puisque  tous  les  attributs  sont  primaires  et  qu’il  n’y  a  pas 
d’autres  DF  que  celles  qui  découlent  de  la  clé.  Certaines  anomalies  sont  encore  bien  présentes 
dans cette relation en FNBC. Par exemple, un employé sans enfant ne peut pas être représenté 
dans  cette  BD.  Une  dépendance  multivaleur  triviale  (appelée  aussi  multivaluée)  existe  entre 
deux attributs pris hors contexte (sans le contexte des autres attributs de la relation) si à une valeur 
du déterminant (partie gauche), correspond plusieurs valeurs du déterminé (partie droite). Par 
exemple,  si  un  employé  peut  avoir  plusieurs  enfants  sans  considérer  ses  diplômes,  cette 
dépendance  est  appelée  une  DMV  triviale  lorsquʹelle  est  formulée  sans  le  contexte  des  autres 
attributs  dʹune  relation.  En  dʹautres  mots,  une  DMV  triviale  dans  une  relation  sous‐tend  la 
notion de 1 à plusieurs (1:n) entre les deux attributs. Elle est notée par le formalisme : employe ‐
>> enfant. Lorsqu’une DMV est sans contexte, elle est aussi appelée une dépendance multiple.  
Si la conception aboutit à une relation avec plusieurs DMV dans la même relation, il peut y avoir 
un  problème  d’anomalies  et  de  redondance  si  les  déterminés  des  DMV  sont  indépendants.  La 
DMV revêt aussi une certaine importance dans la conception puisqu’elle permet de pousser un 
peu  plus  loin  le  processus  de  normalisation  des  tables.  Considérons  l’exemple  précédent,  un 
employé  a  plusieurs  enfants  et  aussi  plusieurs  diplômes.  Les  enfants  dont  il  est  le  parent  sont 
indépendants  des  diplômes  quʹil  a  obtenus.  (indépendance  entre  les  attributs  enfant  et 

 
Chapitre 10 Théorie de la normalisation   93

diplôme).Cette  indépendance  se  traduit  par  la  présence  de  DMV  dans  la  relation  R(employe*, 
enfant*, diplome*) :    
  employe ‐‐>> enfant    employe ‐‐>> diplome 
 
Si  un  employé  e1  a  deux  enfants  {f1  et  f2}  et  un  premier  diplôme  d1,  alors  l’employé  e1  a 
nécessairement ce même diplôme pour chaque enfant dont il est le parent. Cette information est 
représentée par deux tuples :     
                                                            
r:  Employe*  Diplome*  Enfant* 
  e1  d1  f2 
  e1  d1  f1 
 
Si l’employé e1 a un deuxième diplôme d2, alors il faut ajouter non pas un, mais deux nouveaux 
tuples  dans  lʹextension  de  la  relation.  Avec  ce  nouveau  diplôme  d2,    lʹemployé  e1  demeure 
toujours parent de f1 et f2 ! 
r:   Employe*  Diplome*  Enfant* 
  e1  d1  f2 
  e1  d2  f1 
  e1  d2  f2 
  e1  d1  f1 
 
Cette  multiplication  est  une  conséquence  des  DMV  présentes  dans  la  relation  et  dont  les 
déterminés sont indépendants. En effet, il n’y a pas de lien ou de dépendance entre les diplômes 
et  les  enfants.  Cette  indépendance  se  traduit  par  la  présence  obligatoire  de  toutes  les 
combinaisons  diplome‐enfant  dans  lʹextension  pour  chaque  employé  concerné.  Sʹil  y  avait  une 
combinaison absente, alors les deux attributs seraient dépendants, puisque quʹun diplôme serait 
inscrit,  par  exemple  que  pour  un  enfant.  Cette  même  relation  peut  être  aussi  formulée  comme 
une  relation  NF2  (Non  First  Normal  Form)  dont  l’extension  peut  être  représentée  par  les 
ensembles suivants :  
1er tuple :  {e1,{d1, d2}, {f1, f2}} 
2e tuple :  {e2,{d2}, {f3}} 
 
Dans  un  modèle  normalisé,  l’extension  est  composée  de  valeurs  atomiques  et  pour  s’y 
conformer, il doit y avoir multiplication des tuples dans la table. Le schéma R ci‐dessous est en 
forme FNBC.       R : (employe*, diplome*, enfant*) 
 
 r :  e1   d1   f1  
  e1   d1   f2  
  e1   d2   f1  
  e1   d2   f2  
*  e2   d2   f3  
 

 
Chapitre 10 Théorie de la normalisation   94

La  clé  est  composée  de  trois  attributs :  employe*,  diplome*,  enfant*.  Malgré  sa  forme  FNBC, 
cette  relation  contient  encore  des  anomalies.  Comment  peut‐elle  être  décomposée?  Quels  sont 
les critères pour sa décomposition ?                                          
Anomalies résiduelles dans R 
Les anomalies apparaissent en ajout, modification et en suppression : 
 
a) Il est impossible d’ajouter dans r un nouvel employé sans enfant : 
   < e4, d2, null >  car il y a violation de la contrainte de clé.            
 
b) Si l’enfant unique de e2 décède (*), son tuple doit être supprimé. Est‐il possible de conserver 
une  trace  du  diplôme  d2  de  e2  ?  Autrement  dit,    est‐il  impossible  de  garder  le  tuple  <e2,  d2, 
null> dans r ? 
 
c) L’employé e1 parent de f1 et titulaire du diplôme d1 doit être mis à jour pour que son diplôme 
porte dorénavant le nom de f5.  Une mise à jour limitée au premier tuple laisse la base dans un 
état incohérent. En effet, e2 possède toujours  le diplôme d1 pour le 3e tuple ! 
 
Ces anomalies découlent de la présence de la  DMV dans R, soit employe ‐>> diplome | enfant, 
c’est‐à‐dire du fait que les attributs diplome et enfant sont indépendants et se retrouvent dans la 
même  relation.  Une  DMV  est  une  contrainte  multiplicatrice  de  tuples :  si  un  tuple  doit  être 
présent dans l’extension r, d’autres doivent aussi obligatoirement l’être dans la même relation. 
Sur le plan sémantique, ceci traduit la réalité suivante : un employé a les mêmes diplômes pour 
tous ses enfants ! 
Voici  un  autre  exemple  représentant  lʹallocation  des  avions  sur  différents  vols.  La  relation 
Service  (vol*,  jour*,  avion*)  n’a  pas  de  DF  et  présente  une  redondance  qui  est  une  source 
d’anomalies. Les DMV non triviales sont les suivantes :   
vol ‐>> jour et vol −>> avion et  
Avec F qui est vide, il n’y a donc pas de DF dans la table Service.  
 
Est‐ce  que  ces  DMV  sont  validées  ou  présentes  dans  la  relation  Service ?  Si  oui,  il  y  a 
indépendance entre les attributs  jour et avion qui reflète la sémantique sous‐jacente à savoir que 
le type d’avion B747 dessert le vol 871 (Paris‐Montréal), le lundi et le mardi. Forcément, puisque 
la DMV non triviale est donnée valide, l’ajout d’un autre avion pour le vol 871 doit faire aussi le 
service les lundi et mardi.   
                                                     
service(1) :  vol*  jour*  avion* 
  871  lundi  B747 
  871  mardi  B747 
  871  lundi  DC10 
  871  mardi  DC10 
  870  jeudi  A330 
  870  jeudi  B747 

 
Chapitre 10 Théorie de la normalisation   95

Cette  relation  Service  peut  être  divisée  en  deux  autres  relations  pour  diminuer  la  redondance. 
Par contre, si les attributs jour et avion sont dépendants, la DMV ne serait pas alors validée, la 
division  de  la  relation  ne  pourrait  pas  se  faire  sans  perdre  de  lʹinformation.  Par  exemple, 
lʹextension service(2) ci‐dessous a des dépendances multiples ou DMV triviales qui ne créent pas  
de problème à faire cohabiter certains tuples dans la même relation.   
                           
service(2) :  vol*  jour*  avion* 
  871  lundi  B747 
  871  mardi  B747 
  871  lundi  DC10 
  870  jeudi  A330 
  870  jeudi  B747 
 
Le vol 871 du mardi assuré par le DC10 n’a pas l’obligation de l’être aussi le lundi ! 
Cette dernière extension correspond à une relation qui ne peut pas être décomposée sans perte 
de données. Par contre, la première extension correspond à une relation service(1) qui peut être 
décomposée sur la base d’une DMV valide et cela, pour aboutir à deux nouveaux fragments de 
table qui ne présentent plus les anomalies observées dans la relation dʹorigine. 
                           
volJour :  vol  jour  volAvion :  vol  avion 
  871  lundi    871  B747 
  871  mardi    870  DC10 
  870  jeudi    870  A330 
        870  B747 
La relation Service(1) peut donc être divisée sans perte d’information (CDD) et cela, grâce à la 
DMV présente. 
                     
Dépendance entre les attributs 
Lorsque  deux  attributs  sont  dépendants,  la  DMV  n’existe  pas  dans  la  relation  ou  en  d’autres 
mots, elle existe avec un contexte vide, i.e. sans aucun autre attribut dans la relation que les deux 
attributs dépendants. 
Exemple :   
R1 :  employe*  diplôme*  pgmEtudes* 
  jean  DEC  arts 
  jean  DEC  sciences po 
  jean  bacc  sciences po 
  louise  DEC  humanités 
  sylvie  DEC  sciences 
  pierre  DEC  santé 
  claire  Bac  santé 
  jean  PhD  droit    <‐‐ 
 

 
Chapitre 10 Théorie de la normalisation   96

Est‐ce  que  la  DMV  employe ‐‐>>diplome  est  vérifiée  ou  valide  dans  cette  relation  R1 ?  En 
d’autres  mots,  est‐ce  que  les  attributs  diplome  et  pgmEtudes  sont  indépendants ?  Si  la  DMV 
était confirmée, alors l’indépendance des deux déterminés le serait aussi.  
 
Si Jean obtenait un PhD en droit, il n’aurait pas automatiquement aussi le DEC et le Bac en droit; 
un  seul  tuple  serait  ajouté  dans  l’extension  dans  le  cas  où  le  diplôme  est  dépendant  de 
pgmEtudes.  Si  tel  est  le  cas,  la  DMV  employe ‐/‐>>  diplome  n’est  pas  validée  dans  R1  et  cela,  
même s’il existe une association (1‐n) entre les attributs employe et diplome.  Notez cependant, 
qu’une  DMV  qui  n’est  pas  validée  dans  R1,  pourrait  cependant  apparaître  dans  une  autre 
relation (ou projection) et être vérifiée. Dans un tel cas, la DMV est dite imbriquée (latente) dans 
la  première,  R1.  Dans  l’exemple  R1  ci‐dessus,  la  DMV  n’est  pas  validée  et  par  conséquent  les 
attributs diplome et pgmEtudes sont considérés dépendants.            
      
Définition formelle de la DMV dans le contexte dʹune relation 
Comment détecter qu’une DMV formulée hors contexte est validée dans une relation ? Soit R(A, 
B, C) et α(R) = 3 avec a, b, c comme valeurs quelconques (constantes) de A, B et C. 
 
Définition de l’opérateur : Ba,c = {b | (a, b, c) ∈ r} pour une paire donnée (a, c) 
Soit la DMV hors contexte : A‐>> B,  elle est validée dans la relation R(A, B, C) (et donc A ‐>> C),  
si B(a, c) =  B(a, c’) pour chaque (a, c) et (a,c’), pour (a, c) ∈ R[A, C] et (a, c’) ∈ R[A, C].              
 
Si  l’ensemble  des  éléments  de  B  ne  varie  pas  quelle  que  soit  la  valeur  de  C  (c  ou  c’),  alors 
l’attribut  B  est  indépendant  de  C  dans  la  relation  R  et  la  DMV  est  validée  ou  existe  dans  le 
contexte de cette relation. On peut aussi de démontrer que la DMV A‐‐>> B est validée dans R(A, 
B, C) si pour toute paire de tuples dans r ayant la même valeur pour l’attribut A : (a, b, c) et (a, 
b’, c’), on a aussi (a, b’, c) et (a, b, c’) ∈ r.    
  
A         B        C  A         B        C 
 
a          b         c  a          b’         c 
 
   
 
   
 
a          b’        c’  a          b        c’ 
 
   
 
 
 
Autre définition de la DMV 
 
Dans R(X, Y, Z), la DMV X ‐‐>> Y est validée si, pour toute extension de R, pour chaque paire de 
tuples avec le même X, alors deux autres tuples de r obtenus en intervertissant les valeurs de Y 
de la paire de départ sont obligatoirement dans l’extension.  
                                           

 
Chapitre 10 Théorie de la normalisation   97

Exemple :  r :  X  Y  Z 
    1  1  1 
    1  2  2 
    1  2  1 
    1  1  2 
    2  2  2 
 
Cette extension est présumée représenter tous les cas de figure admis pour cette relation, c’est‐à‐
dire que toutes les dépendances de cette relation sont vérifiées. 
 
Test de validité dʹune DMV dans une relation 
Pour  toute  paire  de  tuples  de  R  partageant  la  même  valeur  d’attribut  X,  d’autres  tuples  sont 
obligatoirement présents dans l’extension de R (X,Y, Z).     
                                                        
  X, Y, Z     
X ‐‐>> Y ?  1, 1, 1  ⇒  1, 2, 1 
  1, 2, 2    1, 1, 2 
  1, 1, 1  ⇒  1, 2, 1 
  1, 2, 1    1, 1, 1 
 
X ‐‐>> Z ?  1, 1, 1  ⇒  1, 1, 2 
  1, 2, 2    1, 2, 1 
  ...     
                                    
Cas particulier  
Si dans R(A, B, C), A −> B et si pour toute paire (a,c) et (a, c’), on a  
Ba,c = {b |(a, b, c) ∈r} = {b1} et si Ba, c’ = {b | (a, b, c’) ∈ r} = {b1} (avec un seul élément), alors A‐
>> B Donc si A ‐> B, alors A‐>> B et  mais l’inverse nʹest pas vrai. De façon similaire avec A ‐‐> B, 
si pour toute paire de tuples partageant la même valeur pour A, la relation suivante est vérifiée : 
 
(a, b, c )      ( a, b, c)   
  Si ∈ r  alors    ∈ r 
(a, b, c’ )      (a, b, c’ )   
 
La condition est toujours vérifiée avec une DF. Toute DF est donc une DMV. 
Décomposition dʹun schéma de BD 
Cas 1 : décomposition avec DF, mais sans DMV  
Soit le schéma de la relation universelle et lʹensemble F des DF : (U, F) :  
U = {titre, realisateur, heure, prix, cinema, adresse, tel} et  
F = {DF1 : cinema −> adresse, tel;  
DF2 : cinema, titre, heure −> prix;  
DF3 : titre −> realisateur } 
 

 
Chapitre 10 Théorie de la normalisation   98

L’algorithme de décomposition en FNBC est utilisé : 
a) ( {cinema, adresse, tel}, {DF1}), ({titre, realisateur, heure, prix, cinema}, {DF2, DF3}); 
b) la deuxième relation obtenue est à nouveau décomposée : 
({cinema, titre, heure, prix}, {DF2}), ({titre, realisateur, heure, cinema}, DF3); 
c) la décomposition est poursuivie avec DF3 
  ({titre, realisateur}, {DF3}), ({titre, heure, cinema }, {Ø}); 
d) la relation {titre, heure, cinema } est supprimée parce qu’elle est une projection de la relation 
déjà obtenue à l’étape b; 
e) Schéma de la base est alors le suivant : 
({cinema, adresse, tel}, {DF1}),  
({cinema, titre, heure, prix}, {DF2}),  
({titre, realisateur}, {DF3}). 
Le schéma final est donc CDF et CDD et est composé que de relations FNBC. 
 
Cas 2 : décomposition avec une DMV  
Supposons  qu’un  nouvel  attribut,  acteur,  soit  ajouté  au  schéma  initial  ci‐dessus.  De  plus,  la 
DMV  suivante:  titre  ‐‐>>  acteur  est  présumée  présente  dans  le  schéma  (comme  hypothèse  qui 
restera cependant à vérifier). 
La décomposition par l’algorithme précédent fournira un schéma en FNBC, mais qui regroupe 
aussi des attributs qui ne vont pas ensemble sur le plan de la sémantique (soit l’attribut acteur).   
 
({cinema, adresse, tel}, {DF1}),    ({cinema, titre, heure, prix}, {DF2}),  
({titre, realisateur}, {DF3}),    ({cinema, titre, heure, acteur}, {DMV}) 
                                       
La  dernière  relation  ne  peut  pas  être  supprimée  en  raison  de  l’attribut  acteur.  Cette  dernière 
relation  n’est  pas  naturelle  parce  que  l’heure  du  cinéma  serait  associée  (dépendant  des)  aux 
acteurs !  Or,  la  décomposition  peut  être  poursuivie  en  prenant  en  compte  la  DMV.  Le  schéma 
obtenu est CDD. La forme normale  FN4 a été formulée pour traiter ce type de problème. 
({cinema, adresse, tel}, {DF1}),  
({cinema, titre, heure, prix}, {DF2}), 
({titre, realisateur}, {DF3}), 
 (titre, acteur}. {DMV}},  <‐‐‐‐   
({cinema, titre, heure}, {DF2})  ‐‐> est une relation redondante 
 
La  dernière  relation  est  supprimée  parce  qu’elle  est  une  projection  d’une  autre  relation  du 
schéma (voir l’algorithme de transformation en FNBC d’un schéma de BD) Le schéma final est 
donc :   
  ({cinema, adresse, tel}, {DF1}),   ({titre, realisateur}, {DF3}),   
({cinema, titre, heure, prix}, {DF2}),     (titre, acteur}. {DMV sans contexte}} 
 
Le schéma obtenu est de type CDD et CDF. La DMV a donc permis de décomposer un schéma 
de  BD  pour  obtenir  des  relations  qui  regroupent  correctement  les  attributs  en  accord  avec  la 
sémantique  des  DF  et  les  DMV.  Doit‐on  conclure  que  la  présence  d’un  attribut  associé  à 

 
Chapitre 10 Théorie de la normalisation   99

plusieurs  valeurs  et  que  ce  dernier  doit  être  isolé  dans  une  relation ?  Non,  car  il  y  a 
multiplication inutile des relations, notamment lorsque la DMV hors contexte n’en est plus une 
lorsqu’elle est intégrée avec d’autres attributs d’une même relation.   
Décomposition de Fagin 
La présence d’une DMV vérifiée dans une relation R sous‐tend des anomalies. La relation R(A, 
B,  C)  est  donc  décomposée  dans  le  but  de  corriger  certains  de  ces  problèmes :  si  A‐>> B  est 
validée dans R, elle doit induire A‐>> C dans la même relation. La relation peut être alors divisée 
en deux nouvelles relations dont la jointure est équivalente à la relation de départ.   
                        
R(A, B, C) = R1(A, B) |x| R2(A, C)  et DMV : A‐>>C 
 
Si R1 ∩R2 ‐‐>> R1 ou R1 ∩ R2 ‐‐>> R2, alors la décomposition est sans perte d’information (CDD) 
et la jointure des deux  nouveaux fragments de relation donne la même extension initiale, sans 
générer de fausses informations (théorème de Fagin).   
Exemple 1 :  
employe :  nom*  diplome*  enfant* 
  dion  dec  jean 
  blais  dec  lise 
  blais  bac  lise 
  dion  bac  jean 
  blais  dec  pierre 
  blais  bac  pierre 
             
nom‐>> enfant ?  nom ‐>> diplome ? 
enfant  blais, dec = {lise, pierre}  
 
Pour  toute  paire  de  tuples  partageant  la  même  valeur  de  nom,  l’ensemble  des  enfants  est  le 
même quel que soit le contexte. 
    Enfant blais, bac = {lise, pierre}   Enfant blais, dec  = {lise, pierre} 
  Enfant dion, bac = {jean}    Enfant dion, dec = {jean} 
 
L’ensemble  de  Enfant  ne  varie  pas  lorsque  le  diplôme  (contexte)  varie  pour  un  même  nom 
dʹemployé.  Cette  propriété  est  vérifiée  pour  tous  les  employés.  Il  y  a  donc  indépendance  des 
attributs diplôme et enfant traduisant lʹabsence de lien sémantique entre le diplôme et l’enfant. 
Le schéma peut donc est divisé.                                                   
DMV triviale 
Dans R(X, Y, Z)  si X‐>> Y est une DMV validée, elle est dite triviale si et seulement si Y⊆ X ou 
X ∪ Y = R ou si Z = ø, c’est‐à‐dire lorsqu’il n’y a pas de contexte à la DMV,  X ‐>> Y. En d’autres 
mots,  puisque  Y⊆  X,  c’est‐à‐dire  X  −>Y,  toute  DMV  qui  découle  de  l’existence  d’une  DF 
équivalente est triviale.         
                       

 
Chapitre 10 Théorie de la normalisation   100

r :  A  B  C 
  1  1  1 
  1  2  2 
  1  2  1 
  2  2  1 
  1  1  2 
  2  1  2 
                                            
Est‐ce que A‐>> B  est  validée dans R(A, B, C) ? 
Ba, c  donne les valeurs de b dans R: 
Ba, c =   B1, 1 = {1, 2}  B1, 2 = {2, 1} 
    B2, 2 = {2}  B2, 1 = { 2} 
La DMV, A‐>> B est validée dans la relation R et elle nʹest pas triviale. 
Exemple : R : (A, B), est‐ce que A ‐‐>> B est validée dans l’extension R ci‐dessus? 
 
    1  1  (1, 2) ∈ r       
    1  2  (1, 1) ∈ r       
    2  2  (2, 1) ∈ r       
    2  1  (2,2)  ∈ r       
 
La DMV est validée, mais triviale, car A∪B = R, le schéma de la relation. Une DMV sans contexte 
est  dite  triviale  et  a  aussi  été  appelée  une  dépendance  multiple  dans  certaines  méthodologies 
(voir  méthodologie  EPAS18).  Ces  dépendances  multiples  ne  génèrent  pas  de  problèmes  et  sont 
les seules dépendances multivaluées tolérables dans une relation dont le contexte se limite aux 
attributs de la DMV triviale. Pour éviter les anomalies qui découlent de la présence de DMV non 
triviales,  il  suffit  donc  d’isoler  chaque  DMV  non  triviale  validée  qui  implique  des  attributs 
indépendants dans une nouvelle relation (sans contexte).  Ainsi la DMV est transformée en une 
dépendance multiple (ou DMV triviale).  
Propriétés des DMV 
Soit  R(X,  Y,  Z),  une  relation  dont  le  schéma  est  partitionné  de  sorte  que  X∪Y∪Ζ = R  avec  ((X 
∩ Y)  ∩Ζ) = ø.  Les  propriétés  des  DMV  sont  utiles  pour  déduire  d’autres  DMV  et  ainsi 
poursuivre la normalisation du schéma relationnel. Cela permet la transformation des relations 
autrement impossible sur la seule base des DF.                                       
Réplication ( ou généralisation de la DF) 
La DF est un cas particulier de la DMV. Si X−> Y est validée dans R, alors X‐>> Y l’est aussi, mais 
non l’inverse. Donc, X−>Y |= X ‐>> Y dans R. La démonstration de cette propriété découle de la 
définition même de la DMV. 
Réflexivité 
Si Y⊆ X alors X‐>> Y validée dans R et elle est dite triviale parce que Y ∪ X = R. 
Démonstration  Si  Y  ⊆  X alors  la  DF  X  −−> Y  est  valide  par  réflexivité.  Par  généralisation  de  la 
DF, on obtient la  DMV X ‐>> Y dans R.   

 
Chapitre 10 Théorie de la normalisation   101

Complémentarité 
Soit X ∪ Y ∪ Z = R est une partition du schéma de R avec Y∩Z = ø. Si X ‐>> Y est validée dans R, 
alors X ‐>> R ‐ X ‐ Y, c’est‐à‐dire X ‐>> Z dans R est aussi validée.  
Transitivité 
Si X ‐>> Y et Y ‐>> Z sont validées dans R, alors X ‐>> Z ‐ Y est aussi validée dans R. Si Y∩Z = ø 
alors 
 X ‐>> Z.                                      
Augmentation  
Soit R(X, Y, W, V), si  X ‐>> Y est valide dans R et W⊆ V (donc V‐>> W), alors X, V ‐>> Y, W est 
aussi valide dans R. 
Cas particulier : augmentation du déterminant 
Si X‐>> Y et Y⊆Y (donc Y‐>> Y), alors X, Y‐>> Y 
Union (augmentation du déterminé) 
Si X‐>> Y et X‐>> Z alors X‐>> Y‐Z est valide aussi dans R. 
Cas particulier : si W−> W et X −> Y, alors X, W ‐‐>> Y, W
Pseudo transitivité 
Si X‐>> Y et W, Y‐>> Z sont valides dans R, alors X, W ‐>> Z ‐ Y. 
Décomposition ou projectabilité: intersection, différence et union 
Si X ‐>> Y et X ‐>> Z alors X ‐>> Y∩Z et X ‐>> Z – Y et X ‐>> Y – Z et X ‐>> Y∪Z 
Coalescence : générateur dʹune DF à partir dʹune DMV 
Si  X  ‐>> Y  est  valide  dans  R,  alors  X−> Y’  est  aussi  valide  dans  R  si  et  seulement  si  ∃ Z⊆ R tel 
que Z−> Y’ et que Z∩Y = ø. 
Exemple :  
Dans R(A, B, C, D) avec A‐>> B, C et que D ‐> B, alors A ‐> B par coalescence. 

Fermeture par les DMV (D+) 

Si  D  est  l’ensemble  des  DMV  pour  la  relation  R,  la  fermeture  D+  est  l’ensemble  des  DMV 
obtenues par l’application récursive des propriétés ci‐dessus. Les règles d’inférence essentielles 
et suffisantes sont la complémentarité, la transitivité, l’augmentation et la coalescence. Ces règles 
sont  complètes  et  valides,  c’est‐à‐dire  que  leur  application  récursive  génère  toutes  les 
dépendances possibles de D+. 
Exemple :    R(A, B, C, G, H, I)  D = {A ‐>> B; B ‐>> H, I; C, G ‐>> H} 
Quelques DMV de D+ 
a) D |– A‐>> C, G, H, I;  
A ‐>>  B par hypothèse,  A‐>> C, G, H, I par complémentarité; 
 
b) D |–  A‐>> H, I; A ‐‐>> B par hypothèse, B ‐>> H, I par hypothèse, 
A‐‐>> {H, I ‐ B} = H, I par transitivité; 
 
c) D |–  A‐>> C, G; A‐‐>> C, G, H, I par hypothèse, 
A ‐>> H, I du résultat antérieur, A ‐>> {C, G, H, I} ‐ {H, I} = C, G par différence. 
Exemple :   

 
Chapitre 10 Théorie de la normalisation   102

 Le schéma de la  base R :        R (employe, profession, langueP) 
                           
r :  employe*  profession  langueParlee 
  Tremblay  traducteur  français 
  Tremblay  traducteur  allemand 
  Tremblay  traducteur  grec 
 
Est‐ce que la DMV employe ‐>> profession est valide dans R ? 
 
Si Tremblay devient éditeur, faut‐il ajouter un ou trois tuples dans la relation R ?    
 
Tremblay  éditeur  français 
   
ou 
Tremblay  editeur  français 
Tremblay  editeur  allemand 
Tremblay  editeur  grec 
 
S’il  faut  en  ajouter  3  tuples  dans  la  table,  une  DMV  est  présente  dans  la  relation  et  il  y  a 
indépendance entre les attributs profession et langue‐p. La relation peut alors être décomposée 
ainsi : 
R (employe, profession) et R1 (employe, langueParlee) 
 
 Dans le cas contraire, la DMV n’est pas valide dans R, alors les attributs sont dépendants ce qui 
nécessite l’ajout d’un seul tuple sans la nécessité de diviser la relation.
Forme normale FN4 
Une relation R (A1, A2, ... Ai) est en  FN4 par rapport à un ensemble D formé avec les DF et les 
DMV, si pour chaque DMV ∈ D+   valide dans une relation R   telle que X‐>> Y avec X ⊆ R et Y ⊆ 
R, au moins une des conditions suivantes est vérifiée : 
                                     
a) X‐>> Y est une DMV triviale  dans R  
ou 
b) X est une superclé dans R, c’est‐à‐dire que X −> A1, A2, ... Ai      (DF) 
 
Donc si chaque déterminant des dépendances de D est une superclé, la relation R est en FN4, car 
les DF implicites à la clé correspondent aussi à une DMV. La fermeture de D est alors obtenue 
par les propriétés des DMV. 
 
Exemple :  
Est‐ce que la relation R (A, B, C, D, E) avec l’ensemble D ci‐dessous est en FN4 ?  
  D = {A−>B, C; C ‐>> D, E} et la clé est {A, D, E} 
Puisque C‐>>D, E alors C‐>>A, B. En outre, puisque A‐>B alors A‐>>B. 

 
Chapitre 10 Théorie de la normalisation   103

 
Elle n’est pas en FN4, car la DMV C‐>>D, E n’est pas triviale dans R et C n’est pas une superclé. 
Après  décomposition  de  R  sur  la  base  du  théorème  de  Fagin,  les  fragments  suivants  sont 
obtenus :  
   R1(C*, D*, E*) et R2 (A*, B, C) qui sont en FN4. 
La décomposition est CDD et CDMV (conservation des dépendances multivaluées) 
Algorithme de décomposition de R pour avoir la forme FN4 
Soit R = {R1, R2...} et D l’ensemble des DF et DMV de R et F l’ensemble des DF seulement; 
RESULTAT := R; 
FAIT := FAUX; 
tel que (¬FAIT) faire 
Si (∃Ri dans RESULTAT, la relation n’est pas FN4) 
alors choisissez X ‐>> Y (non triviale) dans Ri tel que X‐> Y ∉ F+ et X∩Y = ø 
RESULTAT :=(RESULTAT ‐ Ri)∪(Ri ‐ Y)∪ (X,Y); 
sinon FAIT :=VRAI 
                             
Importance du contexte avec les dépendances du DMV 
Exemple : 
ResHum (employe, enfant, salaire, annee) 
  F = ø  
  D = {employe ‐>>enfant;  employe ‐>> salaire, annee} 
 
D regroupe les DMV valides de la relation Res‐Hum. 
Est‐ce que la relation Res‐Hum est en FN4 ? Quelles sont les DF de cette relation ? 
employe ‐> enfant ?  employe −> salaire ?   employe −> annee ?   
 
Est‐ce que par coalescence certaines nouvelles DMV peuvent être dérivées ? 
Puisque les DF n’existent pas dans le schéma de la relation Res‐Hum (F est vide)  alors celle‐ci 
n’est pas en FN4, car les DMV valides de D présentes dans le schéma ne sont pas triviales. 
                         
Décomposition de la relation Res‐Hum : 
  Fam (employe, enfant)  HistSal (employe, salaire, annee) 
et dans ces deux nouvelles relations, les DMV sont triviales et donc en FN4. 
 
Autre exemple    
InscEtud (matric*, cours*, sports*)  
 
Les DMV sont les suivantes : D = {matric ‐‐>> cours ;  matric ‐‐>> sports}. Les DMV valides sont 
non‐triviales. 
Alors pour être en FN4, il faudrait que : 
matric ‐‐>matric;  matric ‐‐> cours; et que matric ‐‐> sports  
 

 
Chapitre 10 Théorie de la normalisation   104

Or, comme cela n’est pas le cas, la décomposition s’impose : 
ChoixCours (matric*, cours*) 
ChoixSports (matric*, sports*)                
Fermeture dʹun ensemble dʹattributs avec les DMV (D+) 
Pour  effectuer  la  fermeture  d’un  ensemble  ou  d’un  seul  attribut  X,  il  suffit  de  réunir  les 
ensembles  d’attributs  déterminés  X  dans  toutes  les  DMV  dérivables  par  les  règles  d’inférence. 
Ces  ensembles  sont  appelés  une  base  de  dépendances  de  X  laquelle  est  notée  G(X)  et 
correspondent à la fermeture de l’attribut X par rapport à D. 
Base de dépendance de X par rapport à D 
Soit D un ensemble de DF et de DMV et U l’ensemble des attributs qui apparaissent parmi les 
déterminés  ou  les  déterminants  de  ces  dépendances.  La  base  de  dépendance  de  X,  G(X)  est 
constituée  des  sous‐ensembles  d’attributs  de  {U  ‐  X  }  tel  que  chacun  soit  déterminé  par  le 
déterminant X dʹune DMV de D+.               
En d’autres mots, la base de dépendances de X est la partition la plus fine des attributs de  U : 
G(X) = {P1, P2, P3, ... Pk  | 1 <= k <= n}. Donc, X ‐‐>> Pj   ∈ D+   ou X‐‐>> Y ∈ D+   où Y est obtenu 
par la réunion de quelques partitions Pj ∈ G(X). 

Dépendance DMV élémentaire 
Une  DMV  X  ‐>>Y  est  dite  élémentaire  si  Y  est  un  élément  de  la  partition  de  la  base  de 
dépendances. Dans les autres cas, elle est dite DMV.                                                    
Calcul de G(X) 
Le  calcul  de  la  base  de  dépendances  de  X  n’est  pas  linéaire,  mais  plutôt  polynomial  et 
proportionnel à (n3 x m) où n est le nombre d’attributs dans U et m le nombre de dépendances 
dans D.  
Algorithme : 
a) À partir de l’ensemble des FD et DMV, convertissez les DF explicites en DMV par la propriété 
de  généralisation.  Soit  FD :  X  ‐>Y,    la  DF  est  alors  convertie  en  X‐>>Y.  L’ensemble  D  contient 
alors que des DMV explicites;  
b) Initialisez au départ l’ensemble S comme nul. S = φ; 
c) Pour chaque DMV, Y ‐>> Z dans D si Y ⊆ X, ajoutez {Z‐X} et {U‐Z‐X} dans l’ensemble S, car 
1. Y ‐>> Z et X ‐>> Y (de Y ⊆ X) et par transitivité, on a que X ‐>> Z‐X; 
2. par complémentation, X ‐>> U ‐ Z ‐ X; 
3. S = {(Z ‐ X), (U ‐ Z ‐ X)}; 
d)  Appliquez  les  règles  de  projectabilité  (addition),  tant  que  cela  est  possible,  à  toute  paire  de 
partitions de  S non disjointes . 
Si Y et Z sont des déterminés de A dans S (i.e.  X‐>>Y et X‐>> Z ) et X ∩ Ζ ≠ φ, alors (Y ∩ Z), (Y ‐ 
Z) et (Z‐ Y) dont les déterminés de X s’ajoutent à S et remplacent Y et Z, initialement dans S;    
e) Pour chaque DMV,  W ‐‐>> Z de D, recherchez Y dans l’ensemble S tel que W ∩ Y = φ, mais 
avec Z ∩ Y ≠ φ et Y ‐ Z ≠ φ. Pour cette DMV remplacez Y par Y ‐ Z et par Y ∩ Z; 
f) À la fin de l’algorithme, S∪X contient la base de dépendances pour X, soit G(X).  
 
Exemple :    R (A, B, C, D, E) avec D = {A −> B, C et D, E ‐>>C} 

 
Chapitre 10 Théorie de la normalisation   105

Calculez la G(A), c’est‐à‐dire les DMV de D+ ayant A dans la partie gauche (fermeture de A par 
rapport à D). 
1. Conversion en DMV pour obtenir D = {A ‐>> B, C; D, E ‐>>C}; 
2. Au début, S = φ; 
3. De A ‐>>B, C alors ajout de D, E dans S et B et C pour avoir S = {B, C; D, E}; 
4. ne s’applique pas; 
5. Avec A ‐>> B, C pas de modification à S avec D, E ‐>>C de D, le {B, C} de S est remplacé par 
{C} et {D, E} pour avoir alors S ={B; C; D, E}; 
6. Finalement, S ∪ A = {A; B; C; D, E}, c’est‐à‐dire que la fermeture de A: A‐>>A; A‐>> B; A‐>  C; 
A‐>> D, E;  
 
Théorème dʹappartenance dʹune DMV à D+ 
Une DMV, X ‐>>Z est dans la fermeture D+ si et seulement si le déterminé Z peut être formé par 
l’union de quelques éléments de la base de dépendances G(X)19. 
Exemple :   A ‐>> A, D, E  est valide si A ‐> {A} ∪ {D, E} ∈ G(A). 
10.8.1 Cohérence dʹune base de données 
Avant d’étudier une autre dépendance plus spécialisée, soulignons que les dépendances de type 
FD  et  DMV  sont  utiles  pour  la  conception  du  schéma  d’une  BD  parce  qu’elles  diminuent  la 
redondance et les anomalies tout en assurant une cohérence de la BD. Lorsque le schéma est déjà 
connu, la question est de savoir s’il est cohérent.  
 
Exemple :  Soit la BD formée des relations suivantes : R1(B, E), R2(E, CL), R3 (CL, C) et R4 (C, B) 
 
où  R1 (banque, emprunt)  R3 (client, compte) 
  R2 (emprunt, client)    R4 (compte, banque) 
 
R1 :  B  E    R2 :  E :  CL    R3 :  CL  C    R4 :  C  B 
  b0  e0      e0  cl0      cl0  c0      c0  b0 
  b1  e1      e0  cl1      cl1  c0      c0  b1 
          e1  cl0                 
 
Pour répondre à la question, quels sont les clients de chaque banque (CL, B), un utilisateur peut 
formuler  son  traitement  de  différentes  façons.  Est‐ce  que  ces  requêtes  sont  équivalentes  et 
correctes ?  Par  exemple,  voici  trois  requêtes  possibles dont  le  schéma  résultant  contient  les 
attributs de la réponse. Ces trois requêtes prétendent donc répondre à la question (Rep) :    
 
R1 |X| R2 = Rep(B, CL) ;  b) R3 |X| R4 = Rep(B, CL);  c) R2 |X| R3 |X| R4 = Rep(B, CL) 
 
par a) :   R1 |X| R2  B  E  CL 
    b0  e0  cl0 
    b0  e0  cl1 
    b1  e1  cl0 

 
Chapitre 10 Théorie de la normalisation   106

                        
par b) :  R3 |X| R4  CL  C  B 
    cl0  c0  b0 
    cl0  c0  b1 
    cl1  c0  b0 
    cl1  c0  b1 
                                                                                            
par c) :  R2 |X| R3 |X| R4  E  CL  C  B 
    e0  cl0  c0  b0 
    e0  cl1  c0  b0 
    e1  cl0  c0  b0 
    e0  cl0  c0  b1 
    e0  cl1  c0  b1 
    e1  cl0  c0  b1 
                   
Pour calculer la réponse, il faut faire la projection sur B et CL.  
 
de a)  B  CL    de b)  CL  B 
  b0  cl0      cl0  b0 
  b0  cl1      cl0  b1 
  b1  cl0      cl1  b0 
          cl1  b1 
                                     
de c)  CL  B 
  cl0  b0 
  cl1  b0 
  cl0  b1 
  cl1  b1 
 
Il y a donc  une différence entre ces résultats qui ont pourtant le même schéma de réponse. La 
réponse  est  incohérente  en  raison  d’une  déficience  du  schéma  initial  de  la  BD.  Sous  certaines 
conditions, le schéma d’une BD ne fournira que des résultats corrects et cohérents. 
Jointure complète dʹordre n 
Une jointure (R1 |X| R2 |X|... Rn) est dite complète si les n Ri se retrouvent dans la projection 
du résultat de la jointure de toutes ces relations.  
Cohérence dʹune base de données  
Une basee de données est cohérente si la jointure de toutes les relations de son schéma de BD est 
complète et si le schéma de BD est acyclique.   

 
Chapitre 10 Théorie de la normalisation   107

Cohérence par 2 
Une basse de données est cohérente par 2 si pour toute paire de relations du schéma de la BD, Ri et 
Rj, leur projection respective sur leurs attributs communs est égale. En d’autres mots, la jointure 
de ces paires quelconques de relations doit être complète : Π Ri ∩ Rj (Ri) = ΠRi ∩ Rj (Rj) . 
 
Si toutes les jointures sont complètes par 2, il y a aussi une  jointure complète dʹordre n.  
Exemple : Est‐ce que le schéma R1(A, B, C), R2 (B, C, D) et R3 (A, D) de la base de données ci‐
dessous est cohérente par 2 ?                         
                               
R1:  A  B  C    R2:  B  C  D    R3:  A  D 
  0  0  1      0  1  1      0  1 
  1  0  1      3  4  5      1  1 
  2  3  4                2  5 

ΠC (R1)  = ΠC  (R2)       (R2)  = ΠD  (R3)   

ΠA (R1)  = ΠA  (R3)  
Donc,  la jointure est complète et que la base de données est cohérente par 2. 
Schéma cyclique 
La présence d’un cycle dans certains schémas empêche la cohérence par 2 et n’implique pas la 
cohérence de la BD.  
 
Exemple :  R1(A, B, C)   R2(B, C, D)  R3(A, D) avec F = { B‐> C; A ‐> B; B, C ‐> D; A ‐> D} 
les DF établies entre les relations forment un cycle qui introduit une incohérence dans la base.   
 
Le schéma peut être transformé et rendu acyclique par divers moyens : 
a) Fusion de schémas relationnels; 
b) Ajout de nouveaux attributs aux relations; 
c) Supprimer certains attributs; 
d) Ajouter de nouveaux schémas de relation. 
Test de la présence dʹun cycle dans le schéma dʹune base de données 
Algorithme : 
a) Création d’un tableau avec les schémas de la BD : 
1‐ chaque ligne représente un schéma de relation; 
2‐ chaque colonne représente un attribut; 
b) Appliquez de façon récursive les opérations ci‐dessous tant que cela modifie le tableau : 
1. opération colonne : toute colonne avec un seul attribut est effacée, 
2. opération ligne : effacement de toute ligne qui est un sous-ensemble
d’une autre ligne présente dans le tableau;
 

 
Chapitre 10 Théorie de la normalisation   108

c)  S’il  ne  reste  plus  aucune  entrée,  le  schéma  de  base,  excluant  les  sous‐schémas,  est  dit 
acyclique, sinon il y a un cycle lorsquʹil reste des entrées. Pour être complet, il faut aussi tester 
tous les sous‐schémas de la BD en les incluant dans le tableau. 
 
Exemple :  
Le schéma d’une base est le suivant : R1(A, B, C) R2(B, C, D) et R3(C, D, E).  
Le tableau initial est donné ci‐dessous.    
                                  
A  B  C     
  B  C  D   
    C  D  E 
 
Effacement des colonnes seules : 
  B  C     
  B  C  D   
    C  D   
 
Effacement des lignes qui ont un sur‐ensemble :   
         
  B  C  D   
         
 
Effacement des attributs seuls dans une colonne :    
 
         
  (vide)       
         
 
Le tableau est vide, le schéma est acyclique et si la cohérence par 2 est démontrée, alors il existe 
un schéma de base de données correspondant qui est cohérent. 
Exemple :  
Est‐ce que le schéma suivant est acyclique ?    R1(A, B, C) R2(B, C, D) R3(A, D) 
10.9 Dépendance complexe dans une relation : introduction à la dépendance mutuelle 
Certaines relations ont des contraintes particulières qui ne sont pas formalisables par les DF et 
les  DMV.  Ces  contraintes  le  seraient  cependant  par  une  formulation  logique  plus  ou  moins 
complexe  lesquelles  pourraient  être  implantées  notamment  par  des  déclencheurs.  Certaines  de 
ces contraintes sur les données revêtent une importance particulière puisqu’elles permettent de 
décomposer  davantage  une  relation  qui  ne  peut  l’être  sur  la  base  des  dépendances 
fonctionnelles ou multivaluées.  En poussant plus loin la décomposition, cela permet de corriger 
certaines  anomalies  plutôt  rares,  mais  bien  réelles  dans  une  telle  relation.  Ces  contraintes 
spéciales sont souvent difficiles à découvrir dans une base de données. La dépendance mutuelle 
(DMUT)  est  une  de  ces  dépendances  qui  traduit  une  contrainte  complexe  permettant  la 

 
Chapitre 10 Théorie de la normalisation   109

décomposition  dʹune  relation.  La  dépendance  mutuelle  permet  de  décomposer    une  relation 
autrement indécomposable sur la base des DF et des DMV.    
 
Voici un exemple proposé par Nicolas18 :     Represente (rep*, prod*, soc) 
                           
    rep* pro* soc    
  r1  p1  s1    s1= { p1, p2 } 
  r1  p2  s1     
  r2  p4   s3    s3 = {p4} 
suppression Æ  r1  p3   s2  Å  s2 = {p3 } 
  r2  p1   s1     
  r1  p4  s5    s5 = { p4} 
 
Indécomposabilité et anomalies de la table Represente 
Dans cette relation, il y a qu’une seule dépendance fonctionnelle, soit DF :  rep, pro −> soc  qui 
est celle sous‐jacente à la clé primaire. La relation est donc en FN4, puisque la DMV dérivée est 
triviale découlant de la dépendance fonctionnelle. En supprimant le tuple (r1, p3, s2), r1 vend p3 
de  la  société  s2,  alors  on  perd  lʹinformation  que  s2  fabrique  le  produit  p3.  Un  produit  p5 
fabriqué  par  la  société  s5  ne  peut  pas  être  inscrit  dans  la  base  sans  avoir  un  représentant.  La 
relation en FN4 a encore une anomalie de suppression qui persiste en dépit de la normalisation 
de  son  schéma.  Comment    supprimer  ces  anomalies  résiduelles ?  Il  faut  certes  la  décomposer, 
mais comment faut‐il le faire ? 
Sémantique du tuple dans Represente 
Le tuple t = (r, p, s) s’interprète comme un représentant r qui vend le produit p fabriqué par la 
société s. Il y a dans cette relation une seule DF qui justifie la clé primaire : 
rep, prod ‐‐> soc   
 
Cette dépendance fonctionnelle s’interprète ainsi : 
Un  représentant  qui  vend  un  produit    ne  le  fait  que  pour  une  société  même  s’il  vend  d’autres 
produits fabriqués par  dʹautres sociétés. Il ne se permet aucune concurrence inter‐société pour 
les produits qu’il vend. 
 
Ainsi,  si  les  tuples  (r1,  p1,  s1)  et  (r1,  p2,  s2)  sont  valides  et  présents  dans  l’extension  de 
Represente,  alors  les  tuples  (r1,  p1,  s2)  et  (r1,  p2,  s3)  ne  le  sont  pas  en  présence  des  deux 
premiers. En effet, ils violeraient la DF définie par la clé primaire. Donc le représentant r1 vend 
le produit p1 fabriqué par une seule société, ne pouvant vendre p1 qui est aussi fabriqué par s2. 
Il doit choisir un fabriquant pour le produit p1. Remarquez que la nature de l’exclusion dépend 
du temps puisque si le tuple (r1, p2, s2) est inscrit en premier dans la base, il sera accepté, mais 
le  tuple  (r1,  p1,  s1)  ne  le  sera  pas.  Cette  table  pourra  être  de  nouveau  décomposée  en  faisant 
intervenir une nouvelle dépendance dite mutuelle et notée DMUT.  
Définition de la dépendance mutuelle (DMUT <~>) 

 
Chapitre 10 Théorie de la normalisation   110

Considérons l’exemple du fournisseur de matériaux à des chantiers. Cet approvisionnement est 
représenté par la relation FMC :  
                                    
FMC :  fournisseur  materiau  chantier   
  (1)  BDS  tige   Square‐Les‐Arts  1 
  BDS  poutre  Centre‐de‐Congrès  2 
  CPR  tige  Centre‐de‐Congrès  3 
  BDS  tige  Centre‐de‐Congrès  4 
    
Si BDS fournit des tiges,  (voir tuple 1) 
 
et des tiges sont utilisées par le Centre‐de‐Congrès (voir tuple 3) 
 
et  le Centre‐de‐Congrès est approvionné par BDS (voir tuple 2) 
 
alors  BDS doit fournir des tiges au Centre‐de‐Congrès (voir tuple no 4) 
 
Cela  se  traduit  par  la  présence  obligatoire  de  ce  tuple  no4  dès  l’instant  que  les  tuples  1,  2  et  3 
sont ajoutés dans la table. Le lien fonctionnel fait entre le fournisseur, le matériau et le chantier 
ne  tient  pas  à  l’implication  logique,  mais  est  imposé  par  une  contrainte  inhérente  à  la  réalité 
modélisée par la table FMC. On dit alors qu’il y a une dépendance mutuelle (DMUT) entre les 
attributs  fournisseur  et  les  attributs  matériau  et  chantier.  Cette  dépendance  mutuelle  est  notée 
fournisseur  <~>  materiau|chantier.  Elle  doit  être  vérifiée  pour  chaque  tuple  de  l’extension  de 
FMC.  
Est‐ce  que  la  table  FMC  contenant  un  seul  tuple  valide  la  DMUT  fournisseur  <~>  materiau, 
chantier ? 
 
FMC :  fournisseur  materiau  chantier   
  BDS  tige   Square‐Les‐Arts  1 
 
Vérifions la DMUT dans la table FMC contenant un seul tuple : 
Si BDS fournit des tiges et des tiges sont utilisées par le chantier Square‐Les‐Arts et le Square‐
Les‐Arts est approvisionné par BDS, 
alors BDS fournit des tiges au chantier Square‐Les‐Arts.  
 
Cette  conclusion  est  surprenante,  car  BDS  pourrait  fournir  le  chantier  Square‐Les‐Arts  avec 
autres choses que des tiges. Or, la conclusion de l’implication correspond au tuple présent dans 
la table et la dépendance mutuelle (DMUT ) fournisseur <~> materiau | chantier est valide.  
 
Voici une deuxième extension, obtenue après  l’ajout d’un tuple :                                
FMC :  fournisseur  materiau  chantier   
  BDS  tige   Square‐Les‐Arts  1 
  BDS  poutre  Centre‐de‐Congrès  2 

 
Chapitre 10 Théorie de la normalisation   111

 
Pour  les  mêmes  raisons  que  précédemment,  la  DMUT  fournisseur  <~>  materiau  |  chantier  est 
toujours  valide.  Si  dans  cette  table  avec  la  DMUT  il  y  a  ajout  du  tuple  (CPR,  tige,  Centre‐de‐
Congrès),  la  DMUT  impose  aussi  l’ajout  d’un  autre  tuple :  (BDS,  tige,  Centre‐de‐Congrès). 
Examinons cet ajout obligatoire de 2 tuples dans la tabale FMC. Supposons que dans FMC il y a 
ajout seulement du tuple (CPR, tige, Centre‐Les‐Arts).  
               
FMC :  fournisseur  materiau  chantier   
  BDS  tige   Square‐Les‐Arts  1 
  BDS  poutre  Centre‐de‐Congrès  2 
+  CPR  tige  Centre‐de‐Congrès  3 
 
Est‐ce que cette table valide la DMUT  fournisseur <~> materiau | chantier? 
Si CPR fournit des tiges 
et des tiges sont utilisées par le Square‐Les‐Arts 
et le Square‐Les‐Arts est approvisionné par BDS, 
alors CPR fournit des tiges au chantier Square‐Les‐Arts.  
 
Donc l’extension ci‐dessus ne valide pas la DMUT car ce tuple est absent de l’extension. Pour ce 
faire, il faudrait ajouter deux tuples.  
 
Ajout du tuple no 4 : 
FMC :  fournisseur  materiau  chantier   
  BDS  tige  Square‐Les‐Arts  1 
  BDS  poutre  Centre‐de‐Congrès  2 
+  CPR  tige  Centre‐de‐Congrès  3 
Æ  CPR  tige  Square‐des‐Arts  4  Å‐ 
 
Par contre, après l’ajout du tuples no3, s’il y a aussi l’ajout obligatoire du tuple no 4, la DMUT 
est respectée. La DMUT est donc valide dans l’extension ci‐dessus. 
Définition formelle de la DMUT 
Soit  une  relation  R(X,  Y,  Z)  avec  X  ∩ Y = X  ∩ Z  = Y  ∩ Z = ø.  Il  existe  une  DMUT  dans  R  si 
l’implication logique suivante vérifiée quel que soit le tuple dans R : 
 
Si (x, y, z )∈ r et (x, y’, z’) ∈ r alors  
    si (y, z’)∈ R[Y, Z] alors <x, y, z’> ∈ r 
    et si (y’, z) ∈ R[Y, Z] alors (x, y’, z) ∈ r 
 
Si cette ce prédicat est valide, alors il y a une dépendance mutuelle (DMUT) dans R.  
Revenons à la table Represente  pour illustrer la DMUT. 
 

 
Chapitre 10 Théorie de la normalisation   112

  Represente :  rep*  pro*  soc       


    r1  p1   s1  1     
    r1  p2   s1  2     
 
  r2  p4  s5  3     
 
  r1  p4  s3  4     
 
  r2  p4  s3  5     
 
  r2  p2  s1  6     
 
 
 
Est‐ce que la DMUT rep <~> pro|soc est présente dans cette table ? 
 
no1 :   r1, p1  (du tuple no 1) 
p1, s1  (du tuple no 1 
s1, r1  (du tuple no 2) 
donc (r1, p1, s1 ) doit être dans la table (voir tuple no 1). 
 
no 2 :   (r1, p2) (du tuple no 2) 
(p2, s1) (du tuple no 6) 
(s1, r1) (du tuple no 2)    
donc (r1, p2, s1) doit être dans la table (voir tuple 2).      
no 3 :   (r2, p4) (du tuple no 3) 
(p4, s5) (du tuple no 3) 
(s5, r2) (du tuple no 3) 
 
donc (r2, p4, s5) doit être dans la table (voir tuple no 3). 
En vérifiant chaque tuple, on conclura que l’extension. La relation Represente vérifie la DMUT  
rep <~> pro | soc .  
 
La  présence  de  la  DMUT  rep<~>prod, soc  va  permettre  une  3‐décomposition  de  la  relation 
Represente (rep, prod, soc) pour donner trois fragments R1, R2 et R3. 
R1(rep, prod ) |x| R2(rep, soc) |x| R3(prod, soc)   
                                                                
R :  r1, p1, s1  R1 :  r1, p1  R2:  r1, s1  R3 :  p1, s1 
  r1, p2  s1    r1, p2    r2, s1    p2, s1 
  r2, p4, s3  =  r2, p4    r2, s3    p4, s5 
  r1, p4, s3    r1, p4    r2, s5    p4, s3 
  r2, p2, s1    r2, p2    r1, s3     
  r2, p4, s5             

 
Chapitre 10 Théorie de la normalisation   113

 
Ainsi, R1 |x| R2 donne une table dont le schéma est (rep, prod, soc) et dont les tuples sont filtrés 
par la 3e jointure.  
Théorème de décomposition de Nicolas   
S’il y a une DMUT X<~> Y⏐Ζ dans R(X, Y, Z) alors R(X, Y, Z) = R1(X,Y) |x| R1(X,Z) |x|RF(Y, Z).  
 
En  d’autres  mots,  la  relation  R  peut  être  décomposée  en 3  fragments.  Une  telle  décomposition 
est sans perte d’information (CDD) et donne des fragments de relation présentant un minimum 
de redondance des données. L’ordre de calcul des jointures est transparent et ne donne pas de 
perte  d’information  dans  le  résultat  final,  pour  autant  que  les  trois  jointures  soient  toujours 
effectuées en série.  
Propriétés de la DMUT 
Voici quelques propriétés de la dépendance mutuelle : 
a) Si X <~> Y alors Y <~>X (symétrie de l’opérateur). 
b) X <~> Y|Ζ est triviale dans R(X, Y) lorsque Z = ø, c’est‐à‐dire sans contexte. 
c) Si X ‐>> Y|Ζ alors X <~> Y|Ζ, mais pas l’inverse, car s’il y a aucune DMV, il peut y avoir une 
DMUT.  
La DMUT sous‐tend, par généralisation, la décomposition de R en k‐relations; la dépendance de 
jointure  *(R1,  R2,  RF)  dont  elle  est  un  cas  particulier,  permet  de  restreindre  à  une  3‐
décomposition :   
 R = R1 |x| R2 |x| R3. 
La  DMV  génère  une  2‐décomposition  formée  avec  les  déterminés  des  dépendances 
multivaluées. 
Décomposition sans perte de données 
Une  DMUT  A  <~>  B|C  existe  dans  une  relation  R(A,  B,  C)  si  R  =  R1(A,  B)  |x|  R2(A,C)  |x| 
R3(B,C). Les projections de R sur les attributs des nouvelles relations sont sans perte de données.  
 
Exemple :  
La DMUT A <~> B |C est valide dans r.     
                        
r :  A  B  C 
  1  4  2 
  1  4  8 
  1  5  8 
  2  5  8 
  2  4  8 
                                       
Les trois projections obtenues à partir de lʹextension r donnent par jointure la relation initiale : 
    
r1 :  A  B  r2 :  A  C  r3 :  B  C 
  1  4    1  2    4  2 

 
Chapitre 10 Théorie de la normalisation   114

  1  5    1  8    5  8 
  2  5    2  8    4  8 
  2  4             
 
La 3e relation est une sorte de filtre pour supprimer les tuples de  la jointure ( r1 |x| r2) dont la 
valeur de B et C ne sont pas dans l’extension de r3. Si l’attribut est connu comme un déterminant 
d’une DMUT, alors le nombre et la nature des jointures de la suite sans perte d’information ne 
sont pas quelconque mais prédéterminés : 
  R1 |x| R2 |x| R3 ou R3 |x| R1 |x| R2 
Toute  jointure  avec  une  suite  partielle  de  ces  fragments  de  relation  génère  des  informations 
fausses.  L’intérêt  premier  de  la  DMUT  consiste  à  démontrer  la  possibilité  de  raffiner  encore  la 
décomposition et a pavé la voie à sa généralisation soit la dépendance de jointure (DJ). 
10.10 Dépendance de jointure (DJ) 
La DJ est une autre façon de spécifier le fait qu’une relation qui ne peut pas être décomposée en 
deux  relations sans perte d’information, pourrait cependant l’être en trois ou en k  relations de 
degré  moindre  et  cela  sans  aucune  perte  (CDD).  Cette  décomposition  est  possible  au  moyen 
dʹune contrainte symétrique du genre de la DMUT. 
 
Soit R(A1, A2 ... An) et {R1, R2, ... Rk}, une k‐décomposition de R, telle que R = (R1 |x| R2 |x| ... 
Rk).  Une  DJ  notée  *(R1,  R2  ...  Rk)  est  donc  une  façon  de  définir  parmi  toutes  les  relations 
possibles (obtenues par des projections) l’ensemble des relations « légales » ou valides obtenues 
sans perte d’information par rapport à R. La DJ définit donc une jointure complète.   
                   
Si R = ∪i Ri pour i = 1, k  alors l’extension r(R) vérifie *(R1, R2, ... Rk) ssi R = R1 |x| R2 |x| ... Rk 
Par exemple, pour i = 2 alors *(R1, R2) est une DJ  si R1 ∩ R2 ‐>> R1 ou R1 ∩ R2 ‐‐>> R2 . Cette 
DJ est en fait un cas particulier de DMV.   
Exemple   :  R(X,  Y,  Z)  =  R1(X,  Y)  |x|  R2  (X,  Z),  une  2‐décomposition  de  R.  La  DJ  *(R1,  R2)  est 
valide si et seulement si X ‐‐>> Y|Ζ est dans R. 
DJ sans DMV dans une relation 
Voici un exemple qui illustre ce cas où une DJ ne sous‐tend pas une DMV : R(A, B, C) sans DF et 
DMV non triviales. Est‐ce qu’une DJ est présente dans R ? Par exemple, est‐ce que la DJ *{(A, B), 
(A, C), (B, C)} est validée ? 
 
La  décomposition  est  possible  et  cela  sans  perte  d’information,  en  raison  de  la  présence  d’une 
dépendance de jointure (DJ).  
(relation initiale)   
R (A*,B*, C*)    R1 (A*, B*)  R2 (A*, C*)  R3 (B*, C*) 
a1     b1     c2    a1     b1  a1     c2  b1     c2 
a2     b1     c1    a2     b1  a2     c1  b1     c1 
a1     b2     c2    a1    b2  a1     c1  b2     c2 
a1     b1     c1         

 
Chapitre 10 Théorie de la normalisation   115

 
Cette 3‐décomposition diminue la redondance dans l’extension initiale r. Toutefois, la  jointure 
de R1 et R2 ne donne pas lʹextension initiale, même si le schéma est (A, B, C). Des tuples faux 
apparaissent et qui devront être filtrés par une jointure avec R3.    
        R1 |x| R2 |x|  R3 
a1    b1   c2    a1    b1    c2 
a1    b1    c1    a1    b1    c1 
a2    b1    c1    a2    b1    c1 
a1   b2    c2    a1    b2    c2 
a1    b2    c1     
 
Dans ce cas, k = 3, il y a une DJ qui  justifie la 3‐décomposition. Donc R(A, B, C) = R1(A, B) |x| 
R2(A,C) |x| R3(B,C). 

Dépendance de clé 
Cette dépendance de clé existe lorsque R(A, B, C, D) avec A − > B, C, D et B −> A, C, D (clés), i.e. 
∀ les dépendances ont une clé comme déterminant: DF et  DMUT avec A <~> D | C et DJ avec 
*{AB, AD, BC}. Cette décomposition donne des relations normales non décomposables. C’est en 
quelque sorte la forme canonique de la BD. 
 
Ordre de jointure des relations dʹune DJ 
Dans  une  DJ,  lʹordre  de  jointure  des  fragments  nʹa  pas  dʹimportance,  dans  la  mesure  où  ils 
interviennent tous dans le calcul. 
Suite de jointures no 1 :  
  R[A, B] |x| R[A, D] |x| R[B, C] = R(A, B, C, D) 
    R1(A, B, D) |x|...   avec A ‐> B       
    R2(A, B, C, D)  et F = {A −> D; A‐> B; A −>D; A −> C  
Suite de jointures no 2 : 
  R[AB] |x| R[BC] |x| R[AD] = R(A, B, C, D)   
Généralisation de DMUT 
La DJ est une généralisation des diverses dépendances DMV, DMUT et des autres dépendances 
supérieures. Or, une k‐décomposition est différente selon la valeur de k. 
k = 2 alors dépendance de jointure ==> présence de DMV 
k = 3 alors dépendance de jointure ==> présence de DMUT 
... 
k= k dépendance de jointure ==> présence d’une dépendance d’ordre supérieur. 
Forme normale FN5 
Une relation R est en FN5 ou en FNPJ en regard de D, c’est‐à‐dire de l’ensemble des DF, DMV et 
DJ, si elle n’a pas de DJ ou si, pour toutes les dépendances ∈ D+ de R de la forme *(R1, ... Rk) 
avec R = ∪i Ri avec (i =1, k),  une des conditions  suivantes est vérifiée  : 
                                                         

 
Chapitre 10 Théorie de la normalisation   116

a) *(R1, ...Rk) est une DJ triviale, c’est‐à‐dire quʹil y a un Ri égale à R ou 
b) chaque Ri de la DJ est une superclé de R ou 
c) la relation R est en  FN4 et sʹil n’y a pas de DJ. 
                
Dans le cas affirmatif, la relation initiale R demeure inchangée et elle est en FN5. 
Dépendance de jointure triviale dans R(A1 ... An) 
Une  Dj  *(R1,  ...Rm)  est  dite  triviale  si  elle  est  toujours  valide,  c’est‐à‐dire  quʹau  moins  un  Ri 
correspond au schéma de la relation R. 
Exemple :  
R(A, B, C, D) et *(ABCD, AB, AD, CD) est triviale, car un des composants de la DJ est  elle‐même 
la relation R. 
Test de DJ dans R (A1, A2, ... An) par les tableaux 
La  recherche  des  DJ  est  difficile  et  longue;  celle‐ci  permet  de  faire  éclater  une  relation  en 
fragments sans perte d’information. Par contre, si un éclatement est énoncé, il est plus facile de 
vérifier si ce dernier est légal, c’est‐à‐dire de savoir s’il correspond à ceux dʹune dépendance de 
jointure. Pour ce faire, il existe un algorithme qui applique une technique fondée sur les tableaux 
pour confirmer ou infirmer une DJ présumée.    
Algorithme 
Construction d’un tableau TDJ avec n colonnes, k lignes de bij pour R(A1, ... An). La DJ étant 
*(R1, .., Rk) où chaque Ri correspond à un sous‐ensemble du schéma de R,  Ri ⊆ R et ∪i Ri = R .  
  
TDJ :    A1  A2  A3  ...  An 
R1    b11  b12  b13  ...  b1n 
R2    b21  b22  b23  ...  b2n 
…             
Rk    bk1  bk2  bk3  ...  bkn 
 
2)  Les  variables  marquées  sont  par  bi,j  qui  identifient  initialement  les  cellules.  Elles  sont 
modifiées par la suite de la façon suivante :  
 
a‐ Pour  chaque  ligne Ri, chaque  bij  est remplacé par un marqueur aj  (colonne) si Aj ∈ Ri pour 
chaque j de 1 à n, sinon bij demeure inchangé ; 
 
b‐ Pour chaque dépendance de F associée à R, Y −> A où Y ⊆ {A1, ..., An} et A∈{A1, ..., An}, si 
Ri[Y]  =  Rj[Y]  alors  les  symboles  de  Ri[A]  =  v1  et  de  Rj[A]  =  v2.  Ces  symboles  sont  par  la  suite  
traités de la façon suivante : 
 
• si l’un des deux symboles v1 ou v2 est une variable marquée alors le symbole est remplacé par 
l’autre qui est un marqueur ;  
 ou 

 
Chapitre 10 Théorie de la normalisation   117

• Si v1 et v2 sont deux variables marquées, alors elles sont remplacées par la variable marquée 
ayant l’indice de ligne le plus petit. 
 
3) dès qu’une ligne est formée seulement avec des marqueurs, alors la dépendance de jointure 
DJ est vérifiée.  
La décomposition de R devient donc possible sans perte d’information. 
Exemple :    
R(A, B, C, D, E, F) et F= {A‐> B; F‐> E} et la DJ suivante  à vérifier : 
D : *((A, B, D, E), (A, C, D, F)), (B, C, E, F))     

DJ :  A1  A2  A3  A4  A5  A6 
  A  B  C  D  E  F 
R1 : ABDE  a1  a2  b13  a4  a5  b16 
R2 : ACDF  a1  b22  a3  a4  b25  a6 
R3 : BCEF  b31  a2  a3  b34  a5  a6 
                    
Avec A‐> B 
  A  B  C  D  E  F 
R1  a1  a2  b13  a4  a5  b16 
R2  a1  a2  a3  a4  b25  a6 
R3  b31  a2  a3  b34  a5  a6 
Avec F−> E    
  A  B  C  D  E  F 
R1  a1  a2  b13  a4  a5  b16 
R2  a1  a2  a3  a4  a5  a6 
R3**  b31  a2  a3  b4  a5  a6 
                           
Lʹavant‐dernière ligne est composée que de marqueurs. Cela indique que la DJ (hypothèse) est 
vérifiée.  De  plus,  comme  aucun  fragment  Ri    correspond  à  une  superclé  de  R  (la  clé  étant 
composée  de  tous  les  attributs),  cette  relation  R  nʹest  pas  en  FN5  et  devra  être  décomposée 
conformément à la DJ testée et vérifiée.   
 
Exemple :  
 R(A, B, C, D)  avec F= {A −> B, C, D; B −>A, C, D}. Est‐ce que la DJ *((A, B), (A, D), (B, C)) est 
vérifiée dans R ?    
    A  B  C  D 
R1(A, B)  a1  a2  b13  b14 
R2(A, D)  a1  b22  a23  a4 
R3(B, C)  b11  a2  a3  b34 
A ‐>  B, C, D 
(A, B)    a1  a2  b13  a4 

 
Chapitre 10 Théorie de la normalisation   118

(A, D)    a1  a2  b13  a4 


(B, C)    b11  a2  a3  b34 
B ‐>  A, C, D 
A, B    a1  a2  a3  a4 <‐‐‐ 
A, D    a1  a2  a3  a4 
B, C    a1  a2  a3  a4 

Après  le  traitement  de  toutes  les  dépendances,  la  1re  ligne  satisfait  le  test  et  donc  la  DJ  en 
hypothèse est vérifiée et la décomposition validée. La DJ affirme que lors de manipulations avec 
les relations, par exemple par des projections, toute relation intermédiaire n’est pas valide sans 
risquer d’engendrer une incohérence sémantique lors de jointures impliquant les projections. 
  
En identifiant une DJ dans R, cela permet de morceler la relation R immédiatement en fragments 
et d’autoriser la jointure de certaines projections, malgré le fait  que la condition de Delobel et 
Casey    (Heath)  ne  soit  pas  vérifiée.  On  peut  donc  obtenir  seulement  par  certaines  jointures, 
l’extension  initiale  sans  perte  d’information.  Bien  entendu,  toute  jointure  impliquant  un 
fragment  devra  être  poursuivie  pour  y  inclure  tous  les  fragments  identifiés  dans  la  DJ.  En 
d’autres  mots,  une  DJ  dans  une  relation  indique  que  ce  schéma  de  relation  ne  peut  pas  être 
fragmenté de n’importe quelle façon sans risquer de perdre des informations.  
 
Voici un exemple d’incohérence dans une relation. Soit la relation R(A*, B*, C, D) avec la clé {A, 
B} de ce schéma.   
 
A* B* C D
1  1  1  1 
2  1  2  2 
2  2  3  3 
3  2  3  4 
4  3  4  4 
                            
Cette  relation  est  en  FNBC,  mais  elle  a  des  anomalies  résiduelles  associées  à  la  redondance. 
Supposons  qu’au  cours  de  manipulations  sur  la  BD  il  y  ait  projection  de  R  en  trois  relations 
intermédiaires (éventuellement des vues) : R1(A, B, C), R2(B, D) et R3 (A, D).    
         
R1  A*  B*  C    R2  B*  D*    R3  A*  D* 
  1  1  1      1  1      1  1 
  2  1  2      1  2      2  2 
  2  2  3      2  3      2  3 
  3  2  3      2  4      3  4 
  4  3  4      3  4      4  4 
                                  

 
Chapitre 10 Théorie de la normalisation   119

Qu’arrive‐t‐il  avec  une  jointure  de  R1  avec  R2  faite  à  travers  deux  vues  relationnelles ?  Est‐ce 
que le résultat dont le schéma est identique à celui de la relation originale est correct ? Est‐ce que 
l’extension initiale est retrouvée ? Est‐ce que la jointure est complète ?   
                                    
R12:  A  B  C  D   
  1  1  1  1   
  2  1  2  1  * 
  1  1  1  2  * 
  2  1  2  2   
  2  2  3  3   
  3  2  3  3  * 
  4  3  4  4   
  2  2  3  4  * 
  3  2  3  4   
 
Il  y  a  effectivement  cohérence  par  2  et  la  jointure  est  complète  par  3.  Toutefois,  le  schéma  est 
cyclique  et,  donc  la  base  serait  incohérente  en  lʹabsence  dʹune  dépendance  de  jointure  (DJ).  Le 
résultat  de  R12  a  le  schéma  de  la  relation  initiale  et  pourrait  laisser  croire  à  un  utilisateur  que 
son  extension  représente  toute  l’information  correspondante  dans  la  BD.  Or,  la  jointure  R12 
donne une extension incluant de mauvais tuples (*) et cette fausse information sera produite si 
une troisième jointure n’est pas effectuée afin d’agir comme un filtre. Les tuples marqués par * 
ne sont pas valides dans l’extension initiale et tout affichage de ce résultat pourrait fournir une 
information fausse aux utilisateurs. La raison de cette incohérence repose sur la dépendance de 
jointure (DJ) présente dans R et, qui permet la division de la relation en trois fragments et cela, 
sans  perte  d’information  (CDD).  Ces  trois  relations  sont  alors  essentielles  dans  la  jointure 
complète afin de retrouver l’extension initiale.  
 
Toute  jointure  partielle  fournit  un  bon  schéma  mais  avec  une  extension  erronée.  Toutefois,  la 
même relation peut être décomposée, au regard de la clé, de la façon suivante : *(ABC, ABD), car 
les DF: A, B ‐‐> C; et A, B ‐‐> D sont vérifiées dans R.  Dans ce cas particulier, la DJ correspond à 
une  décomposition  en  deux  relations  sans  perte  d’informations,  puisque  la  condition  de 
décomposition sans perte dʹinformation est vérifiée.  
 
Anomalie résultant de jointures interdites 
Voici  une  relation  Consultation  représentant  les  informations  concernant  les  consultations 
médicales. La base de données comprend trois vues définies et autorisées par le DBA: VR1, VR2 
et VR3. Les applications utilisent les vues sans restriction. Une de ces vues est une projection de 
Consultation nʹinclut pas une clé candidate. Le système ne peut pas contrôler l’usage des vues 
autrement que d’en permettre ou en interdire l’usage par les concepteurs. Une fois que les vues 
sont  autorisées,  le  concepteur  peut  les  exploiter  à  sa  guise  pour  répondre  aux  besoins  de  son 
application et le contrôle du DBA est inefficace à ce stade.    
                      
                                 

 
Chapitre 10 Théorie de la normalisation   120

consultation :  noCons*  pathologie*  traitementPrimaire  resultat 


  C  P  T  O 
  100  bronchite  pénicilline  + + 
  200  bronchite  antibiotique  ‐ ‐ 
  200  ACV  héparine  ‐ 
  300  ACV  héparine  + 
  400  infarctus  héparine  + 
         
 
Les  seules  DF  sont  celles  sous‐tendues  par  la  clé  {noCons,  pathologie}. Pour  répondre  à  des 
besoins particuliers, les trois vues sont créées par des projections et cela seulement pour des fins 
d’affichage. 
 
VR1 :  noCons*  pathologie*  traitementPrimaire 
  C  P  T 
  100  bronchite  pénicilline 
  200  bronchite  antibiotique 
  200  ACV  héparine 
  300  ACV  héparine 
  400  infarctus  héparine 
              
Cette  division  de  la  relation  initiale  Consultation  se  fait  sans  perte  d’information  et  elle 
correspond dans ce cas‐ci, aux fragments obtenus suite à la division de la relation et cela grâce à 
la  dépendance de jointure.  La cohérence par 2 est vérifiée pour cette base de données.   
                                          
VR2 :  pathologie*  resultat* 
  P  O 
  bronchite  + + 
  bronchite  ‐ ‐ 
  ACV  ‐ 
  infarctus  + 
  ACV  + 
                                               
VR3 :  noCons*  resultat* 
  C  O 
  100  + + 
  200  ‐ ‐ 
  300  + 
  400  + 
  200  ‐ 
 
Supposons que les jointures ci‐dessous soient effectuées en cours d’exploitation afin de répondre 
à des questions précises. Pour ce faire une application peut exploiter des vues existantes ou en 

 
Chapitre 10 Théorie de la normalisation   121

définir. Par exemple, la première jointure ne vérifie pas la condition nécessaire pour avoir une 
opération sans perte d’information.     
                         
VR1 |x| VR2  noCons*  pathologie*  traitementPrimaire*  resultat* 
  C  P  T  O 
  100  bronchite  pénicilline  + + 
  200  bronchite  antibiotique  + + 
  100  bronchite  pénicilline  ‐ ‐ 
  200  bronchite  antibiotique  ‐ ‐ 
  200  ACV  héparine  ‐ 
  300  ACV  héparine  ‐ 
  400  infarctus  héparine  + 
  200  ACV  héparine  + 
  300  ACV  héparine  + 
 
En dépit de la cohérence par 2, cette jointure (VR1 |x| VR2) est trompeuse même si le schéma 
correspond  à  celui  de  la  jointure  complète;    elle  donne  une  extension  avec  quelques  résultats 
faux.  Toutefois,  comme  ces  deux  relations  sont  parmi  celles  incluses  dans  une  dépendance  de 
jointure, il suffira de faire une 3e jointure avec VR3 pour retrouver l’extension intégrale. Ainsi, 
avec une sélection des pathologies d’origine bronchique traitée à la pénicilline, le résultat affiché 
apparaît  contradictoire,  tandis  que  la  réalité  représentées  par  les  données  ne  reflète  pas  cette 
situation. 
                                     
SELECT P, O   FROM    VR1, VR2 
WHERE VR1.P= VR2.P and  
P =‘bronchite’ and T = ʹpénicillineʹ 
                                     
Résultat de la requête : 
 
pathologie*  résultat*   
P  O   
bronchite  + +  ** contradiction 
bronchite  ‐ ‐  ** 
 
Résultat surprenant qui est la manifestation dʹun cas anormal dans le design dʹun schéma. Que 
peut‐on conclure d’une telle réponse ?  
 
Imaginez  l’impact  d’un  tel  résultat  dans  un  autre  contexte,  soit  celui  du  contrôle  aérien.    Une 
telle réponse à la requête pour connaître la position de l’un des avions sous son contrôle qui est 
dans sa phase finale d’atterrissage ! Le système pourrait lui signaler que l’avion est en descente 
finale,  mais  tout  en  étant  absent de  lʹécran  radar!  La  contradiction  pourrait  être  lourde  de 
conséquence.  
                          

 
Chapitre 10 Théorie de la normalisation   122

Explication de lʹanomalie 
Ce résultat surprenant découle du fait que la relation Consultation nʹa pas une dépendance de 
jointure non triviale du type  *((C, P, T); (P, O); (C, O)) et que, dans pareil cas, toute jointure de 
ces  projections  ne  fournit  pas    lʹextension  de  la  relation  dʹorigine,  Consultation.  Ainsi,  toute 
exploitation des jointures intermédiaires en mode recherche peut fournir des faussetés!  En effet,  
ces  faits  faux  résultent  de  lʹabsence  de  la  clé  dans  le  schéma  de  chaque  fragment.  Or,  ces 
fragments peuvent correspondre à des vues autorisées à des moments différents par le DBA. En 
joignant  seulement deux schémas, il est possible dʹobtenir celui de la réponse recherchée mais 
son extension est fausse.  
 
Une  solution  théorique  à  cette  anomalie  consiste  donc  à  vérifier  si  les  fragments  qui  résultent 
des projections sont admissibles en vérifiant la présence d’une dépendance de jointure. Ainsi, si 
cette  dépendance  de  jointure  est  vérifiée,  alors  les  vues  relationnelles  obtenues  par 
décomposition  pourront  être  utilisées  dans  un  calcul  que  si  tous  les  fragments  de  la  DJ 
interviennent  dans  celui‐là.  Une  autre  solution  consiste  à  interdire  la  création  des  vues  et  à 
autoriser (pour un SELECT  avec jointure) que celles préalablement définies dans le dictionnaire 
du  système.  Cʹest  une  solution  contraignante,  car  le  DBA  peut  interdire  aux  utilisateurs  la 
création  ou  lʹutilisation  des  vues,  mais  ne  peut  pas  contrôler  la  dépendance  entre  les  vues 
autorisées.    Dans  une  telle  situation,  le  DBA  verra  à  ce  que  la  relation  Consultation  ne  soit 
décomposée et à ne pas autoriser les vues formées par ces  fragments de la DJ.  Le DBA pourrait 
autoriser que les vues dans lesquelles une superclé de la relation R se retrouve dans la définition 
de la vue. Il faut aussi remarquer que le schéma d’origine comporte une clé complexe qui est la 
source des anomalies observées. 
Test de la DJ  *(C,P,T; P,O; C,O) avec des tableaux 
Le  tableau  ne  présentant  pas  deux  lignes  identiques,  la  DJ  présumée  n’existe  pas  et  la 
décomposition  testée  n’est  pas  valide  (donc  les  vues  correspondantes  ne  devraient  pas  être 
autorisées  par  le  DBA).  Comment  interdire  à  un  utilisateur  de  faire  ces  vues  ou  le  forcer  à 
utiliser un filtre chaque fois qu’il exploite une combinaison des vues ? Pour le moment il n’y a 
pas  de  solution  facile  à  ce  problème,  sinon  celle  de  bien  contrôler  la  validation  des  vues  et 
d’informer les concepteurs. 
Dépendance de clé dans une DJ 
Lorsque dans une dépendance de jointure, tous les attributs de jointure sont des superclés, on a 
une  DJ  de  clé.  Cette  décomposition  fournit  donc  des  relations  dont  la  jointure  correspond  à  la 
relation initiale que si les trois fragments sont impliqués dans lʹopération. Le problème observé 
précédemment est alors évité. 
 
Exemple :  
Soit DF, A‐‐> B, C, D et B‐‐>A, C, D dans R(A, B, C, D)  DJ : ((A, B), (A*, D), (B*, C)).   
Pour  obtenir  le  schéma  initial,  il  faut  forcément  faire  la  jointure  des  trois  projections  de  la  DJ. 
Donc R(A, B, C, D) = R1(A, B) |x| R2(A, D) |x| R3(B, C) 
              
Dans cette relation, A et B sont deux clés de R. La relation R(A, B, C, D) est donc en FN5, car elle 
satisfait lʹune des conditions de cette forme normale.  

 
Chapitre 10 Théorie de la normalisation   123

10.11 Condition suffisante pour avoir une relation en FN5 
Il a été démontré en 1992 par Date et Fagin20 que tout schéma de relation en FN3 dont toutes les 
clés sont de type atomiques est un schéma en FN5. En formulant ainsi la FN5, tout concepteur 
peut  obtenir  facilement  un  schéma  normalisé  dans  cette  forme  en  imposant,  au  besoin,  un 
identifiant ou en vérifiant que toutes les clés du schéma de la BD sont simples. Cette condition est 
suffisante  mais pas nécessaire, car il y a des relations avec une clé composée qui sont aussi en 
FN5. Toutefois, cette condition ne peut être vérifiée que pour les relations de base, mais ne règle 
pas le cas des relations temporaires formées en cours de calcul ou pour une vue concrète ou dite 
matérialisée. 
Sommaire 
La  théorie  de  la  normalisation  souligne  l’importance  du  processus  de  validation  des  schémas 
pour  appuyer  la  cohérence  et  la  justesse  du  contenu  de  la  base  de  données.  Ce  travail  de 
validation  permet  d’éviter  les  anomalies  bien  connues  et  de  réduire  la  redondance  aux  seules 
clés  primaires  et  étrangères.  La  formulation  des  schémas  en  FN3  est  la  forme  minimale 
recommandée pour l’implantation d’une base de données. Tout concepteur doit être en mesure 
de vérifier la normalité des tables exploitées par les applications.     
Les travaux sur cette question de normalisation sont très nombreux et ont permis de décrire les 
problématiques  soulevées  par  les  dépendances  fonctionnelles,  multivaluées,  de  jointure  et 
d’inclusion.  Bien  que  la  question  de  la  normalisation  ne  soit  plus  nouvelle,  le  DBA  doit  en 
connaître  les  bases  et  les  résultats  pratiques  notamment  pour  les  trois  premières  formes 
normales.    Au  besoin,  les  algorithmes  de  transformation  des  relations  doivent  être  appliqués 
pour normaliser les schémas. 
La  recherche  des  formes    FN4  et  FN5  est  un  peu  plus  complexe,  car  les  dépendances  sous‐
jacentes  sont    moins  évidentes.  Pour  certaines  BD  spéciales  dont  les  clés  sont  composées,  les 
formes  supérieures  s’imposent  pour  éviter  des  anomalies  rares,  mais  bien  réelles.  Les 
algorithmes de recherche des DJ et DMV facilitent la tâche à l’administrateur de la BD qui doit 
voir  à  la  cohérence  des  schémas,  mais  aussi  à  celle  des  données.  En  imposant  des  identifiants 
aux relations, il devient possible dʹavoir facilement un schéma en FN5. 
 
Exercices  
 
Série A (Certaines questions sont reprises du chapitre 4) 
 
1‐ Démontrez que {B, C‐‐> D, E; A ‐‐> B; A, E, G ‐‐> H} |‐ A, C ‐‐> D, E. En d’autres mots, il faut 
dériver la DF A,C‐‐>D des trois dépendances qui composent F. 
 
2‐ Démontrez que {B, C ‐‐> D, E; A ‐‐> B; A, E, G ‐‐> H}|‐ A, C ‐‐> A, B, C, D, E. 
 
3‐ Est‐ce que F= {A, B ‐‐> C; C ‐‐> A; C ‐‐> B; A, B, D ‐‐> E} est un ensemble redondant ? Il faut 
trouver une DF qui enlevée de F ne change pas sa fermeture. 
 
4‐ Trouvez toutes les clés de R(A, B, C, D) dont le F = {A, B ‐‐>C; D ‐‐>A; C ‐> D}. Il faut donc 
trouver un ensemble d’attributs dont la fermeture donne le schéma de R. 

 
Chapitre 10 Théorie de la normalisation   124

 
5‐ Soit F = {E ‐‐> G; B, G, E‐‐>D, H; A, B ‐‐> B; G ‐‐> D, E}. Vérifiez si les DF ci‐dessous sont une 
conséquence logique de F :  
a) A, B, G ‐‐> E    b) B, G ‐‐> D, B, E    c) E ‐‐> D. 
                     
6‐ Trouvez une clé pour la relation R (A, B, C, D, E, G, H, I) avec lʹensemble F suivant : {E ‐‐> G, I; 
B, G, E ‐‐>D, H;  A, B ‐‐> B;  G ‐‐> D, E}. A noter que le processus de recherche est plus long si la 
question est de trouver toutes les clés de R. Il faut trouver un ensemble d’attributs X ‐‐> A, B, C, 
D, E, G, H, I . Pour ce faire vous pouvez partir avec une DF et tenter d’enrichir le déterminant en 
utilisant les propriétés des DF. 
 
7‐ Trouver les superclés de V(G, H, J, K) dont le F ={G, H ‐‐> J; H, J ‐‐> K; J, K‐‐>G; G, K ‐‐> H} 
8‐ Démontrez par un exemple de relation concrète que les propositions suivantes sont fausses: 
a) Si A ‐‐> B alors B ‐‐> A;  b) Si A, B ‐‐> C et A ‐‐> C alors B ‐‐> C; 
 
9‐ En supposant que le schéma de R soit variable: R (A1, A2, A3, A4, ...An), trouvez en fonction 
de n, le nombre de superclés possibles pour R dans les conditions suivantes : 
a) La seule clé de R est A1 
b) Les seules clés de R sont A1 et A2. 
10‐ Un ensemble dʹattributs X est dit fermé par rapport à F si X+ = X. Avec la relation R(A, B, C) 
et un ensemble F indéterminé de DF. Quelles sont les DF de F non triviales si tous les ensembles 
possibles avec les quatre attributs de R sont fermés. 
 
Exercices  sur les formes normales et les dépendances 
 
Série B   (Certaines questions sont reprises du chapitre 4) 
Dans  les  exercices  ci‐dessous,  le  contexte  et  la  sémantique  sont  sous‐entendus  par  les 
dépendances fonctionnelles explicites (celles regroupées dans F) et implicites lorsque la clé de la 
relation  est  identifiée.  Les  autres  dépendances,  non  explicitement  énoncées  sont  réputées 
absentes, sauf indication contraire.  
 
Rappel: Une clé de relation est un ensemble minimal dʹattributs qui détermine tous les attributs 
de  la  relation  dans  laquelle  cet  ensemble  agit  comme  une  clé.  Cʹest  une  autre  façon  dʹénoncer 
implicitement certaines dépendances relatives à une clé. 
 
1‐ Soit R (A, B, C, D)  avec F = {A ‐‐> B; A ‐‐> C; A ‐‐> D}. Démontrez que lʹattribut A est une clé 
candidate ?  
Réponse : À partir des DF connues et par la propriété de réflexivité : A ‐> B;  A ‐> D;  A ‐> C, et A 
‐>A donc A est une clé. Une autre façon est de calculer la fermeture de l’attribut A.  A+ = (A, B, 
C, D), soit le schéma de la relation. A est donc une clé. 
2‐ Soit  R (A, B, C, D)  F: {A ‐‐> B, A ‐‐> C, A ‐‐> D} Est‐ce que R est en F N 3 ?   

 
Chapitre 10 Théorie de la normalisation   125

Réponse : Oui, car la clé A est transitivité et R est en FN2 et en FN3 puisque tous les attributs 
non primaires dépendent que de la clé. 
 
3‐  Soit  la  relation  Production  (noProd*,  nomClient,  dateLivraison,  prix)  avec  F  =  {noProd  ‐‐> 
nomClient,    noProd  ‐‐>  dateLivraison,  prix}.  Trouvez  une  clé  et  une  superclé  pour  la  relation 
Production. 
Réponse : La clé est noProd, car selon le F, il détermine tous les attributs. 
La  superclé:  est  (noProd,  date)  et  dʹautres  superclés  obtenues  par  lʹajout  dʹun  attribut 
quelconque à la clé. 
                     
4‐ Soit R (A, B) avec F = { A ‐‐> B; B ‐‐> A}. Est‐ce que A et B sont des clés ? 
Réponse : A ‐‐> B et A ‐‐> A (réflexivité), donc A est une clé, car il détermine tous les attributs de 
R. De plus,  B ‐‐> A et B ‐‐>B  donc B est aussi une clé. Il y a donc deux clés candidates qui sont 
équivalentes parce que ce sont les mêmes attributs qui sont en cause.  
                            
5‐ Est‐ce que R (A, B) avec F = { A ‐‐> B; B ‐‐> A} en FNBC?   
                           
Réponse : Si A ‐‐> B et B ‐‐> A sont les clés, donc B et A sont primaires et équivalentes; tous les 
attributs ne dépendent alors que de clés (primaire ou candidate). Elle est donc en FNBC. 
 
6‐ Soit R (A, B, C) avec F = { A ‐‐> B;  B ‐‐> C; C ‐‐> A}. Est‐ce que R est en FN2 ? 
Réponse : 
Les  clés  candidates  sont  A,  B  et  C.  Donc  aucun  attribut  non  primaire.  Donc  la  relation  est  en 
FN2. 
 
7‐ Soit la relation Elections pour représenter les résultats des élections de la dernière décennie:  
Elections (date*, region*, depute, premierMin) avec  F= { date ‐‐> premierMin + les DF de la clé} 
et region ‐/‐> depute;   depute ‐/‐> premierMin; region ‐/‐> premierMin;  premierMin ‐/‐> depute.  
N.B. le ‐/‐> est la négation de la dépendance fonctionnelle. Est‐ce que cette relation est en FNBC? 
Justifiez votre réponse. 
Réponse :  Comme la clé est composée, les attributs non primaires sont depute et premierMin. 
Avec la DF date ‐‐> premierMin, la relation nʹest pas en FN3 et donc pas en FNBC. 
 
8‐ Soit R (A, B, C) avec F= { A, B ‐‐> C; C ‐‐> A}. Est‐ce que (A, B) est une clé ? 
Réponse : De F, on  a que A, B ‐‐> C  et par réflexivité, A, B ‐‐> A et A, B ‐‐> B donc (A, B) est une 
clé. 
 
9‐ Est‐ce que  R (A, B, C) avec F = {A, B ‐‐> C; C ‐‐> A} est en FN3 ? 
Réponse : Pour être en  FN3, il faut  que le seul  attribut non primaire C  dépende que de la clé. 
Cʹest le cas selon le F donné. 
 
10‐ Est‐ce que la relation R (A, B, C) avec F = { AB ‐‐> C; C ‐‐> A}  est en FNBC? 
11‐ R (A, B, C) avec  F = { C ‐‐> A}.  Est‐ce que R est en FN3 ? 

 
Chapitre 10 Théorie de la normalisation   126

Réponse : La clé est {A, B, C} en raison des DF réflexives suivantes: A, B, C ‐> A;  A, B , C ‐> B;  
A, B, C ‐> C. Comme il nʹy a aucun attribut  non primaire, R est en FN3. 
 
12‐ Est‐ce que R (A, B, C) avec F = { C ‐> A} est en FNBC ? La clé est (A, B, C). 
Réponse : Non, car la DF  C ‐> A  avec l’attribut A qui est primaire dépend de C lequel nʹest pas 
une clé. 
 
13‐ Soit une relation J pour représenter la naissance des jumeaux : J (nas1, nas2, date) avec F = { 
nas1 ‐> nas2;  nas2‐> nas1; nas1 ‐>date}. Est‐ce que la relation J est en FNBC ? 
Réponse  :  Les  clés  candidates  sont  nas1  et  nas2.  La  clé  de  choisie  sera  nas1.  Les  attributs 
primaires,nas1 et nas2 et l’attribut non primaire date sont en dépendance fonctionnelle que sur 
des clés. La relation J est donc en NBC.   
                                         
14‐ Soit R (A, B, C, D) avec F = { A ‐‐> B;  C ‐‐> D}. Si R est en FN3,  la clé est‐elle {A, B, C} ? 
Réponse : Si la clé est {A, B, C}, alors le seul attribut non primaire est D qui doit dépendre que 
de la clé. Or, C ‐‐> D, est une DF qui contredit lʹhypothèse. Donc, la clé nʹest pas {A, B, C}. 
 
15‐ Soit R (A, B, C ) avec F = { A, B ‐‐> C; A, C ‐‐> B; B, C ‐‐> A}. Est‐ce que A est une clé candidate 
? Quelle est la plus grande forme normale de R ? 
Réponse  :  L’attribut  A  ne  peut  être  une  clé  candidate,  car  la  DF  A‐>B,C  n’est  pas  dans  la 
fermeture de F. Les clés candidates sont celles données dans F. Donc la relation R est en FNBC. 
 
16‐ Soit R (A, B , C ) avec F = { A, B ‐‐> C; B‐‐> C}. Est‐ce que R est en FN3 ?  
Réponse : La clé de R  est A,B et le seul attribut non primaire est C. Puisque B ‐> C, il faut en 
conclure que R n’est pas en FN2, et donc pas en FN3. 
 
17‐ Soit R (A, B, C ) avec F ={ A, B ‐‐> C; A, C ‐‐> B}. Est‐ce que A, B, C est une superclé ? 
Réponse : De F, on a que (A, B) est une clé et donc que (A, B, C)  est une superclé puisque cet 
ensemble d’attributs contient une clé. 
 
18‐ Un jeu du type cognitif  pour les enfants consiste à insérer dans les trous dʹune plaquette, des 
pièces en forme de cercle et dont les surfaces et les textures sont celles illustrées ci‐dessous. Il y a 
donc autant de trous que de pièces.  
 
  2
  3 
  5
 

 

 
4
  Les pièces  
  La plaquette  
 

 
Chapitre 10 Théorie de la normalisation   127

 
Afin  de  représenter  les  placements  possibles  pour  chaque  pièce,  on  utilise  la  relation 
PlacementPiece(rayon, texture, position) avec F = { position ‐> rayon;  les DF de la 
clé}.    Servez‐vous  des  informations  obtenues  en  observant  la  plaquette  du  jeu  qui  donnent 
indirectement  les  placements  possibles  au  regard  du  rayon  des  trous  et  des  pièces.  Est‐ce  que 
cette relation PlacementPiece est en FN3 ?    
Réponse  :  La  clé  de  cette  relation  est  composée  des  attributs  {texture,  position}.  En  effet,  en 
observant  bien  les  contraintes  physiques  imposées  par  la  plaquette  et  les  caractéristiques  des 
pièces, un placement pour une pièce est complètement déterminé par sa position et sa texture. 
La clé est donc {texture, position}. L’attribut texture est nécessaire dans la composition de la clé, 
puisquʹil  y  a  deux  pièces  de  même  surface  qui  peuvent  être  placées  dans  deux  positions 
différentes.  Le  seul  attribut  non  primaire  est  le  rayon.  La  dépendance  {texture,  position}  ‐‐> 
rayon définit la clé et texture ‐/‐> rayon ; position ‐‐> rayon. La relation Placement n’est pas en 
FN3, car l’attribut non primaire rayon dépend de l’attribut  position qui n’est pas une clé.. Est‐
elle en FNBC ? Non, car elle n’est pas en FN3. 
 
19‐ Soit R (A, B, C ) avec F = {A, B ‐‐> C; A, C ‐‐> B}. Est‐ce que A, C est une clé candidate ? 
Réponse : De F, on a A, C ‐‐> B. Par réflexivité, on a que les DF A, C ‐> A et A, C ‐> C. Donc la 
paire {A, C} est une clé candidate. 
 
20‐ Si R(A*, B*, C) en FN3, est‐ce que B dépend que dʹune clé ? 
Réponse  :  Par  définition  de  la  FN3,  R  ne  contient  aucun  attribut  non  primaire  en  dépendance 
sur autre chose qu’une clé. Par conséquent, A, B ‐> B et aucune autre DF avec B, car R ne serait 
pas en FN3. 
 
21‐ Est‐ce que la relation R(A*, B, C)  est en FNBC si F = {A, B ‐> C;  A,C ‐> B ; A‐> B, C}. 
Réponse : Non, car  A, B ‐> C où C est non primaire et dépend de (A, C) qui n’est pas clé. 
 
22‐ Soit R (A, B, C, D) avec F = { A, B, C ‐> A; A, C ‐> B; B, C ‐> D ; B‐>C}. Est‐ce que (A, B, C) est 
une superclé ? Quelle est la plus grande forme normale de R? 
Réponse : Selon F, (A, C) est une clé. En effet, A, C ‐> B et A, C‐> D obtenue pseudo‐transitivité 
et A,C ‐> A et A,C ‐> C par réflexivité. Donc (A, B, C) contient la clé et de ce fait est une superclé. 
La relation R n’est pas en FNBC, car l’attribut primaire C est déterminé par B qui n’est pas une 
clé. 
 
23‐  Soit R (A) avec F = {A‐>A }. Est‐ce que cette relation est en  FNBC ?  
Réponse : Oui, car le seul attribut primaire dépend que de la clé A. Elle est en FN3, puisqu’il n’y 
a pas d’attributs non primaires. 
 
24‐ R (A, B, C, D) avec F = { A ‐‐> B}. Est‐ce que A est une clé? 
Réponse  :  Non,  car  si  A  ‐‐>  B,  on  a  que  A‐>  C  et  que  la  DF  A  ‐>  D  n’appartiennent  pas  à  la 
fermeture F+.  
   

 
Chapitre 10 Théorie de la normalisation   128

25‐ Soit R (A, B, C, D) avec F = { A ‐‐> B}. Est‐ce que {A, C, D} est une superclé ?  
Réponse  :  Si    {A,  C,  D}  est  une  clé,  toutes  les  DF  suivantes  doivent  exister  dans  F  ou  être 
dérivables :  C, D ‐>B; A, C, D‐> C;  A, C, D‐‐> A; A, C, D‐> D. Comme ce nʹest pas le cas pour le F 
donné, il faut en conclure que A, C, D nʹest pas une superclé. 
                     
26‐ Soit R (A*, B, C*, D*), est‐elle en FN3 ? 
Réponse : Le seul attribut non primaire est B et de la clé on a que  A, C, D ‐‐> B.Elle est donc en 
FN3 puisque le seul attribut non primaire ne dépend que de la clé. 
27‐  R  (A,  B,  C,  D)  avec  F  =  {  (A  ‐>  B,  C  ;    C  ‐>  D)  }.  Quelles  sont  les  clés  parmi  les  ensembles 
d’attributs suivants : 
‐Est‐ce que A est une clé ? 
Réponse : De F on a : A ‐> B; A ‐> C; par réflexivité : A ‐‐> A et par transitivité, on a que A ‐‐> D.  
Donc, A détermine tous les attributs de R, il est donc une clé. 
 
27.1 Est‐ce que {B, C} est une clé ?  
Réponse : Si oui, les DF suivantes doivent être présentes dans F+. 
B, C ‐‐>B, réflexive ; B, C ‐‐> C, réflexive (2) 
B, C ‐‐> D ? de F, on a que C ‐‐> D dans F, donc  B, C ‐‐> D par transitivité avec (2); 
B, C ‐‐> A ? Non, car  elle ne peut pas être dérivée et donc n’est pas dans F+.  
En effet, BC+ = {B,C, D}. Par conséquent, B, C ‐/‐> A. 
 
27.2 Est‐ce que C est une clé de R ? Si oui, alors C‐> C‐>A ; C‐> B  ∈ F+ 
Réponse :     C ‐‐> C réflexive; 
      C ‐‐> A est impossible à dériver;       
      C ‐‐> B      
      C ‐‐> D  dans F   
Donc C ne peut pas être une clé de R. 
 
27.3 Est‐ce que {A, B, C} est une clé ?  
Réponse : Si oui, les DF suivantes sont dérivables : 
    A, B, C ‐‐> A, réflexive; 
    A, B, C ‐‐> B. réflexive; 
    A, B, C ‐‐> C, réflexive; 
    A, B, C ‐‐> D, par augmentation de C ‐‐> D; 
 
27.4 Est‐ce que {A, B, C} est minimal ?  
Réponse : Non, car A ‐‐> A, B, C, D. Donc {A, B, C} est une superclé, mais pas une clé. 
 
28‐ Soit R (A, B, C) avec F = { A ‐‐> B; B ‐‐> C; C ‐‐> A}. Est‐ce que R est en FN3 ? 
Réponse  :  Tous  les  attributs  sont  primaires  par  F  ou  par  dérivation.  Donc  aucun  attribut  non 
primaire n’est en dépendance fonctionnelle sur une clé R est en FN3. 
                            

 
Chapitre 10 Théorie de la normalisation   129

29‐  Soit  la  relation  Usine  (nom,  region,  equipement)  avec  F  =  {nom  ,  region  ‐‐>  equipement;  
equipement ‐‐> nom}. Est‐ce que la relation Usine est en FN3 ? 
Réponse  :  De  F,  on  peut  conclure  que  la  clé  est  {nom,  region}.  Le  seul  attribut  primaire  ne 
dépend que de la clé. Donc, elle est en FN3. 
                   
30‐    Soit  la  relation  Usine  (nomUs,  regionUs,  equipement,  capital)  avec  les  dépendances  F  = 
{nomUs  ,  regionUs  ‐‐>  equipement,  capital;    equipement  ‐‐>  nomUs}  .  Est‐ce  que  la  relation 
Usine est en FNBC ? 
Réponse  :  La  clé  est  {nom,  regionUs}  et  il  y  a  un  attribut  primaire,  soit  nomUs  qui  dépend  de 
equipement lequel nʹest pas une clé. Donc R  nʹest pas en FNBC.  
 
31‐ Soit R (A, B, C) avec F = { A, B, C ‐> A; A, B, C ‐> B; A, B, C ‐> C}. Est‐ce que {A, B} est une 
superclé ?       
Réponse : Si {A, B} est une superclé, il faut que A ou B ou A, B soit une clé. Or, comme A, B ‐/‐> 
C,  et A, B ‐/‐> B et A, B ‐/‐> A, alors il faut en conclure que A, B nʹest pas une clé (infirmation de 
la dépendance fonctionnelle).   
            
32‐  Est‐ce  que  C  est  une  clé  candidate  dans  la  relation    R  (A,  B,  C)  avec  lʹensemble  F  suivant  : 
{AB‐> C; C ‐> A} ? 
Réponse : Non, car si C ‐> C  par réflexivité, la DF  C ‐> B est impossible à dériver à partir de F. 
 
33.  Soit  la  relation  Marketing  pour  représenter  les  test  de  commercialisation  effectués  dans 
différentes régions du Québec et à diverses périodes de lʹannée.  
Marketing (produit*, region*, mois*, prix, score)  
 
Les DF sont regroupées dans  F = {produit, region, mois ‐> prix, score;  produit ‐‐> prix}. Est‐ce 
que cette relation est en FN3 ?  
 
Réponse : Non, puisquʹun attribut non primaire, soit prix, dépend de l’attribut produit qui nʹest 
pas une clé. Est‐elle en FNBC? Non, puisquʹelle nʹest pas en FN3.  
 
34‐ Soit R (A, B, C, D) avec F = {A ‐> B; B ‐> C; C ‐> D}. Est‐ce que A ‐‐> D ? 
Réponse : De F, on a que A ‐‐> B; B ‐‐> C; par transitivité, on  obtient la DF A ‐‐> C; et  C ‐‐> D; 
alors A ‐‐> D (par transitivité). 
                           
35‐ Soit R (A, B, C) avec F = { A ‐‐> B; B ‐‐> C; C ‐‐> A}. Est‐ce que A est la seule clé candidate 
dans R ? 
Réponse : De F, on a les DF suivantes dans F = {A ‐‐> B; B ‐‐> C}, donc par transitivité on obtient 
que A ‐‐> C; B ‐‐> A;  A‐‐> A; et B ‐‐> B; donc B est aussi une clé candidate.   
  
 *** Quelques exercices non résolus*** 
36‐ En supposant que lʹextension ci‐dessous soit représentative de toutes les DF possibles dans R 
(A, B, C, D), quelles sont les DF non triviales qui existent dans R ?    

 
Chapitre 10 Théorie de la normalisation   130

                                        
R :  A  B  C  D 
  1  1  1  2 
  3  2  3  1 
  2  2  3  1 
  1  1  2  3 
 
37‐ Soit la BD académique suivante pour la gestion des études : 
Acad (matric*, noCours*, nomEtud, pgm, horaire, bat) 
matric :  matricule étudiant 
noCours :  numéro du cours 
nomEtud :  nom de lʹélève 
pgm :    programme dʹétude 
horaire :  heure hebdomadaire de cours 
bat :    bâtiment de salle de cours 
 
Les dépendances fonctionnelles de lʹensemble F sont les suivantes : 
matric ‐> nomEtud; et noCours ‐/‐> nomEtud. 
matric ‐> pgm; et no‐cours ‐/‐> pgm 
noCours ‐> horaire; et matric ‐/‐> horaire 
noCours ‐> bat; et matric ‐/‐> bât 
 
Lorsquʹun  étudiant  se  retire  de  tous  ses  cours,  toutes  les  données  le  concernant  doivent  être 
supprimées de la BD. Les domaines sont réputés atomiques. 
Transformez la relation Acad pour obtenir des relations en FN3.  
 
38‐ Voici une BD composée dʹune seule relation pour représenter le transport maritime :  
TransMmar ( bat*, date*, cap, cargo, valeur). La sémantique des attributs est al suivante : 
 
bat :   le libellé du bateau 
date :  date de départ du port 
cap :  capacité des caves du vrac 
cargo : nature du vrac à livrer 
valeur : valeur de la cargaison de vrac 
 
Lʹensemble F = { les DF définies par la clé;  bat, date ‐> cargo; cargo, cap ‐> valeur;  bat ‐> cap}  
38.1‐ Quelle est la plus grande forme normale de cette relation ? 
38.2‐ Donnez quelques anomalies possibles avec cette relation. 
38.3‐ Normalisez cette relation en FNBC. 
 
39‐ Voici la relation Univ et ses dépendances fonctionnelles F. Notez que dans cet exemple avec 
sept attributs, la recherche de la clé à partir seulement de F nʹest pas triviale. 
 

 
Chapitre 10 Théorie de la normalisation   131

Univ  (fac,  doy,  dep,  dir,  prof,  rang,  etudiant) 


  a  b  c  d  e  f  g 
                
La sémantique des attributs est la suivante:  
 
  fac: faculté  doy: doyen   
  dir: directeur de département  prof: nas du professeur   
  rang: rang académique  etudiant: nas de lʹétudiant(e)   
 
Lʹensemble F est le suivant (avec son pendant simplifié) : 
fac ‐> doy;         qui est codée ainsi :  a ‐> b; 
doy ‐> fac;  b ‐> a; 
dep ‐> dir;  c ‐> d; 
prof ‐> rang, dir;  e ‐> f, d; 
dep ‐> fac;    c ‐> a; 
etudiant ‐> dep, fac, doy;  g ‐>c, a, b; 
prof, rang ‐> dep, fac;  e, f ‐> c, a; 
 
39.1‐ Quelle est la clé de cette relation ? 
39.2‐ Normalisez cette relation en FN3. 
 
40‐ Soit un schéma de BD dans laquelle lʹensemble F est le suivant:  F = { A, B, C, ‐‐> G;   
D, B ‐‐> A;  H, C ‐‐> F}. Dérivez de lʹensemble F la dépendance H, C, D ‐‐> G? 
 
41‐ Démontrez que les ensembles de dépendances F et G ci‐dessous sont équivalents.  
F = { A ‐‐> B, C; B ‐‐> A, D; C, D ‐‐> E; E ‐‐> C, D} 
G = { A ‐‐> B, E; B ‐‐> A; C, D ‐‐> E; E ‐‐> C, D } 
Pour prouver cette équivalence il faut démontrer que F  |=G et que G |= F. 
 
42‐ Calculez la fermeture de lʹensemble dʹattributs {B, C, E} dans la relation R(A, B, C, D, E, G, H, 
I} dont le F = {C, E ‐‐> A, C, H; B, C ‐‐> A, D; A, C, H ‐‐> A, D, G;  
A, D, G ‐‐> G, I}. Donnez une superclé de R.  
                                              
43‐ Soit F = {A ‐> C; B ‐> C; C, D ‐> E; C, D‐> F; E, F ‐> A}. Est‐ce que les dépendances de F sont 
vérifiées directement ou non dans les tables de la base de données ci‐dessous ? 
                                                
A  D  E    A  B    C  E  F    B  D  F 
0  0  0    0  3    0  0  0    3  0  0 
1  2  0    1  4            4  2  0 
 
44‐ Soit la relation R (A, B, C, D, E, J, K) avec F ={A, B ‐> C; A, C ‐>B; A, D ‐>E;  B ‐> D; B, C ‐> A; 
E  ‐>  J}.  Quelles  sont  les  décompositions  de  R,  parmi  les  suivantes,  qui  conservent  les 
dépendances ? 

 
Chapitre 10 Théorie de la normalisation   132

a) { (A, B); (B, C); (A, B, D, E); (E, K)} 
b) { (A, B,  C); (A, B, D, E, K)} 
c) {(A, B, C); (A, C, D, E); (A, D, K)}
 
Série C  Synthèse relationnelle (cas particuliers) 
45‐ Démontrez que le schéma de la BD formé par les relations R1(A, B) et R2(B, C) et dont le F = { 
B‐‐>A} est un schéma de BD qui conserve les données.  
Les exercices proposés sont résolus avec lʹalgorithme de synthèse et concernent le traitement des 
cas particuliers : 
1‐ Obtenez un schéma de relations avec F = {A, B, X −> G; A −> X}. 
  (Attention : il y a présence d’un attribut étranger). 
 
2‐ Obtenez un schéma de relations avec F = {X −> Y; X −> Z; Y −> Z} (Attention : présence d’une 
DF redondante). 
 
3‐ Obtenez un schéma de relations avec F = {X −> A; Y−>B; Y −> X; X −> Y} 
(Attention : présence de clés équivalentes). 
 
4‐ Obtenez un schéma de relations avec F = {X −> A; Y−>X; A −> Y} 
(Attention : clé équivalente et DF redondante) 
                                  
Série D Cyclicité du schéma, dépendance multivaluée et FN4/FN5 
1‐ Vérifiez ou infirmez la cyclicité des schémas ci‐dessous. Justifiez votre réponse. 
a) R1(A, B, D), R2(A, C, D), R3(D, E, G), R4(D, F, G), R5(G, I, J); 
b) R1(A, B, C), R2(B, C, D), R3(A, C, D), R4(A, B, D); 
c) R1(A, B), R2(B, D), R3(C, D), R4(C, E), R5(D, E); 
d) R1(A, B, C), R2(B, C, D), R3(A, D). 
 
2‐ Soit R(A, B, C, H, E) 
avec D ={ (1) A ‐‐>> B, C; (2) H, E ‐‐>> C. Démontrez que D |= A, H ‐‐>> B, E. 
Solution : 
Par complémentarité de (1) on a :  (3) A‐‐>> H, E 
Par transitivité de (3) et (2) : A‐‐>> C – { E, H}. Donc (4) A‐‐>> C. 
Augmentation de (4) avec l’attribut H : (6) A, H ‐‐>> C, H 
Par complémentarité  de (6); A, H ‐‐>> B, E   
CQFD 
 
3‐ Soit R(A, B, C, H, E) avec D = { (1) A‐‐>B, C ; (2) C ‐‐> H}. Démontrez que A‐‐>> H, E ne peut 
pas être valide dans R.  
Réponse : 
Il est possible de le démontrer soit par un contre‐exemple, soit par dérivation. 
Contre‐exemple : 

 
Chapitre 10 Théorie de la normalisation   133

Voici une instance de R dans laquelle les DF de D sont vérifiées : 
 
R:  A  B  C  H  E 
  b2  c1  h3  e0 
a1 
  b2  c1  d3  e2 
a1 
  a2  b2  c1  h3  e1 
  a3  b3  c2  h5  e2 
  a1  b2  c1  h3  e1 
  a2  b2  c1  h3  e1 
 
Par contre, A‐‐>> H, E  n’est pas vérifiée, car (a1, h3, e2) et (a1, d3, e0) ne sont pas deux tuples 
dans l’extension de R. Par conséquent, cette extension vérifie toutes les dépendances énoncées et 
invalide celle recherchée. 
 
4‐ Démontrez que dans R (A, B, C, H. E) avec D = {A‐‐>> B, C ; H‐‐> C} la DMV A ‐‐> C est aussi 
valide.  
Solution : 
Par dérivation : (1) A‐‐>> B, C; 
(2) H ‐‐>> C; 
par la propriété de coalescence : 
de (1) on a que A‐‐> C s’il existe un déterminant qui détermine C. 
Or, H ‐‐> C; Donc  la DF est valide. De plus A‐‐>> C. 
CQFD. 
5‐ Soit R(A, B, C, H, E) avec D = {C‐‐>> H, E; A ‐‐> B, C}.  Décomposez s’il y a lieu la relation R 
pour obtenir une base de données en FN4.  
Solution : 
R n’est pas en FN4 puisque la DMV C‐‐>> H, E est valide et qu’elle n’est pas triviale. En Effet, 
l’union du déterminé et du déterminant ne donne pas le schéma complet. De plus, le déterminé 
n’est pas un sous‐ensemble du déterminant. 
La décomposition fournit donc deux nouvelles relations qui s’avèrent en FN4 : 
R1(C, H, E) et R2(A, B, C) . 
 
6‐ Soit R (A, B, C, H, E) avec D = {A‐‐>> B, C; H, E ‐‐>> C}  Démontrez que D |= A ‐‐>> C, H, E. 
Réponse : 
(1) A ‐‐>> B, C 
(2) A ‐‐>> H, E  par complémentarité de (1) 
(3) H, E ‐‐>> C 
(4) A ‐‐>> C par transitivité 
Addition de (2) et (4) A ‐‐>> C, H, E 
 

 
Chapitre 10 Théorie de la normalisation   134

7‐  Donnez  un  contre‐exemple  pour  illustrer  que  les  DMV  ci‐dessous  sont  invalides  dans  la 
relation R(A, B, C) : 
7.1 Si  D= {A ‐‐>> B, C} pour R  alors A ‐‐>>B; 
Solution : 
R :  A  B  C 
  a1  b1  c1 
  a1  b2  c2 
 
On a alors que A ‐‐>> B, C dans R :  car (a1, b2, c2) et (a1, b1, c1) sont des tuples de R. Par contre, 
A ‐/‐>>B, car (a1,b2, c1) et (a1, b1, c2) ne sont pas des tuples de cette extension.  
7.2 Si  D= {A ‐‐>> B} pour R  alors A ‐‐>B; 
Solution : 
R :  A  B  C 
  a1  b1  c1 
  a1  b2  c1 
                    
Dans cette extension, A‐‐>> B, car (a1, b2, c1) et (a1, b1, c1) sont des tuples présents. Toutefois, la 
dépendance fonctionnelle A‐> B n’est pas validée.    
                        
D’une DMV, on ne peut pas déduire la DF correspondante. Comme nous l’avons vu, d’une DF, 
il est possible de déduire la DMV correspondante. 
 
7.3 Si  D= {A, B ‐‐>> C} pour R  alors A ‐‐>C; 
Réponse :  
L’extension ci‐dessous vérifie la DMV seule dépendance valide dans R. 
La DMV est vérifiée puisque les tuples (a1, b1, c2) et a1, b1, c1) sont dans l’extension de R. Par 
contre, la DF A‐‐> C n’est pas vérifiée.                                     
R :  A  B  C 
  a1  b1  c1 
  a1  b1  c2 
 
8‐ Voici deux relations R(A, B, C) et S(H, E) avec F = { H ‐‐> E; B ‐‐> C}.  
 
R :  A  B  C  S:  H  E 
  1  2  5    7  2 
  1  3  6    2  3 
  2  3  6    9  11 
 
Par la suite, la dépendance d’inclusion DIN = {R [A, B]  ⊆ S [H, E] est ajoutée au schéma de cette 
base de données. Au départ, la base est cohérente par rapport à F; suite à l’ajout de la DIN, la 
base de données doit demeurer cohérente.  

 
Chapitre 10 Théorie de la normalisation   135

 
8.1 Quels sont les tuples à ajouter dans la base pour qu’elle soit cohérente ? 
Réponse : 
Pour  que  la  cohérence  soit  vérifiée,  il  faut  que  la  DIN  soit  validée.  Il  faudra  donc  ajouter  les 
tuples (1, 2) et (1, 3) à l’extension S.    
 
8.2 Quelle règle faudrait–il ajouter à R pour que la vérification de l’inclusion soit une procédure 
finie?    
Réponse : 
Il faut ajouter dans R la DF suivante : A‐‐> B.  

 
Chapitre 11 Optimisation des requêtes   137

Chapitre 11   
Optimisation des requêtes relationnelles 
 
Une  requête  relationnelle  formulée  avec  SQL  est    traduite  en  une  expression  algébrique 
représentée  sous  la  forme  dʹune  arborescence  (arbre  de  requête)  dans  laquelle  les  noeuds 
internes  sont  les  opérateurs  unaires  ou  binaires  et  les  noeuds  terminaux    sont  des  relations  de 
base  ou  des  vues.    Cet  arbre  de  requête  est  optimisé  21  avant  dʹêtre  transformé  en  un  plan 
dʹexécution formé d’une suite d’appels de procédures pour exécuter l’arbre optimisé. Le rôle de 
lʹoptimiseur  du  SGBD  est  de  choisir  parmi  tous  les  arbres  équivalents  possibles  à  une  requête 
particulière, celui dont le plan dʹexécution sera le plus performant. Comme le nombre dʹarbres 
est grand et que toutes les données ou facteurs qui influencent le calcul dʹune expression ne sont 
pas connus à priori, le choix repose souvent sur une heuristique générale dont lʹimplémentation 
peut varier dʹun optimiseur à lʹautre. 
 
Avant de formuler cette heuristique générale dʹoptimisation, nous allons examiner les propriétés 
des  opérateurs  relationnels  qui  sont  à  la  base  des  transformations  d’un  arbre  en  un  autre 
équivalent. Ces transformations se font uniquement sur la base de règles syntaxiques 22. Le MRD 
de la base OUC qui sera utilisé par les exemples de ce chapitre comprend trois relations: 
Ouvrier (nas*, nom, salaire, spec, noUs) 
Usine (noUs*, site, prod, nasChef) 
Contrat(noCont*, fin, cout, noUs 
 
L’attribut  fin  est  de  type  Date.  Le  MCD  ci‐dessous  est  spécifié  avec  le  formalisme  UML. 
L’association entre les classes Ouvrier et Usine est caractérisée par une multiplicité (auparavant 
les  contraintes  structurelle  du  modèle  E/As) :   zéro  ou  plusieurs  ouvriers  travaillent  dans  au  plus 
une usine.    
 
   
  Usine 
  0..1 noUs*  0..1
  Tavaille  site 
  Execute
0..*  prod 
 
Ch f 0..*
  Ouvrier 
  nas*  Contrat 
  nom  noCont* 
  salaire  fin 
   spec  cout 
noUs U
 
Figure 11.1 Modèle UML de la base OUC 
 
Ce  MRD  a  ses  contraintes  référentielles  et  éventuellement,  il  est  possible  de  définir  une 
contrainte  d’inclusion.  Cette  dernière  existerait  si  chaque  chef  d’usine  était  obligatoirement  un 

 
Chapitre 11 Optimisation des requêtes   138

ouvrier  de  cette  usine.  Le  diagramme  ci‐dessous  représente  les  contraintes  référentielles  et  la 
contrainte d’inclusion (ligne pointillée) entre les classes. 
 
  Usine
 
  (p) (p)
 
 
  Ouvrier  (e)  (e) Contrat
 
      Figure 11.2 Diagramme des contraintes 
            
La présence d’un cycle est gênante, car elle bloque la création d’une usine si le chef qui est aussi 
un  ouvrier  n’est  pas  créé  et  ce  dernier  ne  peut  pas  être  créé  si  l’usine  où  il  travaille  n’est  pas 
créée. Pour éviter ce cycle de contraintes, il suffit de ne pas définir la contrainte d’inclusion au 
moyen  du  mécanisme de  l’intégrité  référentielle  et  de  le  faire  avec  un  trigger  de  Pre‐Insertion. 
Ce MRD sans la contrainte d’inclusion sera le modèle type auquel nous ferons référence dans ce 
chapitre sur l’optimisation des requêtes. 
 
Typologie des requêtes 
Le  nombre  de  requêtes  qui  peuvent  utiliser  pour  interroger  un  modèle  de  données  est  très 
grand.  Dans  la  discussion  de  lʹoptimisation  de  celles‐ci,  il  est  utile  de  les  classer ;    nous 
emprunterons la typologie proposée par Shasha23 qui regroupe les requêtes de la façon suivante: 
 
a‐ Requête singulière (Point query) 
C’est une requête basée sur une seule relation et dont le prédicat de sélection est composé avec 
une ou plusieurs égalités dʹattributs de manière à ce que la réponse comprenne au plus un tuple. 
SELECT * 
FROM  Ouvrier 
WHERE nas = ʹ1234ʹ; 
 
b‐ Requête singulière multiple (Multipoint query) 
C’est  une  requête  basée  sur  une  seule  relation  et  dont  le  prédicat  est  composé  avec  une  ou 
plusieurs égalités dʹattributs, mais dont la réponse comprend plusieurs tuples. 
SELECT * 
FROM  Ouvrier 
WHERE salaire > 10000.00 And spec = ‘soudeur’; 
                      
                                   
c‐ Requête à intervalle (Range query) 
Requête  basée  sur  une  relation  et  dont  le  prédicat  comprend  un  test  dʹintervalle.  La  réponse 
comprend normalement plusieurs tuples. 

 
Chapitre 11 Optimisation des requêtes   139

SELECT * 
FROM  Ouvrier 
WHERE salaire > 10000.00 and salaire < 50000.00; 
 
d‐ Requête dʹappariement sur un ensemble ordonné dʹattributs Z (prefix match query) 
Requête  dont  les  conditions  dʹégalité  doivent  inclure  obligatoirement  une  sous‐chaîne  définie 
selon  lʹensemble  ordonné  Z.  Par  exemple,  dans  la  relation  Ouvrier,  considérons  le  sous‐
ensemble  ordonné  Z  =  {nas,  nom}  de  cette  classe.  Une  requête  dont  le  prédicat  comprend  des 
égalités  formulées  avec  un  des  deux  sous‐ensembles  {nas}  ou  {nas,  nom}  est  une  requête 
dʹappariement.  Souvent,  ce  type  de  requête  découle  de  la  présence  dʹun  index  composé 
disponible dans le dictionnaire.  
SELECT nom, salaire 
FROM Ouvrier 
WHERE nas = ʹ1234ʹ and nom = ʹTrudelʹ; 
 
Par  contre,  la  requête  ci‐dessous  nʹest  pas  de  ce  type  puisque  lʹattribut  nas  est  absent  du 
prédicat. 
SELECT nom, salaire 
FROM Ouvrier 
WHERE  nom = ʹTrudelʹ ; 
 
Cette requête est cependant singulière multiple. 
 
e‐ Requête min‐max (Extremal query) 
Requête  formulée  avec  une  seule  relation  et  dont  le  prédicat  est  composé  dʹégalités  faisant 
référence  à  avec  une  valeur  minimale  ou  maximale.  La  réponse  peut  comprendre  plusieurs 
tuples.  
SELECT nom, salaire 
FROM Ouvrier 
WHERE  salaire = MAX (SELECT salaire from Ouvrier); 
 
f‐ Requête avec ordonnement (Ordering query) 
Requête dont la réponse est affichée selon lʹordre dʹun ou plusieurs attributs. 
SELECT nom, salaire 
FROM Ouvrier 
ORDER BY nom; 
        
g‐ Requête de groupement (Grouping query) 
Requête dont la réponse est partionnée par un ou plusieurs attributs. 
SELECT noUs, AVG(salaire) 
FROM Ouvrier 
GROUP BY noUs ;   
 

 
Chapitre 11 Optimisation des requêtes   140

h‐ Requête de jointure (Joint query) 
Requête comprenant une ou plusieurs jointures définies sur une ou plusieurs relations. 
SELECT nas, nom, site 
FROM Ouvrier O, Usine U 
WHERE O.noUs = U.noUs; 
 
Une telle requête est lourde à calculer puisquʹelle sous‐tend la comparaison de tous les tuples de  
lʹextension Ouvrier avec ceux de lʹextension Usine.  
 
Bien entendu, une requête peut être à la fois du type intervalle et de groupement, comme une 
autre  peut  être  de  type  min‐max  et  en  même  temps  dʹintervalle.  Les  types  de  la  requête 
prépondérants dans une exploitation de la base ont une incidence sur la nature des index à créer 
et  dans  la  manière  avec  laquelle  la  réponse  sera  calculée.  C’est  cette  opération  de  calcul  de  la 
réponse  que  l’optimisation  cherche  à  rendre  rapide  quelle  que  soit  la  catégorie  à  laquelle  une 
requête appartient. Pour y arriver, l’optimiseur transformera l’arbre de requête et fera un choix 
approprié  d’un  ou  plusieurs  algorithmes  de  calcul  qui  exploiteront  les  index  existants.  Le 
résultat de cette opération est un plan de calcul (Execution plan) qui fournit la réponse recherchée 
pour une requête.  
 
Traitement d’une requête SQL 
Toute requête SQL transmise au SGBD soit par un pilote ODBC, soit par une autre interface API 
fournie par le développeur du système de gestion est l’objet d’un traitement en trois étapes : 
 
1‐ Analyse de la requête 
La clause SQL est l’objet d’une analyse syntaxique suivie d’une analyse sémantique. La première 
phase de l’analyse consiste à vérifier si la clause SQL est conforme à la grammaire en validant 
les mots clés utilisés (partie lexicographique) et ensuite en voyant à ce que la structure respecte 
celle  imposée  par  les  règles  d’écriture.  Dans  le  cas  d’un  rejet,  c’est  une  erreur  de  type  1.  Dans 
une deuxième phase, il y a validation du nom des tables, des attributs, des fonctions d’usager, …  
utilisés  dans  la  requête.  Pour  effectuer  cette  opération,  le  module  d’analyse  consultera  le 
dictionnaire de données de la base. Si l’analyse rejette la requête, c’est une erreur de type 2. 
 
La  deuxième  phase  de  l’analyse  est  de  nature  sémantique  et  vise  à  débusquer  les  prédicats 
toujours  faux,  à  transformer  une  requête  imbriquée  en  une  jointure  classique  et  à  vérifier  la 
cohérence d’un prédicat au regard des contraintes de domaine définies dans la dictionnaire. Par 
exemple, si le salaire maximum d’un employé est limité à 100 000.00$, il ne sert à rien de lancer 
l’exécution  d’une  requête  dont  le  prédicat  est  le  suivant :  Where  salaire  =>  150000.00.  Lors  de 
l’étape  d’analyse  sémantique,  il  est  possible  de  rejeter  la  requête  sur  la  base  seule  d’une 
contrainte de domaine (Erreur de type 3).  
                 
Finalement, une troisième phase consiste à générer l’arbre de la requête formulé au moyen des 
opérateurs  algébriques.  Cette  opération  quoique  non  obligatoire  a  l’avantage  de  fournir  une 
représentation algébrique de la requête SQL dont les transformations sont facilement comprises 

 
Chapitre 11 Optimisation des requêtes   141

puisque  nous  maîtrisons  la  sémantique  des  opérateurs  de  l’algèbre.  Souvent  dans  les  SGBD 
commercialisés,  cet  arbre  n’est  pas  explicitement  généré  et  le  système  passe  directement  à  la 
formulation  du  plan  d’exécution.  Un  grand  nombre  de  SGBD  implémentent  les  phases  1  et  2. 
Par contre, la phase 3 n’est pas encore très bien mise en œuvre par la plupart des systèmes. À la 
sortie de l’analyse le SGBD récupère un plan d’exécution logique représenté soit par l’arbre de 
requête,  soit  par  une  suite  d’appels  de  procédures  spécialisées  dont  le  résultat  est  la  réponse 
demandée.  Ce  plan  d’exécution  dit  logique  pourra  être  par  la  suite  converti  lors  de  l’étape 
d’optimisation en un plan d’exécution physique composé lui aussi de programmes organisés en 
arbre. À la limite, un plan d’exécution logique peut coïncider avec le physique. 
 
2‐ Optimisation de l’arbre de requête 
L’arbre de requête est transformé en un plan d’exécution physique composé avec les procédures 
associées aux opérateurs algébriques qui sont exécutés dans un ordre choisi pour être efficace. 
Ce choix dépend aussi de l’indexation de certains attributs et des algorithmes sous‐jacents à leur 
indexation. La difficulté est de choisir parmi de très nombreux plans d’exécution possibles, celui 
qui dans le contexte présent s’avèrera le plus efficace.  
 
3‐ Exécution du plan 
Le plan d’exécution obtenu de l’étape d’optimisation est ensuite compilé (plus précisément les 
programmes appelés par les nœuds du plan) et lancé afin d’aboutir aux résultats escomptés. 
 
Transformations algébriques dʹun arbre de requête 
Lʹarbre de requête peut être lʹobjet de plusieurs transformations de nature syntaxique basées sur 
les propriétés des opérateurs de l’algèbre relationnelle. Le résultat recherché est un nouvel arbre 
dit  équivalent  parce  que  donnant  la  même  réponse,  mais  permettant  un  calcul  plus  rapide. 
Toutefois,  la  décision  dʹappliquer  de  telles  transformations  ne  dépend  pas  uniquement  de  la 
requête  en  soi,  mais  aussi  des  ressources  dʹaccès  disponibles  et  de  la  taille  des  diverses 
extensions.  
 
1‐ Lʹintégration dʹune cascade de sélections 
Lorsque qu’un sous‐arbre d’une requête est formulé une cascade de sélection,    
σp1 (σp2 ( (σp3(R))) ≡ σp1∧p2∧p3(R)  ≡  σp2∧p1∧p3(R) ≡  σp3∧p2∧p1(R)  
où pi est un prédicat formulé avec les attributs de R.    
Exemple : 
σ p1 
  σ p1 ∧ p2  ∧ p3 
 
  σ p2 
  =
R
 
  σ p3 
 
  R 
 
            Figure 11.3 

 
Chapitre 11 Optimisation des requêtes   142

Exemple SQL : 
SELECT nas, nom 
FROM  (SELECT nas, nom FROM  Ouvrier WHERE salaire < 30 000) 
WHERE nom = ʹGagnonʹ ; 
 
Cette requête SQL sera plutôt exécutée par l’équivalent algébrique de la requête suivante : 
 
SELECT nas, nom 
FROM  Ouvrier 
WHERE salaire < 30 000 and nom = ʹGagnonʹ ; 
 
Il  en  sera  autrement  lorsque  la  requête  sous‐tend  des  jointures.  Dans  ce  cas,  le  prédicat 
conjonctif  sera  distribué  parmi  des  diverses  sélections  pertinentes  à  chaque  extension 
participante à la jointure. 
 
2‐ Commutativité des sélections 
σp2∧ p1 (R)  ≡  σ p1∧ p2 (R)  ≡  σ p1 (σp2 (R)) 
 
Il peut être utile dans certains cas de transformer une cascade de prédicats en une conjonction 
des  mêmes  prédicats.  Inversement,  il  peut  utile  de  remplacer  une  conjonction  de  prédicats 
distincts par une cascade de sélections dans laquelle l’ordre des sélections importe peu.  
La requête ci‐dessus est équivalente à la suivante  dans laquelle les prédicats ont été commutés : 
                                                           
SELECT nas, nom  
FROM  (SELECT nas, nom From Ouvrier WHERE WHERE nom = ʹGagnonʹ) 
WHERE salaire < 30 000); 
 
3‐ Commutativité de la sélection avec la jointure ou le produit cartésien 
Lorsque  tous  les  attributs  du  prédicat  de  sélection  sont  inclus  dans  le  schéma  de  lʹun  des 
opérandes, il est possible dʹexécuter la sélection avant la jointure. 
 σp (R1 |x| R2) ≡ σp (R1) |x| R2  ssi p ∈  R1 
De même, comme la jointure est commutative, on aura aussi l’expression équivalente suivante : 
 
   σp (R1 |x| R2) ≡ R2 |x|σp (R1) 
 
Toutefois, si le prédicat p peut être transformé en une conjonction de prédicats p1 et p2 de sorte 
que  tous  les  attributs  de  p1  soient  inclus  dans  le  schéma  de  R1  et  que  tous  les  attributs  de  p2 
soient inclus dans le schéma R2, alors on a l’équivalence suivante : 
 
 σp (R1 |x| R2) ≡ σ p1 ∧ p2 (R1 |x| R2)  ≡  σ p1 (R1) |x| σ p2 (R1)  
                                
Voici un exemple dans lequel l’équivalence est exprimée entre deux arbres de requête. 

 
Chapitre 11 Optimisation des requêtes   143

 
 
|X| Usine.noUs = Ouvrier.noUs 
  σ nom =’Tremblay’ And noUs =10
  ≡
  σ noUs =10 σ nom =’Tremblay’  
|X| Usine.noUs = Ouvrier.noUs
 
  Usine Ouvrier
Usine  Ouvrier 
 
 
            Figure 11.4 
 
Dans cette transformation par la commutativité, la sélection descend vers les feuilles, permettant 
ainsi  dʹeffectuer  plus  tôt  dans  le  calcul  la  sélection  des  tuples  et  dʹobtenir  une  extension 
intermédiaire moins encombrante qui, si la taille le permet, pourrait être rangée totalement dans 
la RAM. Cette stratégie accélère le calcul en évitant les accès au disque. 
 
Voici un exemple dʹune descente plus profonde de la sélection. Voici une requête SQL avec deux 
jointures dont lʹune est exprimée sous forme dʹune sous‐requête :  
 
SELECT  noCont 
FROM  Ouvrier O, Usine U  
WHERE O.noUS = U.noUs and nom = ʹPlanteʹ and noUs = 10 And  
    noUS IN (SELECT noUS 
      FROM Contrat 
      WHERE  fin > ʹdec‐97ʹ); 
 
Cette  requête  initiale  avec  une  sous‐requête  est  transformée  par  l’analyseur  en  une  jointure 
triple : 
 
SELECT  noCont 
FROM  Ouvrier O, Usine U, Contrat C  
WHERE O.noUS = U.noUs And U.noUs = C.noUS And  
O.nom = ʹPlanteʹ And U.noUs = 10 And C.fin > ʹdec‐97ʹ); 
 
N.B. Cette requête est plus facilement représentée par un arbre de requête évitant l’usage d’un 
sous‐arbre dans le prédicat de jointure entre Ouvrier et Usine. 
 

 
Chapitre 11 Optimisation des requêtes   144

Lʹarbre de la requête est donc le suivant:  
 
Π noCont
 
  σ    O.nom  =’Plante’  And  U.noUS  =  10  And  fin  >  ‘dec‐
 
  |X| U.noUS = C.noUS
 
  |X| O.noUS = U.noUS
Contrat
 
  Usine  Ouvrier 
 
Cet arbre de requête peut être transformé lors de l’optimisation en commutant la sélection avec 
la jointure de sorte que la sélection soit exécutée le plus tôt possible dans le calcul de la réponse. 
En effet une sélection réduit la taille de la table intermédiaire qui occupe moins d’espace disque 
ce qui entraîne moins de lectures.   
 
  Π noCont
 
  |X| U.noUS = C.noUS
 
 
σ  fin > ‘dec‐1997’ 
 
  |X| O.noUS = U.noUS
Contrat
 
  σ  U.noUS = 10  σ  O.nom =’Plante’
 
Usine  Ouvrier
 
 
            Figure 11.5 
 
Dʹautres  transformations  algébriques  sont  possibles  comme  par  exemple  la  permutation  des 
jointures.  Celles‐ci  sont  avantageuses,  notamment  si  elles  réduisent  la  taille  des  extensions 
intermédiaires dans le processus dʹévaluation de lʹarbre de requête.   
 
4‐ Absorption des projections dans une cascade 
Une cascade de projections est équivalente à la dernière projection de la cascade.  
Π liste1 ( Π liste2 (Πliste3 ( R)))  ≡  Π liste1(R) 
 
En  effet  la  liste  précédente  doit  obligatoirement  contenir  les  attributs  de  la  liste  suivante.  À  la 
fin, seuls les attributs de la dernière liste de projection seront présents dans la réponse.  
 

 
Chapitre 11 Optimisation des requêtes   145

Voici un exemple d’une requête SQL de ce type : 
Select nas  
From (Select nas, nom  
From (Select nas, nom, salaire  
From Ouvrier));  
La transformation fournit une autre clause SQL équivalente, en principe plus simple à calculer. 
Select nas   From Ouvrier; 
 
5‐ Commutativité de la sélection et de la jointure (ou du produit cartésien) 
 Lorsque tous les attributs du prédicat p de la sélection sont inclus dans le schéma dʹune relation 
opérande de la jointure précédente, on a lʹéquivalence suivante : 
σp (R1 |x| R2) ≡  σp(R1) |x| R2  ssi p ⊆  R1 
 
Toutefois, si p = p1∧ p2 et que p1 contient que les attributs de R1 et si p2 est formulé quʹavec les 
attributs de R2 alors on aura que :    σp (R1 |x| R2) ≡  σp1(R1) |x| σp2(R2) 
 
6‐ Commutativité de la projection avec la jointure 
Deux cas de figure sont à distinguer : 
Cas  1:  Soit  la  liste  dʹattributs  de  sortie  L  =  {  A1,  A2,  ...An,  B1,  B2,...  Bk}  et  dans  laquelle  les 
attributs  Ai  appartiennent  au  schéma  de  R1,  tandis  que  les  attributs  Bk  sont  inclus  dans  le 
schéma de R2. Si la condition de jointure cj  est formulée quʹavec les attributs de la liste L,  alors 
on a lʹéquivalence suivante : 
ΠL (R1|x|cj R2) ≡ Π(A1, A2, ...An) (R1) |x|cj  Π(B1, B2, ...Bk) (R2) 
 
Cas 2: Sʹil y a des attributs dans la condition de jointure qui ne sont pas dans la liste de sortie L, 
alors ces  attributs L’= {C1, C2, ...Ct} doivent alors être ajoutés à la liste L de la projection finale. 
Une  projection  supplémentaire  avec  L  est  alors  nécessaire  afin  de  pouvoir  transformer  la 
projection initiale de la jointure. 
ΠL+L’ (R1 |x|R2) ≡ Π(A1, A2, ...An, C1, .. Ct) (R1)  |x| Π(B1, B2, ...Bn, C1, ... Ck) (R2) 
 
Exemple :  SELECT nom, salaire 
    FROM Ouvrier O, Usine U 
    WHERE O.noUs = U.noUs ; 
 
Lʹarbre de requête correspondant est le suivant :   
 
Π nom, salaire 
 
 
|X| Ouvrier.noUs = Usine.noUs
 
 
Ouvrier  Usine
 
Figure 11.7 

 
Chapitre 11 Optimisation des requêtes   146

      
Lʹattribut noUs nʹest donc pas dans la liste de la projection et il y sera ajouté pour descendre la 
projection et faire commutation avec la jointure. 
 
Π nom. salaire 
 
  |X| Ouvrier.noUs = Usine.noUs
 
  Π nom, salaire, noUs  Π nom, salaire, noUs 
 
  Ouvrier  Usine
Figure 11.8 
 
Cette transformation permet en théorie de diminuer la taille de lʹextension en diminuant la taille 
de chaque tuple. Dans certains cas, cette transformation place beaucoup plus de tuples dans la 
zone de mémoire partagée de sorte quʹil y aura accélération du calcul de la jointure. Rappelons 
encore  une  fois  que  si  le  SGBD  exécute  en  pipeline  cette  clause,  la  jointure  de  deux  tuples  est 
accompagnée de la projection sur le ou les attributs de sortie.  
 
7‐ Commutativité de la sélection et de la projection 
Sachant que la jointure est équivalente à une sélection du produit cartésien :   
(R1 |x| R2) ≡ σR1.A = R2.A(R1 x R2)   (1) 
  
On a donc la suite des transformations suivantes : 
ΠA, B (R1 |x| R2)  ≡  Π A, B (σR1.A = R2.A (R1 x R2)) ≡  σR1.A = R2.A(ΠA, B (R1 x R2)) ≡ σ R1.A 
= R2.A (ΠAR1 x Π A, B (R2)))  
Dans cet exemple, la jointure est transformée en un produit cartésien et il y a commutation entre 
la projection et la sélection (3). Ensuite la projection est commutée avec le produit cartésien (4) 
pour provoquer une descente de la projection vers les opérandes. Finalement, la sélection et le 
produit cartésien sont fusionnés pour donner une jointure thêta (5).    
                      
L’arbre de requête est graduellement transformé pour donner la suite suivante :    
 
 
Π a,b  Π a,b  σ R1.a = R2.a σ R1.a = R2.a  |X|R1.a  = 
 
 
σ R1.a = R2.a Π a,b X Π a,b  Π a,b
  |X| R1.a = R2.a  ≡  ≡ ≡ ≡ 
 
XR1.a  =  XR1.a  =  Π a,b Π a,b
  R1  R2 
R1 R2 
 
  R1  R2  R1 R2 R1 R2
  (1)  (2)  (3) (4) (5) 
 
            Figure 11.6 

 
Chapitre 11 Optimisation des requêtes   147

                                
L’exécution  de  cet  arbre  se  fait  de  bas  en  haut.  Il  ne  faut  y  avoir  un  calcul  essentiel  sériel.  En 
effet, La projection de R 1 est combinée la jointure pour que les deux opérations s’effectuent en 
parallèles. 
Voici un exemple en SQL d’une telle transformation : 
Select O.noUs, O.nom 
From Ouvrier O, Usine U 
Where O.noUs = U.noUs ; 
 
Cette  simple  jointure  pourrait  être  exécutée  avec  la  projection  des  deux  relations  Ouvrier  et 
Usine.  En  principe,  une  telle  transformation  accélère  le  calcul  si  chaque  opérateur  est  exécuté 
complètement avant que le suivant soir lancé. 
Select O1.noUs, O1.nom 
From  (Select noUs, nom From Ouvrier) O1, (Select noUs From Usine) U1 
Where O1.noUs = U1.noUs ;   
                               
En  pratique,  cette  transformation  ne  se  fera  pas,  car  la  première  forme  peut  être  exécutée 
rapidement  en  mode  pipeline :  un  tuple  de  Ouvrier  est  comparé  avec  un  tuple  de  Usine.  Si  la 
condition  de  jointure  est  vérifiée,  alors  la  projection  du  tuple  est  faite  immédiatement  et  le 
résultat  est  stocké  dans  la  table  de  la  réponse.  On  voit  donc  que  seule  la  transformation 
algébrique est insuffisante pour obtenir une exécution optimisée. Il faut aussi prendre en compte 
les algorithmes de calcul qui peuvent réaliser plusieurs opérateurs dans une seule exécution. Ce 
rôle  est  dévolu  à  l’optimiseur  qui  se  doit  de  produire  un  PLAN  D’EXÉCUTION  de  la  requête 
formulée avec les algorithmes disponibles dans le SGBD pour le calcul des requêtes.  
8‐ Commutativité de la sélection avec lʹintersection, lʹunion et la différence 
Avec ces opérateurs ensemblistes, il y a une obligation préalable concernant la compatibilité des 
schémas. Les équivalences suivantes sont vérifiées :   
 
σp (R1 ∩ R2) ≡ σp(R1)  ∩  σp (R2) 
σp (R1 ∪ R2) ≡  σp(R1)  ∪ σp (R2) 
σp (R1 −  R2) ≡  σp(R1)  −  σp (R2) 
 
Les arbres correspondants sont faciles  construire ainsi :                   
 
L’intersection de deux relations :  
  σ p  ∩
 
  ∩  ≡ σp σ p 
 
  R1  R2  R1 R2 
                                    
                                                           
 

 
Chapitre 11 Optimisation des requêtes   148

L’union de deux relations : 
  ∪
  σ p 
 
≡ σp σ p 
  ∪ 
 
R1  R2  R1 R2 
 
 
Différence entre deux relations : 
 
 
σ p  −
 
  σp
−  ≡ σp
 
  R1  R2  R1 R2
 
 
          Figure 11.9 
 
En SQL‐92, ces arbres correspondraient aux requêtes suivantes: 
 
SELECT nom 
FROM (SELECT * 
  FROM  Ouvrier 
  INTERSECT 
  SELECT  * 
  FROM OuvrierEnMission) 
WHERE salaire > 50000 and noUs = ʹu20ʹ; 
 
La  relation  ou  la  vue  relationnelle  OuvrierEnMission  est  présumée  avoir  été  créée  dans  le 
dictionnaire avec un schéma identique à celui de la relation de base Ouvrier. Dans cette requête 
la sélection est effectuée en dernier et constitue ce qui et appelé le 2e bloc de la clause SQL. 
La requête équivalente dans laquelle la sélection est effectuée en premier avant l’intersection. 
SELECT nom 
FROM  Ouvrier 
WHERE  salaire > 50000 and noUs = ʹu20ʹ 
INTERSECT 
SELECT  nom 
FROM  OuvrierEnMission 
WHERE salaire >50000 and noUs = ʹu20ʹ; 
                                                                                                                                                                                          

 
Chapitre 11 Optimisation des requêtes   149

Voici  un  exemple  similaire  avec  lʹunion.  Cet  opérateur  exige  aussi  deux  schémas  compatibles 
pour que le calcul soit exécutable. Dans ce cas‐ci, le schéma des deux opérandes est compatible 
avec lʹautre. L’un des opérandes est une vue relationnelle OuvriersEnConge.  
 
SELECT  nom 
FROM  Ouvrier 
WHERE  salaire > 25000 and salaire < 50000                                             
UNION 
SELECT  nom 
FROM  OuvriersEnConge 
WHERE  salaire > 25000 and salaire < 50000 ; 
 
9‐ Commutativité des opérateurs ensemblistes : union et intersection 
Ici encore, les deux schémas logiques, R1 et R2 doivent être identiques. 
(R1 ∪ R2)  ≡ (R2 ∪ R1) 
(R1 ∩ R2)  ≡ (R2 ∩ R1) 
Toutefois,  cette  propriété  ne  tient  pas  pour  la  différence.  L’inversion  des  opérandes  change  le 
contenu de la réponse. 
 
10− Associativité des opérateurs  de jointure, du produit cartésien, de lʹunion et de lʹintersection. 
Soit une double jointure : 
R1 |x|(R2 |x|R3) ≡ (R1 |x|R2) |x|R3 
 
Lʹassociativité est réalisée avec R1 et R2 puisque cette dernière relation, R2 comprend lʹattribut 
de jointure de R1 
R1 x (R2 x R3) + (R1 x R2)  x R3 
R1 ∪  (R2 ∪  R3) ≡ (R1 ∪  R2) ∪  R3 
R1 ∩ (R2 ∩ R3) ≡ (R1 ∩ R2) ∩ R3 
 
Heuristique générale de lʹoptimisation dʹune requête 
Le  principe  général  de  l’optimisation  algébrique  est  la  transformation  de  lʹarbre  de  la  requête 
initiale  en  un  autre  équivalent  en  présumant  quʹil  est  probablement  plus  rapide  à  exécuter24.  
Cette heuristique est assistée par lʹaccès aux statistiques des tables et par la présence des index 
dans le dictionnaire. 
 
Les étapes sont les suivantes : 
1‐ Les prédicats  de sélection  sont transformés, sʹil y a lieu, en une cascade afin de pouvoir par la 
suite  les  descendre  vers  les  feuilles  de  lʹarbre,  i.e.  vers  les  relations  de  base.  Lʹhypothèse  sous‐
tendue  par  cette  opération  est  que  le  prédicat  est  suffisamment  sélectif  pour  diminuer  la  taille 
des relations intermédiaires et ainsi alléger les calculs, notamment ceux de jointure.  
 
2‐ Descendre les opérateurs de sélection le plus bas possible en utilisant la commutativité de la 
sélection avec les opérateurs de projection, de jointure, de l’union et dʹintersection. 

 
Chapitre 11 Optimisation des requêtes   150

 
3- Réorganiser les feuilles de l'arbre en utilisant l'associativité des
opérateurs binaires de sorte que le sous-arbre ayant la sélection la
plus restrictive soit exécuté en premier.
 
4‐ Transformer toute suite formée dʹune sélection et dʹun produit cartésien en une jointure thêta. 
 
5‐ Descendre le plus possible les projections vers les feuilles de lʹarbre de requête. 
 
6‐ Identifier les sous‐arbres ayant des opérateurs qui peuvent être exécutés en même temps que 
dʹautres  (mode  pipeline),  notamment  la  sélection.  Par  exemple,  une  sélection  suivie  dʹune 
projection est une paire dʹopérateurs exécutables simultanément. 
Quelques problèmes avec lʹheuristique générale 
La  restructuration  dʹun  arbre  de  requête  sous‐tend  très  souvent  la  permutation  des  opérateurs 
binaires, notamment celui de la jointure. Or, ces permutations algébriques ne sont pas toujours 
justifiées sur la plan de la performance. Au contraire la connaissance de la taille des extensions 
de  base  et  de  celles  des  relations  intermédiaires  peut  réduire  les  avantages  de  ces 
transformations. Par exemple une suite de jointures peut être transformée en plusieurs arbres de 
requête différents et le choix de lʹarbre optimisé à exécuter est difficile si on ignore la taille des 
extensions, les facteurs de sélectivité pour les différents opérateurs et lʹindexation des attributs. 
 
Exemple :    (Ouvrier |x| Usine) |x| Contrat 
 
Si  card(Usine)  <<<  card(Contrat)  <<<  card(Ouvrier)  alors  lʹexpression  ci‐dessous  a  des  chances 
dʹêtre plus performante pour le calcul de la réponse.  
( Usine  |x|  Contrat)  |x| Ouvrier)  
 
La  taille  de  la  relation  intermédiaire  résultant  par  exemple  la  jointure  (Usine  |x|  Contrat)  est 
plus petite si la sélectivité de lʹattribut de jointure dans Contrat est près de 1 (grande sélectivité). 
Elle  sera  vraisemblablement  plus  rapide  à  calculer.  La  sélectivité  de  lʹattribut  de  jointure  dans 
chaque  relation  interviendra  donc  dans  le  choix  de  la  permutation  de  jointures.  Toutefois,  la 
cardinalité  des  extensions  et  les  sélectivités  des  attributs  ne  sont  pas  les  seuls  facteurs  qui 
conditionnent  la  rapidité  du  calcul.  La  présence  dʹun  index  sur  lʹattribut  de  jointure  de  la 
relation  Ouvrier  augmentera  de  façon  significative  la  vitesse  de  calcul  de  la  relation 
intermédiaire.  Cette  nouvelle  donne,  le  SGBD  peut  la  découvrir  à  partir  des  métadonnées 
stockées dans le dictionnaire. Il se peut fort bien qu’un index compense pour lʹencombrement et 
fait en sorte que lʹexpression initiale soit celle qui est retenue comme étant lʹarbre optimisé.  
           
Un  autre  problème  peut  apparaître  avec  la  transformation  algébrique,  notamment  lors  de  la 
permutation de la sélection avec la jointure. Un index est créé sur une table de base et peut être 
utilisé que pour lʹaccès à aux tuples de cette table.     
 
 
 

 
Chapitre 11 Optimisation des requêtes   151

   σp    |X| R1.A = R2.A 
   
  |X| R1.A = R2.A *          =     σp        R2 
   
R1                  R2      R1                
   
* Indexation de A dans R1
 
Figure 11. 10 
 
Or, si lʹopération de sélection est effectivement descendue le plus bas possible vers les feuilles et 
appliquée avant la jointure, le résultat est une relation intermédiaire qui n’est plus indexée, car 
un index du dictionnaire pointe sur les tuples dʹune table de base dont il connaît lʹemplacement. 
Par  conséquent,  même  si  lʹattribut  est  indexé  dans  la  table  de  base,  celui‐ci  ne  peut  pas  être 
utilisé  dans  la  table  intermédiaire.  Sʹil  arrive  que  lʹattribut  descendu  soit  celui  de  la  jointure, 
alors  cette  descente  inhibant  lʹutilisation  de  lʹindex  dans  le  calcul  de  la  jointure,  autant  alors 
l’éviter et faire en pipeline la jointure et la sélection.  
                                     
Limite de l’optimisation algébrique 
On voit donc que la seule optimisation algébrique est insuffisante et peut même conduire à des 
ralentissements  du  calcul.  De  plus,  les  diverses  transformations  algébriques  conduisent  à  de 
nombreux  arbres  de  requêtes  possibles  parmi  lesquels  lʹoptimiseur  doit  sélectionner  celui  qui 
deviendra  le  plan  à  exécuter.  Or,  cette  sélection  ne  donne  pas  toujours  les  résultats  escomptés 
parce que des informations manquent pour guider ce choix. Ces métadonnées manquantes sont 
notamment, la cardinalité des extensions, la sélectivité des attributs et la présence des index. Ces 
informations seront à la base même de lʹoptimisation fondée sur le coût.  
Modèle de coût 
Pour estimer la taille des résultats intermédiaires, lʹoptimiseur doit avoir accès aux métadonnées 
suivantes: 
a‐ La distribution des valeurs de chaque attribut, sinon il fut assumer lʹhypothèse simplificatrice 
dʹune  distribution  uniforme  pour  les  valeurs  des  attributs  et  de  faire  aussi  lʹhypothèse  que  ces 
derniers sont indépendants les uns des autres. 
 
b‐ Dans une extension, le nombre de valeurs non null pour un attribut Ai  est noté card(Ai); 
 
c‐  Les  valeurs  min(Ai)  et  max(Ai)  pour  chaque  attribut  de  lʹextension  courante;  cette 
métadonnée  permettra  de  connaître  le  nombre  maximum  de  valeurs  différentes  possible 
présentes dans la table; 
 
d‐ La cardinalité de lʹextension de chaque relation, soit card(R1); 
     
e‐ Le nombre de valeurs distinctes pour chaque attribut Ai dans une relation R, soit V(Ai, R). 

 
Chapitre 11 Optimisation des requêtes   152

Ces  métadonnées  stockées  dans  le  dictionnaire  permettront  dʹavoir  une  meilleure 
approximation  des  tailles  des  relations  intermédiaires  et  cela,  grâce  à  la  sélectivité  si  associé  à 
chaque  opérateur.  Pour  un  calcul  plus  précis,  il  faudra  sʹéloigner  de  la  distribution  uniforme 
présumée  et  calculer  la  distribution  des  valeurs  pour  chaque  attribut.  Ce  calcul  plus  lourd  ne 
conduit pas toujours un gain significatif de performance.  
Sélectivité dʹun opérateur :  si 
En présumant une distribution uniforme des valeur, la sélectivité si  dʹun attribut Ai est définie 
ainsi :  
 si = V(Ai, r)/card(r) 
 pi =  1/V(Ai, r) 
 
La probabilité  pi  est celle de trouver une valeur donnée dans une extension R ou la fraction des 
tuples  qui  seront  obtenus  par  un  opérateur  de  sélection.  Par  exemple,  lʹattribut  sexe  dans  une 
extension a la probabilité de 1/2. Par conséquent avec  une  distribution uniforme, la moitié des 
tuples de lʹextension courante réfèrent à des hommes, tandis que lʹautre à des femmes.    
 
Sélection  
Soit lʹopération de sélection suivante: σp(R1) où le prédicat est formulé quʹavec lʹattribut A, soit 
p (A= 10). Le nombre de tuples dans la réponse est estimé par  
card(σp (R1)) =  P(A) ∗ card(R1)  
 
avec P(A), la proportion des tuples de R1 qui vérifient le prédicat p(A). Or, cette proportion est 
la probabilité quʹun tuple de R1 vérifie le critère de sélection p définie pour cet opérateur comme 
étant le suivant : P(A) = 1/V(Ai, R1)soit le la sélectivité du prédicat de sélection. 
card(σp (R1)) = P(A) ∗ card(R1) = card(R1)/ V(Ai, R1)= si * card(R1) 
 
Pour  une  extension  donnée,  plus  le  nombre  de  valeurs  distinctes  est  grand,  plus  la  taille  de  la 
réponse  sera  petite.  Dans  le  cas  dʹune  distribution  non  uniforme,  le  calcul  de  V(A,  R1)  est 
fonction de la nature de A et de la distribution courante. Si la distribution n’est pas uniforme, il 
faut  avoir  l’histogramme  maintenu  dans  le  dictionnaire.  Avec  ce  dernier,  il  est  possible  de 
connaître  combien  de  tuples  ont  une  valeur  donnée  pour  chaque  attribut.  La  sélectivité  d’un 
attribut pour un intervalle de valeurs peut alors être calculée plus précisément.                                
 
Projection 
Une projection sous‐tend toujours la suppression des doublets dans sa réponse. Plus le nombre 
de  doublets  est  grand  plus  la  réponse  risque  dʹêtre  petite.  Par  exemple,  la  projection  de  la 
relation Ouvrier sur lʹattribut sexe, ne fournira que deux valeurs. La liste des attributs peut être 
plus ou moins élaborée.  
a‐ Avec une liste formée dʹun seul attribut A de R1 qui en est aussi la clé. 
card(Π(A) ) = card(R1)  
 
b‐ Avec une liste formée dʹun seul attribut quelconque A de R1 

 
Chapitre 11 Optimisation des requêtes   153

card(ΠA(R1)) = card(R1) ∗ P(A) = card(R1) ∗ 1/ V(A, R1)  


où P(A)  est la proportion des tuples avec une valeur distincte pour A. 
 
c‐ Avec une liste formée avec deux attributs quelconques, A et B  
card(ΠA, B (R1)) = card(R1) ‐ (card(R1)∗ d) = card(R1)  ∗ (1 ‐ d)    
où  d  est  la  proportion  des  tuples  de  R1  qui  sont  des  doublets  sur  les  deux  attributs  A,  B.  Or, 
comme  nous  avons  présumé  une  distribution  uniforme,  cette  proportion  d  est  valable  pour 
toutes les paires de valeurs qui apparaissent dans la table R1. 
 
Jointure 
La  taille  dʹune  jointure  est    estimée  grosso  modo  (approximation  de  1er  ordre)  par  la  formule 
suivante : 
R1 |x|R2 =  Pj ∗ (card(R1)  ∗ card(R2)) 
où Pj est la sélectivité de la jointure. 
 
Calcul de la sélectivité pour une jointure:  Pj 
Supposons  que  la  condition  de  jointure  soit  R1.A  =  R2.A  et  que  lʹattribut  A  de  R1  soit 
indépendant  de  A  de  R2.  Cela  signifie  quʹil  nʹexiste  pas  de  corrélation  entre  le  A  des  deux 
relations.  
 
Chaque tuple t de R1 est joint avec card(R2)/V(A,R2) tuples de R2, de sorte que nous avons au 
plus, 
card(R1 |x| R2) = (card(R1)  ∗ card(R2))/V(A, R2) = card(R1) ∗ card(R2) ∗1/V(A, R2) 
card(R1 |x|R2) = card(R1) ∗ card(R2) ∗ Pj 
 et  donc  Pj  =  1/V(A,  R2).  Toutefois,  ce  résultat  suppose  que  tous  les  tuple  de  R1  (ou  R2) 
participent à la jointure. 
 
La  sélectivité  de  la  jointure  est  estimée  comme  étant  égale  à  lʹinverse  du  nombre  de  valeurs 
distinctes  de  A  dans  la  2e  relation  opérande  de  la  jointure.  Cette  sélectivité  est  généralement 
calculée avec les statistiques recueillies dans le dictionnaire des données. Elles sont mises à jour 
périodiquement avec ou sans l’intervention de l’administrateur de la base de données. 
                              
En inversant la jointure, on obtient : 
card(R2 |x|R1) = (card(R1)  ∗ card(R2))/V(A, R1) =  
card(R2) ∗ card(R1) ∗ 1/V(A, R1)   et donc Pj = 1/V(A, R1). 
    
Il  y  a  donc  un  avantage  théorique  avec  les  hypothèses  avancées  à  exécuter  une  jointure  en 
plaçant comme premier opérande la relation ayant la plus petite taille. Ces estimés de Pj et les 
tailles des extensions courantes varient avec la mise à jour des tables. Ces données sur les tables 
sont des métadonnées rangées dans le dictionnaire de données du SGBD.  
 
Ces  métadonnées  sont actualisées périodiquement  par  lʹexécution  de la  commande  ANALYZE 
(pour Oracle), RUNSTATS (pour DB2‐V2) et UPDATE STATISTICS (pour IBM‐Informix) 

 
Chapitre 11 Optimisation des requêtes   154

 
Cette  approche  ne  tient  pas  compte  des  structures  de  données  et  de  la  charge  dʹentrée‐sortie 
nécessaire pour traiter les tuples des relations. Il y a des structures telles les clusters, les index et 
le  hashing  qui  accélèrent  passablement  lʹaccès  aux  tuples  et  de  ce  fait  allègent  le  calcul    de  la 
jointure.  Lorsque  le  plan  dʹexécution  estimé  le  plus  efficace  a  été  choisi,  il  est  alors  exécuté  en 
faisant  appel  aux  algorithmes  de  base  pour  calculer  la  sélection,  la  projection  et  la  jointure. 
Ceux‐ci  sont  implémentés  en  mettant  à  contribution  les  ressources  présentes  comme  les  index 
(cluster/non‐clustered  et  dense/sparse),  les  clusters  et  le  hashing.  Le  choix  dʹun  algorithme  pour 
calculer une jointure dépendra donc des structures de données existantes au moment du calcul 
 
Algorithmes généraux pour les opérateurs relationnels 
 
Sélection 
Le calcul de la sélection peut se faire avec ou sans index. A priori lʹintérêt de lʹindex est grand, 
mais  son  utilité  dépend  de  la  sélectivité  du  prédicat  ou  dʹune  de  ses  parties  appelées  sous‐
critère. Plus  la sélectivité dʹun attribut A est faible, moins il y a dʹintérêt à utiliser son index.       
a‐ Sélection sans index 
La  construction  dʹun  index  est  généralement  coûteuse  et  nécessite  un  parcours  séquentiel  des 
tuples. En général, comme les pages contiennent que des tuples dʹune même table, il est souvent 
plus  performant  de  parcourir  les  pages  en  testant  un  à  un  les  tuples  en  regard  du  prédicat  de 
sélection.  Tout  tuple  retenu  est  alors  projeté  immédiatement  sur  les  attributs  de  la  réponse 
(traitement pipeline) et ensuite ajouté à lʹensemble réponse.  
 
Lʹabsence dʹun index nʹest pas nécessairement catastrophique lorsquʹil sʹagit dʹattributs de faible 
sélectivité (i.e. que pour une valeur de lʹattribut, beaucoup de tuples sont à vérifier). Il peut être 
aussi  rapide  de  parcourir  séquentiellement  une  extension  de  la  relation  de  taille  raisonnable 
dont  une  partie  est  en  mémoire.  Finalement,  comme  les  pages  contiennent  que  des  tuples  de 
même schéma logique, le balayage total de lʹextension utilisera un minimum de pages.  
 
Exemples :   
La requête dʹintervalle ci‐dessous peut être traitée efficacement même en lʹabsence dʹun index. 
SELECT nas, nom 
FROM Ouvrier 
WHERE salaire > 15000.00 ;    
 
En effet, on peut estimer avec les métadonnées que 85% des ouvriers ont un salaire supérieur à 
15000.00  et  quʹun  balayage  séquentiel  sera  plus  efficace.  Le  seuil  pour  préférer  le  parcours 
séquentiel est souvent fixé à à un facteur de sélectivité supérieur à 0.35. 
Plan dʹexécution: 

t : tuple de l’extension Ouvrier; 
reponse m : set de_tuples;   ‐‐ projection de Ouvrier sur  
        ‐‐attributs de sortie 

 
Chapitre 11 Optimisation des requêtes   155

Pour chaque tuple t de Ouvrier ‐‐ accès séquentiel 
do  
  si t.salaire > 15000  
  alors reponse = reponse ∪ t[nas, nom] 
fin_do 
fin;} 
 
Le plan d’exécution repose essentiellement sur un parcours séquentiel de la table. Lʹefficacité de 
cet algorithme dépendra essentiellement de la taille de l’extension de la table Ouvrier.  
 
b‐ Sélection avec index 
Il peut y avoir, dans un prédicat de sélection, plusieurs attributs qui sont déjà indexés par autant 
de  B‐arbres  et  dont  lʹadresse  de  la  racine  de  chaque  arbre,  rappelons‐le,  est  stockée  dans  le 
dictionnaire du SGBD. Il faut généralement faire un choix préalable pour exploiter dans le calcul 
que  les  attributs  indexés  qui  sont  les  plus  sélectifs,  i.e.  ceux  dont  le  facteur  de  sélectivité  se 
rapproche  de  1.  Souvent,  le  SGBD  retient  que  les  attributs  dont  la  sélectivité  dépasse  un  seuil 
donné.  Les  autres  attributs  seront  testés  après  lʹaccès  aux  tuples  identifiés  ou  obtenus  par  un 
balayage  séquentiel  des  tuples  éventuellement  identifiés  par  lʹindex.  Lʹorganisation  de  lʹindex 
disponible a aussi un impact sur la performance du calcul.  Un index dense/non‐clustérisé sera 
moins performant quʹun autre dense/clustérisé.  
Ainsi,  si  le  prédicat  est  formé  dʹune  conjonction  d’attributs,  le  SGBD  construira  une  liste 
dʹadresses  avec  chaque  attribut  indexé,  pour  ensuite  faire  lʹintersection  des  rowid  obtenus.  Le 
résultat  fournira  les  adresses  de  tuples  dont  lʹaccès  direct  permettra  de  vérifier  les  autres 
attributs non indexés. 
 
Rappel:  
Un  index  dense  est  un  index  sur  un  attribut  dont  chaque  pointeur  conduit  à  un  tuple.  Au 
contraire, si le pointeur conduit à un lot de tuples (ex. une page,  lʹindex est dit non dense.  
De même, si les entrées voisines dans lʹindex conduisent à des tuples qui sont à proximité voire 
dans la même page, lʹindex est dit regroupé i.e. clustérisée. Au contraire, il est dit non clustérisé 
(sparse) 
 
Algorithme SIDX2 
Indexation de deux attributs du prédicat :  (index sur salaire et une autre sur noUs) 
 
SELECT nas, nom 
FROM  Ouvrier 
WHERE salaire >= 25000.00 and noUs = ʹu20ʹ  And nom = ʹTremblayʹ; 
 

 
Chapitre 11 Optimisation des requêtes   156

Plan dʹexécution : 
Deux  index  sont  utilisés  :  index  idx_salaire  et  idx_noUs  déjà  définis  sur  la  relation  Ouvrier  et 
répertoriés  dans  le  dictionnaire.  La  réponse  est  calculée  sans  avoir  recours  dans  une  première 
étape aux tables.  
{ ‐‐SIDX2 
S, U, I set de rowid de Ouvrier; 
S = Lire (idx_salaire_Ouvrier, 25000);‐‐ set de rowid 
U = Lire(idx_no_us_Ouvrier, ʹu20ʹ); ‐‐ set de rowid 
I = Intersection (S, U); ‐‐ pour critère ET 
Pour chaque rid de I   ‐‐ rid est une adresse de tuple 
do 
{ t = Lire (Ouvrier, rid) ‐‐lire directe de t qui est un tuple   ‐‐de Ouvrier 
si t.nom = ʹTremblayʹ alors  
  reponse = Union ( reponse, (t.nas, t.nom)); 
fin_do; 
fin; 
 
Lorsque  les  critères  sont  liés  par  un  OU,  alors  il  y  aura  Union  des  ensembles  S  et  U.  Certains 
systèmes adoptent une stratégie efficace soit dʹexploiter un index et de vérifier les autres critères 
en même temps que lʹaccès aux tuples. 
 
Une  autre  méthode  encore  plus  efficace  consisterait  à  lire  dans  le  B‐arbre  la  première  feuille 
pour  la  valeur  25000.00  et  parcourir  horizontalement  les  autres  feuilles  jusquʹà  la  dernière  en 
vérifiant pour chaque tuple si les autres termes du prédicat conjonctif sont vérifiés. Ce balayage 
via lʹindex est efficace si la sélectivité de lʹattribut est assez grande.                             
 
Jointure sans index 
La  jointure  peut  être  calculée  par  différents  algorithmes  selon  la  présence  ou  lʹabsence  dʹindex 
sur les attributs de jointure. En lʹabsence dʹindex, le calcul est fait en exploitant au maximum la 
structure particulière du placement des tuples parmi les pages. Dans une jointure de type R1 |x|  
R2, nous ferons lʹhypothèse que le premier opérande est toujours formé avec la relation ayant la 
plus petite extension. 
 
a‐ Algorithme de jointure par boucles imbriquées (JBI) 
Cet algorithme est le plus simple, mais souvent le moins efficace avec des extensions de grandes 
tailles.  La  première  extension  est  lue  séquentiellement  et  de  préférence  stockée  entièrement  en 
RAM. Pour chaque tuple de R1 en RAM, il y a une comparaison avec les tuples de la deuxième 
extension,  R2.  Soit  np(R1)  le  nombre  de  pages  utilisées  pour  stocker  les  tuples  de  R1,  alors  le 
coût de ce calcul est approximé par : 
Coût‐JBI = np(R1) + np(R1) * np(R2) 
Le coût est composé des pages de R1 lues initialement pour les placer en mémoire. Ensuite, pour 
chaque page de R1 il faut lire toutes les pages de R2.  

 
Chapitre 11 Optimisation des requêtes   157

Soit  les  deux  attributs  de  jointure  A  et  B  des  relations  R1  et  R2,  alors  lʹalgorithme  BI  est  le 
suivant : 
{ ‐‐ JBI 
Pour chaque tuple de R1 
{do 
Lire (R1, t1);‐‐ t1 est une var. de tuple 
Pour chaque tuple de R2  
  {do  
  Lire (R2, t2); 
  si t1.A = t2.B alors  
  reponse = t1 || t2 ;}}} 
 
Cet algorithme est dʹautant plus coûteux que le nombre de pages de R2 est élevé. 
 
b‐ Jointure par tri‐fusion (JTF) 
Les deux extensions sont en premier triées sur les deux attributs de jointure et ensuite les tuples 
triés  sont  fusionnés  pour  retenir  que  les  tuples  ayant  la  même  valeur  pour  les  attributs  de 
jointure. le coût du tri est de lʹordre de nlog2n avec n égale au nombre de pages dʹune extension. 
Le traitement des tuples en RAM est considéré comme négligeable.  
 
Le coût de cet algorithme est composé des coûts de lecture des pages en mémoire : 
coût‐JTF = np(R1)*log2(np(R1)) + np(R2)*log2(np(R2)) + np(R1) + np(R2) 
 
Cet algorithme est plus performant que le précédent. Son efficacité dépend essentiellement  de 
celle du tri utilisé (ex. Quicksort).   
{‐‐ Algo JTF 
R1 = Trier (R1, A); 
R2 = Trier(R2, B); 
reponse = fusion de R1 et de R2; 
fin; } 
 
c‐ Jointure par hashing (JH) : équijointure 
Lʹidée de base est lire séquentiellement les tuples de la plus petite extension, R1 et de traiter par 
hashing la valeur de lʹattribut de jointure A. Aucun attribut de la jointure n’est indexé ou si la 
sélectivité  de  l’attribut  de  jointure  est  très  grande.  Tous  les  tuples  de  R1  qui  ont  une  même 
valeur  de  hashing  sont  placés  dans  une  même  page  temporaire.  Celle‐ci  est    rappelée  lors  du 
calcul  de  la  jointure.  Lors  du  hashing,  les  pages  sont  gardées  le  plus  longtemps  en  mémoire 
RAM  afin  de  pouvoir  y  ranger,  dans  un  deuxième  temps,  les  tuples  de  R2  qui  ont  la  même 
valeur de hashing. 

 
Chapitre 11 Optimisation des requêtes   158

 
 
  t2 de R2 
  2  pages de 
H(t2[B])  
  données 
 
 
  t1 de R1 
  H(t1[A]) 
  1  ZMP 
 
 
          Figure 11.11 
 
La  deuxième  relation  R2  est  lʹobjet  du  même  traitement  par  hashing  sur  lʹattribut  B.  Dans  une 
même page, se retrouvent donc les tuples différents mais ayant la même valeur de hashing.   
                        
Hashing (R1, A, R2, B) 
{ int p; 
Pour chaque t1 de R1 
do { 
  Lire (R1, t1) 
  p = h(t1, A); 
  Ecrire(page p, t1); 
  } 
Pour chaquet2 de R2 
do { 
  Lire (R2, t2); 
  Accéder(pagesR1, t2.B); 
  si TRUE alors Ecrire(reponse, t1||t2);}} 
 
Si cette opération de regroupement est effectuée lors du chargement initial de chaque tuple, le 
regroupement par la valeur de lʹattribut de jointure se fait progressivement (clustering). Dans ce 
cas,  il  faudra  déclarer  un  cluster  qui  devrait  sous‐tendre  la  création  de  deux  index  denses  et 
regroupés  (clustered),  un  pour  A  et  un  autre  pour  B.  La  jointure  se  fera  alors  par  lecture  des 
tuples  stockés  dans  les  pages  clustérisées.    Si  une  page  vient  à  déborder,  il  y  a  gestion  du 
débordement  par  chaînage  des  pages.  Au  moment  de  la  jointure,  il  suffit  donc  de  faire  la 
jointure des tuples qui se retrouvent dans la même page ou à proximité, en vérifiant quʹils ont la 
même valeur pour les attributs de jointure  et cela, pour éviter les jointures de tuples qui sont en 
collision. Cet algorithme peut être utilisé efficacement qu’avec l’équijointure, i.e. un prédicat de 
jointure construit avec l’égalité.  
 

 
Chapitre 11 Optimisation des requêtes   159

Jointure sans clustering préalable 
Lorsque les tuples ne sont pas au préalable regroupés (clustered), il faudra en premier effectuer 
cette  opération  de  lecture  et  de  placement  des  tuples  dans  des  pages  temporaires.  Cette 
opération est lourde en terme de temps de lecture. Une fois les tuples regroupés, lʹalgorithme se 
poursuit avec le calcul de la jointure.  
 
Une variante de cet algorithme est proposée par Babb25 . Elle est intéressante dans les cas où le 
clustering  est  à  faire  ponctuellement  (dynamiquement),  juste  avant  le  calcul  de  la  jointure.  Il 
sʹagit de construire et dʹutiliser un vecteur de bits qui signale la présence dʹun tuple ayant une 
valeur de hashing dans la première relation. Le vecteur de bits est gardé en mémoire  RAM.   
 
 
R1  0  R2 
  Vecteur  de  bits 

 

 

 

 
  Tuple t de R1  0 
1  Tuple t de R2 
    ?
1   
  h(t[A] = x 
0  h(t[B] = x 
  x  est  l’indice  dans  le 
x est l’indice dans le vecteur 
 
           
Figure 11.12 
 
En  calculant  la  valeur  de  hashing  dʹun  tuple  de  R2  et  de  son  indice  correspondant  dans  le 
vecteur,  il  est  possible  de  savoir  rapidement  si  les  deux  tuples  doivent  être  joints  ou  pas.  En 
effet, si la valeur obtenue correspondant à un bit 1, cela signifie quʹil peut exister un tuple de R1 
ayant la même valeur pour lʹattribut de jointure. Ce tuple est recherché en RAM ou sur disque. 
Lʹintérêt  de  cet  algorithme  est  que  la  lecture  des  tuples  ne  se  fait  que  pour    ceux  dont  on  est 
certain quʹils seront joints. Au contraire, si le bit est 0, alors le tuple de R2 peut être mis de côté 
ou ne pas être lu, car il ne peut être joint à aucun tuple de R1. 
Optimisation avec le SGBD Oracle 
Il y a au moins deux modes dʹoptimisation disponibles et spécifiés dans le fichier des paramètres 
INIT.ORA  :  OPTIMIZER_MODE  =  {RULE,  COST}.  Le  mode  RULE  repose  sur  le  calcul  dʹune 
pondération globale obtenue à partir dʹune table de poids pour chaque chemin dʹaccès que peut 
prendre en compte le noyau du SGBD. Avec les versions plus récentes (version 7 et suivantes), 
la  mode  par  coût  est  devenu  le  mode  par  défaut  et  le  nouvel  optimiseur  est  aussi  capable  de 
générer des plans dʹexécution performants avec ce mode. Les étapes sont le suivantes : 
1‐ génération dʹun ensemble de plans qui reposent sur les chemins dʹaccès; 
2‐ ordonnement des plans avec les statistiques obtenues par la commande ANALYZE; 
3‐ choix du plan dʹexécution qui a le moindre coût en ressource; 
 
Le plan d’exécution dʹune requête est accessible par lʹentremise de la table System.Plan_table 

 
Chapitre 11 Optimisation des requêtes   160

et la commande EXPLAIN PLAN. 
EXPLAIN PLAN set statement_id = ʹq1ʹ  
INTO [system.]Plan_table  FOR   
select * from Empl where age <= 30ʹ; 
 
Pour lire le plan dʹexécution généré il suffit de le lire dans la table Plan_table : 
SELECT operation, options, object_name 
FROM   system.Plan_table   WHERE statement_id = ʹq1ʹ;  
                             
Le plan affiché est lu de bas vers le haut. 
  
OPERATION  OBJECT_NAME   
SELECT STATEMENT     
Table access  Empl  by ROWID 
Index   Empl_age_idx  RANGE SCAN 
            Figure 11.13 
 
Lʹindex  sur  âge  est  utilisé  pour  trouver  le  premier  rowid  du  tuple  correspondant  à  30. 
L’opération qui est suivie dʹun parcours séquentiel dans lʹindex pour trouver les autres adresses 
rowid  qui  correspondent    aux  employés  dont  lʹâge  et  inférieur  ou  égal  à  30  (Range  Scan).  Pour 
chaque adresse (rowid ou rid) trouvée, il y a un accès direct au tuple correspondant. 
Par contre, une jointure donnera un plan dʹexécution comme celui‐ci :
SELECT U.site, C.noCont, C.fin  
FROM Usine U, Contrat C 
WHERE U.noUs = C.noUs;
 
Le plan affiché par la commande EXPLAIN PLAN sera le suivant:   
   
OPERATION  OBJECT_NAME OPTIONS 
SELECT     
STATEMENT 
NESTED LOOPS     
TABLE ACCESS  Contrat  FULL 
TABLE ACCESS  Usine  BY ROWID 
INDEX  Usine  UNIQUE SCAN 
          Figure 11.14 
 
Lʹindex  sur  la  relation  Usine  sera  utilisé  en  accédant  à  sa  première  entrée  et  avec  ce  premier 
rowid, le tuple correspondant dans la table Usine est consulté. Ensuite, pour ce tuple, il y a aura 
un balayage séquentiel de la relation Contrat.  
 
Pour que cette approche soit suivie, il faut que les statistiques aient été générées pour les tables, 
les index et les clusters utilisés par les requêtes. Si celles‐ci ne sont pas disponibles, le système 

 
Chapitre 11 Optimisation des requêtes   161

sera  forcé  dʹutiliser  une  approche  par  les  règles  (RULE  based).  Les  statistiques  sont  au  départ 
celles par défaut et elles sont mises à jour à chaque fois que la commande ANALYZE est lancée 
par le DBA. 
ANALYZE TABLE Ouvrier COMPUTE STATISTICS; 
ou  
ANALYZE TABLE Ouvrier ESTIMATE STATISTICS SAMPLE 3000 rows; 
 
Pour  obtenir  les  statistiques  produites,  notamment  le  nombre  de  tuples,  la  longueur  moyenne 
dʹun tuple, le nombre de valeurs distinctes pour chaque attribut, la plus grande valeur et la plus 
petite, il suffit de consulter les tables USER_TABLES, USER_TAB_COLUMNS, USER_INDEXES. 
 
Exemple :
SELECT *  
FROM USER_TAB_COLUMN                           
                                 
Lʹapproche  par  coût  est  modulée  par  un  paramètre  qui  est  le  but  recherché.  Deux  buts  sont 
possibles  :  ALL_ROWS  (le  défaut)  et  FIRST_ROWS.  Le  premier  but  sous‐tend  une  recherche 
pour  obtenir  tous  les  tuples  de  la  réponse  dans  le  meilleur  temps  possible.  Le  deuxième  but 
imposé au SGBD de retourner au moins un tuple dans le plus court délai possible.  
                               
Optimisation par les coûts 
Cette optimisation repose sur l’importance des écritures et des lectures prévisibles à partir des 
statistiques  rangées  dans  le  dictionnaire.  La  mise  à  jour  de  ces  statistiques  est  de  la 
responsabilité  du  DBA.  Plusieurs  paramètres  régissent  le  fonctionnement  ce  mode 
d’optimisation et doivent être précisés par l’administrateur :   
 
OPTIMIZER_MODE : RULE ou COST (le défaut si les statistiques sont disponibles) 
SORT_AREA_SIZE : taille de la mémoire disponible pour le tri 
HASH_AREA_SIZE : taille de la mémoire disponible pour le hachage 
HASH_JOIN_ENABLED : jointure par hachage autorisée 
 
La  collecte  des  statistiques  est  rendue  possible  par  l’exécution  de  l’utilitaire  ANALYSE  qui 
obtient les informations suivantes sur une table particulière de la base : 
 
Information sur une table  Description 
NUM_ROWS  Nombre de tuples 
BLOCKS  Nombre de pages sur disque 
EMPTY_BLOCKS  Nombre de pages libres sur disque 
AVG_SPACE  Espace moyen libre par page de données 
CHAIN_CNT  Nombre de pages chaînées 
AVG_ROW_LEN  Taille moyenne des tuples dans une table 
SAMPLE_SIZE  Taille de l’échantillon utilisé  
LAST_ANALYSED  Date de la dernière analyse sur une table 

 
Chapitre 11 Optimisation des requêtes   162

 
Ces informations sont conservées dans les tables du dictionnaire : DBA_TABLES, ALL_TABLES, 
USER_TABLES qui sont accessibles à l’administrateur ayant les privilèges appropriés. 
 
Les index peuvent aussi être analysés par l’utilitaire ANALYSE et les données obtenue stockées 
dans les tables DBA_INDEX, ALL_INDEX, USER_INDEX. 
 
Exemples : 
ANALYSE TABLE Ouvrier COMPUTE STATISTICS FOR TABLE ; 
ANALYSE TABLE Ouvrier COMPUTE FOR ALL INDEX ; 
ANALYSE INDEX Ouvrier_idx COMPUTE STATISTICS ; 
                                                                
Affichage du plan dʹexécution dʹune requête  
Le plan dʹune exécution optimisée peut être affiché par la commande suivante et cela avant que 
le plan soit exécuté :   
EXPLAIN PLAN INTO [system.]Plan_table FOR   
select * from Empl where age < 30; 
                                    
La  table  Plan_table  doit  être  au  préalable  créée  avec  un  schéma  imposé  par  le  SGBD.  Le  plan 
dʹexécution  de  la  clause  SQL  est  optimisé  et  rangé  dans  cette  table  où  il  peut  être  relu  par  un 
SELECT. 
 
Pour lire le plan dʹexécution de cette clause, il suffit donc de lire le tuple correspondant stocké 
dans la table Plan_table:
 
SELECT operation, object_name, search_columns 
FROM Plan_table; 
 
La réponse identifie les objets utilisés et le genre dʹaccès effectué par le système.
Schéma de la table Plan_table pour le système Oracle 
Le  schéma  de  la  table  Plan_table  est  créé  au  préalable  par  le  script  Oracle  UTLXPLAN.SQL. 
Lʹexécution de ce script permet la cération de la table SYSTEM.Plan_table dont la structure, i.e. 
son schéma, comprend obligatoirement les attributs ci‐dessous : 
 
Attribut    Sémantique 
Statement_id  varchar2(30)  identifiant de la clause SQL 
TIMESTAMP  date  estampille de lʹexécution du EXPLAIN 
REMARKS  varchar2(80)  commentaire 
OPERATION  varchar2(30)  nom de lʹopérateur exécuté 
OPTIONS  varchar2(30)  options utilisées par lʹopérateur 
OBJECT_NODE  varchar2(128)  nom du lien BD 
OBJEC_OWNER  varchar2(30)  propriétaire de la clause 
OBJECT_NAME  varchar2(30)  nom de lʹobjet 

 
Chapitre 11 Optimisation des requêtes   163

OBJECT_INSTANCE  numeric  no de lʹétape de lʹobjet 


OBJECT_TYPE  varchar2(30)   
OPTIMIZER  varchar2(255)   
SEARCH_COLUMNS  numeric  non utilisé 
ID  numeric  no de lʹétape du plan 
PARENT_ID  numeric  no de lʹétape 
POSITION  numeric   
OTHER  long  info additionnelle 
            Figure 11.15
 
Modification des paramètres de la session courante 
Pour modifier le but il suffira dʹutiliser la commande ALTER SESSION :  
                        
ALTER SESSION SET OPTIMIZER_GOAL = ALL_ROWS; 
ou encore insérer un hint (directive à lʹoptimiseur) dans la requête par lʹentremise de la chaîne 
/*+   hint   */. 
Exemple :  
SELECT   /*+ ALL_ROWS */ nom 
FROM Ouvrier; 
Ou encore : 
SELECT   /*+ FIRST_ROWS */ nom 
FROM Ouvrier; 
 
Le  calcul  dʹune  requête  est  accéléré  par  lʹintervention  de  lʹoptimiseur  qui  agit  au  niveau 
syntaxique  et  au  niveau  des  coûts  dʹexécution.  Cette  optimisation  est  généralement  réalisée 
automatiquement, bien que lʹutilisateur puisse fournir des directives (hints) à lʹoptimiseur pour 
diriger son action, soit en le forçant à utiliser un index, soit en bloquant son utilisation. De plus, 
lʹutilisateur  peut  forcer  lʹoptimiseur  à  agir  seulement  au  niveau  syntaxique,  et  vice  versa, 
seulement au niveau des coûts. Au delà de ces actions, lʹutilisateur peut aussi agir en optimisant 
la formulation de sa requête et de ce fait, en assistant lʹoptimiseur dans la recherche du meilleur 
plan dʹexécution. 
 
Mise au point des requêtes SQL 
L’utilisateur peut aussi lors de l’écriture d’une requête peut aider l’optimiseur en évitant ou en 
privilégiant certaines structures de clauses.  En effet, certaines écritures d’une clause bloque ou 
pas  lʹusage de certaines ressources comme par exemple les index. Lʹutilisateur doit penser à la 
mise  au  point  de  sa  requête  lorsque  les  statistiques  affichent  un  nombre  dʹaccès  disque  fort 
important  ou  que  le  plan  dʹexécution  lʹinforme  quʹun  index  existant  nʹest  point  utilisé. 
Cependant,  il  ne  sʹagit  pas  dʹune  étape  dans  le  processus  dʹoptimisation,  mais  davantage  le 
développement de meilleures façons de formuler des requêtes SQL dictées par l’expérience du 
DBA. 
Les exemples examinés par la suite sont formulés avec les relations de la base suivante : 
Ouvrier (nas*, nom, salaire, spec, noUs) 

 
Chapitre 11 Optimisation des requêtes   164

Usine (noUs*, site, prod, nasChef) 
Contrat(noCont*, fin, cout, noUs 
 
Les index suivants sont définis: 
• index regroupant (cluster index) sur les clés primaires : nas, noUs, noCont; 
• index non regroupant sur les attributs suivants : nom, salaire et spec de Ouvrier; 
• index non regroupant sur lʹattribut site de Usine; 
Validation de lʹusage des index 
 
Plusieurs optimiseurs de requêtes nʹutiliseront pas un index sur un attribut lorsque celui‐ci est 
dans une expression arithmétique. Lʹaffichage du plan dʹexécution par la commande EXPLAIN  
PLAN est mis à profit pour vérifier quels sont les index utilisés dans le calcul dʹune requête. 
SELECT nas nom 
FROM Ouvrier 
WHERE salaire/12 > 2800.00; 
 
Cependant  la  requête  légèrement  remaniée  comme  ci‐dessous  tirera  profit  de  lʹexistence  de 
lʹindex sur salaire. 
SELECT nas nom 
FROM Ouvrier 
WHERE salaire > 2800.00 * 12; 
 
De même lorsquʹun attribut est traité par une fonction de chaîne. 
SELECT nas nom 
FROM Ouvrier 
WHERE Subtr(nom, 1,1) = ʹDʹ; 
           
Cette requête ne peut pas cependant être modifiée afin de forcer lʹusage de lʹindex sur le nom. 
Elle sera donc calculée par un balayage séquentiel de la table Ouvrier. 
Une  comparaison  avec  une  constante  dʹun  autre  type  ou  dont  la  précision  est  différente. 
Supposons que lʹattribut salaire ait le type number (5,2), i.e. le type réel. Lʹindex sur salaire est 
construit avec le type réel pour les entrées. Une recherche avec le salaire comparé avec un entier 
risque au mieux dʹêtre calculée sans recours à lʹindex.   
                              
SELECT nas nom 
FROM Ouvrier 
WHERE salaire = 23000; ‐‐ type réel pour salaire   ‐‐ comparé avec un entier     
 
Toutefois,  une  légère  modification  de  la  requête  peut  permettre  lʹexploitation  de  lʹindex  sur 
salaire.  
SELECT nas nom 
FROM Ouvrier 
WHERE salaire > 22999.99 and salaire < 23000.00; 

 
Chapitre 11 Optimisation des requêtes   165

 
Finalement,  toute  comparaison  avec  l’indicateur  NULL  supprime  généralement  lʹusage  de 
lʹindex puisque cette valeur nʹest pas indexée.  
SELECT nas nom 
FROM Ouvrier 
WHERE salaire is NULL; 
 
Élimination du DISTINCT dans une requête 
En  effet,  une  requête  avec  SELECT  DISTINCT  induit  un  tri  des  tuples  pour  supprimer  les 
doublons.  Lorsquʹil  nʹest  pas  nécessaire  de  lʹutiliser,  il  faut  le  supprimer  de  la  requête.  Cette 
modification ne pourrait pas être faite par lʹoptimiseur, sauf dans le cas où il sʹagirait de trier sur 
une clé de relation.   
SELECT DISTINCT nas 
FROM Ouvrier 
WHERE spec = ʹsoudeurʹ;
 
Cette  requête  peut  engendrer  inutilement  un  tri  puisque  le  nas  est  une  clé  de  Ouvrier.  Elle 
devrait être modifiée ainsi :   
SELECT nas 
FROM Ouvrier 
WHERE spec = ʹsoudeurʹ; 
 
Si lʹintention de lʹusager était dʹavoir une réponse triée, il suffit alors dʹajouter le ORDER BY nas. 
Plus généralement, sʹil y a une clé parmi les attributs affichés, le DISTINCT nʹest pas nécessaire.   
Vérification du traitement dʹune jointure formulée avec une sous‐requête 
 
Quelques systèmes ne peuvent pas déceler une jointure dans la requête imbriquée suivante :  
SELECT nas 
FROM Ouvrier O 
WHERE O.noUs IN (SELECT noUs 
      FROM Usine U); 
 
Si  le  plan  dʹexécution  fournit  par  le  SGBD  ne  traite  pas  cette  requête  en  utilisant  lʹindex  sur 
noUs,  mais  effectue  le  calcul  de  la  sous‐requête  en  premier,  suivie  par  le  test  dʹinclusion  de 
noUs, il faudra transformer la requête en une jointure explicite : 
 
SELECT nas 
FROM Ouvrier O, Usine U 
WHERE O.noUs = U.noUs; 
 
Cette  requête  affichera  le  nas  de  lʹouvrier  qui  travaille  dans  une  usine.  Si  un  ouvrier  travaille 
dans  plusieurs  usines,  le  nas  sera  affiché  autant  de  fois.  La  requête  suivante  permet  dʹafficher 

 
Chapitre 11 Optimisation des requêtes   166

seulement  les  nas,  nom  et  spec  des  ouvriers  qui  sont  aussi  chef    dʹusine  à  lʹendroit  de  leur 
travail.
SELECT nas, nom, spec 
FROM Ouvrier 
WHERE nas IN (SELECT nasChef FROM Usine); 
 
La requête est transformée en jointure pour un calcul plus rapide : 
SELECT nas, nom, spec 
FROM Ouvrier O, Usine U 
WHERE O.noUs = U.noUs and nas = nasChef; 
 
Cas spécial 
Une requête plus complexe pourrait être plus performante avec lʹusage dʹune table temporaire. 
Par  exemple,  pour  trouver  les  ouvriers  les  moins  payés  par  département,  la  requête  SQL 
corrélée suivante peut être formulée : 
SELECT nas  
FROM Ouvrier O 
WHERE salaire = (SELECT Min(salaire) FROM Ouvrier O1 
      WHERE O.spec = O1.spec);     
  
Avec  une  telle  requête,  lʹusage  dʹune  table  temporaire  (Tempo)  simplifie  la  formulation  de  la 
jointure. 
SELECT min(salaire) as petitSal, spec INTO Tempo  
FROM Ouvrier 
GROUP BY spec; 
SELECT nas 
FROM Ouvrier O, Tempo T 
WHERE O.spec = T.spec and O.salaire = T.petitSal;  
 
Utilisation de la table temporaire  
Certains  systèmes,  notamment  les  implémentations  complètes  de  SQL‐92  permettront  de 
formuler une requête en plusieurs étapes et cela, sans faire appel à une vue relationnelle. Pour ce 
faire, il y aura création dʹune table temporaire dont le schéma est stocké dans le dictionnaire du 
SGBD.  
Ainsi pour trouver les ouvriers dont le salaire est égal à un salaire payé à un soudeur, on pourra 
exécuter la requête suivante : 
    SELECT  salaire into Tempo  ‐‐déjà créée 
  FROM Ouvrier 
  WHERE spec = ʹsoudeurʹ; 
 
SELECT nas, nom 
FROM Ouvrier 
WHERE salaire = (SELECT salaire FROM Tempo); 

 
Chapitre 11 Optimisation des requêtes   167

 
Les  requêtes  peuvent  être  réécrites  et  fusionnées  pour  éviter  de  faire  appel  à  une  table 
temporaire : 
SELECT nas, nom 
FROM Ouvrier 
WHERE salaire = (SELECT salaire FROM Ouvrier);   
         
Factorisation des tris  
Une  suite  de  requêtes  similaires  nécessitant  un  tri  par  le  ORDER  BY  peut  être  calculée  plus 
rapidement  si  les  tris  sont  réduits,  lorsque  cela  est  possible,    à  un  seul.  Par  exemple,  sʹil  faut 
trouver, en ordre croissant, les attributs nas et nom des ouvriers dont le salaire est compris dans 
une  plage  de  valeurs.  Cette  requête  sera  formulée  autant  de  fois  quʹil  y  aura  de  plages  de 
valeurs.   
SELECT nas, nom 
FROM Ouvrier  
WHERE salaire >20000.00 and salaire < 45000.00 
ORDER BY nas;  
            
Pour calculer cette requête, lʹindex sur le salaire pourrait ne pas être utilisé en raison de la plage 
de  valeurs  pour  lʹattribut  salaire  dont  la  sélectivité  est  faible.  Lʹextension  de  Ouvrier    sera  de 
préférence  balayée  entièrement  et  un  tri  sera  fait  sur  les  tuples  sélectionnés.  Un  tel  tri  coûteux 
pourrait  être  effectué  pour  chaque  question  similaire.  Pour  éviter  ce  tri  coûteux,  il  suffira  de 
créer une table temporaire ou encore mieux une vue matérialisée :     
SELECT nas, nom INTO Tempo   FROM Ouvrier  
WHERE salaire >10000.oo and salaire < 99999.00 
ORDER BY nas; 
 
La  table  Tempo  est  construite  avec  tous  les  tuples  ordonnés  sur  le  nas  des  ouvriers  compris 
entre les deux bornes de salaire qui ont été choisies à dessein pour inclure tous les ouvriers.  Les 
requêtes subséquentes pour afficher, en ordre, les ouvriers dont le salaire est compris dans une 
plage de valeurs seront maintenant formulées sans tri sur la table Tempo.    
 
SELECT nas, nom 
FROM Tempo  
WHERE salaire >20000.00 And salaire < 45000.00; 
 
Avec  une  telle  approche,  un  seul  tri  est  exécuté  sur  les  tuples    stockés  en  ordre  dans  la  table 
Tempo.  Toutefois,  comme  il  sʹagit  ici  dʹune  table  temporaire,  son  extension  nʹest  pas 
automatiquement mise à jour pour refléter les transactions en cours sur la base de données. Les 
requêtes  ci‐dessus  devront  être  exécutées  sur  une  période  relativement  courte  pour  avoir  des 
réponses  presque  consistantes.  Une  autre  façon  pour  éviter  ce  problème  est  dʹutiliser  de 
préférence  un  snapshot  qui  peut  être  mis  à  jour  automatiquement  et  donc,  être  en  phase  par 
rapport à lʹétat des tables de base.   

 
Chapitre 11 Optimisation des requêtes   168

 
Simplification des clauses SQL incluant un HAVING 
 Les requêtes sont parfois formulées pour obtenir une réponse correcte, sans le souci dʹobtenir la 
clause  la  plus  simple.  Par  exemple,  la  clause  suivante  donne  le  plus  petit  salaire  payé  pour  la 
spécialité ʹsoudeurʹ. 
SELECT Min(salaire) as min_sal, spec 
FROM Ouvrier 
GROUP BY spec 
HAVING spec = ʹsoudeurʹ; 
      
Cette requête ainsi formulée peut induire un tri qui nʹest pas esentiel pour le calcul de réponse. 
Par contre, en modifiant la requête pour avoir la forme suivante :   
 
SELECT min(salaire) as min_sal, spec 
FROM Ouvrier 
WHERE spec = ʹsoudeurʹ; 
La réponse est identique, sans avoir recours à un  tri.  
 
Traitement du connecteur logique OU  
Avec une requête utilisant un prédicat disjonctif, il faut vérifier si le SGBD utilisé exploite bien 
les index présents. Pour cela, il faut afficher le plan dʹexécution de la requête.  
 
SELECT nas 
FROM Ouvrier 
WHERE nom = ʹLangloisʹ or spec = ʹsoudeurʹ; 
 
Si  le  plan  dʹexécution  exploite  les  index  existants  sur  le  nom  et  la  spécialité,  cette  requête  sera 
performante. Au contraire, si les index ne sont pas utilisés, il faut alors supprimer le connecteur 
logique OU et reformuler la requête avec lʹopérateur UNION. La nouvelle requête deviendra :   
SELECT nas 
FROM Ouvrier 
WHERE nom = ʹLangloisʹ  
UNION 
SELECT nas 
FROM Ouvrier 
WHERE spec = ʹsoudeurʹ; 
 
Cette réécriture peut être avantageuse lorsquʹil  y a un index sur les deux attributs  du prédicat 
disjonctif. 
 
Ordre des tables dans la clause FROM 
Dans  certains  SGBD  lʹordre  des  tables  dans  la  clause  FROM  est  significatif.  Par  exemple,  un 
SGBD pourra utiliser un index avec la première table du FROM et balayer la seconde. Si ceci est 

 
Chapitre 11 Optimisation des requêtes   169

confirmé par le plan dʹexécution, il est plus performant alors de balayer la plus petite extension, 
donc de la placer obligatoirement en deuxième place dans le FROM.    
 
Vue relationnelle comme source de ralentissement 
Une vue est une clause SQL placée dans le dictionnaire et qui est soit matérialisée ou fusionnée 
dans  une  requête.  La  première  approche  est  exceptionnelle  puisque  quʹelle  est  plus  lourde  à 
implémenter  en  terme  de  calcul.  La  deuxième  est  celle  généralement  utilisée  par  plusieurs 
logiciels SGBD. 
Par exemple, soit la vue suivante : 
CREATE VIEW  OuvrierQc AS 
SELECT nas, nom, prod 
FROM Ouvier O, Usine U 
WHERE O.noUs = U.noUs  and site = ʹQuébecʹ;
  
Cette vue fournit les nas, nom et production des usines où travaille chaque ouvrier. Accessible 
aux utilisateurs, elle peut engendrer inutilement des requêtes complexes.     
SELECT spec 
FROM OuvrierQc 
WHERE nas = ʹ3456ʹ; 
 
Avec sa fusion avec la vue relationnelle, cette requête devient la jointure suivante :    
SELECT spec 
FROM Ouvrier O, Usine U 
WHERE O.noUs = U.noUs And  
site = ʹQuébecʹ and nas = ʹ3456ʹ; 
 
Cette jointure fournit la réponse attendue. Elle peut être aussi obtenue par une formulation plus 
simple  en    exploitant  le  fait  que  lʹattribut  specialité  est  inclus  dans  le  schéma  de  la  relation 
Ouvrier. 
SELECT spec 
FROM Ouvrier 
WHERE nas = ʹ3456ʹ and noUs = ʹQuébecʹ; 
 
Mode d’optimisation de Oracle et syntaxe des clauses SQL 
L’optimiseur  du  SGBD  Oracle  recourt  à  des  algorithmes  d’optimisation  dont  le  comportement 
externe est généralement prévisible à partir de la syntaxe de la clause SQL (rule‐based), sauf pour 
l’optimisation  basée  sur  le  coût  (cost‐based)  qui  sʹappuie  sur  des  données  internes    comme  la 
taille des tuples, le nombre de tuples, la sélectivité des attributs et lʹéparpillement des clés pour 
établir un plan dʹexécution optimisé. Lʹapproche par les règles établit un plan dʹexécution sur la 
base de la clause SQL, et le plan est formulé en tenant compte de certaines règles dʹordonnement 
présentées dans les pages suivantes. Cependant, il est possible dʹinfluencer le comportement de 
lʹoptimiseur syntaxique par la formulation de la clause SQL.  
 

 
Chapitre 11 Optimisation des requêtes   170

Exemples : les index  sur le noEmpl et celui sur le tauxH ne seront pas exploités. 
  SELECT * 
  FROM Empl 
  WHERE noEmpl like ‘%345Y’; 
 
SELECT * 
  FROM Empl 
  WHERE tauxH * .33 > 24.25 ; 
 
À noter que dans la requête qui précède, un index sur l’attribut tauxH ne serait pas très utile, car 
toutes les valeurs de tauxH  doivent être lues  et transformées afin dʹeffectuer la comparaison du 
prédicat. 
   
De  même,  il  est  aussi  difficile  de  tirer  profit  de  lʹindex  sur  le  nom  dans  la  clause  ci‐dessous, 
lorsque ce dernier est inclus dans une expression de chaîne : 
  SELECT * 
FROM Empl 
WHERE Substr(Empl.nom,5,1) = ʹSʹ; 
 
Dans  certains  exemples  ci‐dessus,  il  est  possible  de  transformer  la  requête  en  une  autre 
équivalente qui ne bloquerait pas lʹusage de l’index.  
 
Mise à jour des index 
Malgré  leur  intérêt  pour  la  recherche,  la  multiplicité  des  index  présentent  quelques 
inconvénients  pour  les  opérations  de  mise  à  jour  et  de  suppression.  Cela  peut  donner  lieu  à 
divers phénomènes de ralentissement : 
‐  un  ralentissement  suite  à  la  mise  à  jour  des  entrées  de  tous  les  index  qui  font  référence  à 
lʹattribut de la table modifiée. Il y aura éventuellement, un rééquilibrage du B‐arbre; 
 
‐  une  occupation  trop  importante  par  les  index,  de  lʹespace  sur  disque  et  dans  les  pages  de  la 
ZMP; 
                                     
‐ une augmentation du taux de remplacement des pages (page swapping) en raison de la présence 
des index, ce qui peut ralentir la recherche. 
 
Par  ailleurs,  l’opération  de  suppression  (DELETE)  des  tuples  se  prolonge  par  une  suppression 
de l’entrée correspondante dans lʹindex, ce qui ralentit celle qui concerne les tuples. L’opération 
d’insertion est aussi pénalisée par la mise à jour de l’index, même si l’insertion ne sous‐tend pas 
de recherche dans une table. Mais, tout ajout dans une table indexée entraîne la mise à jour de 
l’index. 
 
Autre éléments de la clause SQL qui influencent le module optimiseur : 
‐ Distinct dans le SELECT    

 
Chapitre 11 Optimisation des requêtes   171

Le  traitement  de  la  clause  distinct  est  fait  par  un  tri.  La  suppression  des  doublets  est  alors 
réalisée, à la fin du calcul, lors de l’ordonnement des tuples de la réponse. Avec le distinct, les 
index  ne  sont  pas  utilisés  même  s’ils  existent  et  le  SGBD  fait  un  balayage  complet.  Le  tri  est 
fourni par le SGBD et s’effectue dans un espace temporaire défini pour le système. 
 
‐ GROUP BY 
Cette clause sous‐tend un tri interne dont le critère de classement inclut obligatoirement tous les 
attributs  du  SELECT,  sauf  ceux  utilisés  comme  argument  dans  une  fonction.  Aucun  autre 
attribut n’apparaît dans le SELECT.  
 
Par exemple, on aura : 
  SELECT ville, Avg(age)  
  FROM Empl 
  WHERE metier = ʹelectricienʹ 
  GROUP BY ville ; 
 
‐ Traitement de la sous‐requête ( bloc  de niveau 2) 
Celle‐ci est généralement transformée en jointure et optimisée sous cette forme. 
  SELECT *  
  FROM Ventes 
  WHERE Ventes.article  IN (SELECT FROM Pieces_inv  
          WHERE article = ʹa2ʹ or article = ʹa3ʹ;
Dans  cet  exemple,  la  requête  imbriquée  est  transformée  en  jointure  simple  pour  obtenir 
l’expression suivante qui est par la suite optimisée pour tirer profit des index. 
  SELECT *  
  FROM Ventes as V, Inventaire as I 
  WHERE V.article  = I.article and  
        (V.article = ʹa2ʹ or V.article= ʹa3ʹ); 
 
‐ Transformation du prédicat dʹinclusion en un ensemble de constantes littérales. 
Le prédicat dʹinclusion est parfois transformée par l’optimiseur en une jointure. Par exemple, la 
requête ci‐dessous, il y aura modification du avec prédicat d’inclusion pour exécuter plutôt une 
jointure naturelle :                      
  SELECT * 
  FROM Empl 
  WHERE Empl.nom in  
      (ʹGagnonʹ,ʹMathieuʹ,ʹValoisʹ, ʹAudyʹ); 
 
Après transformation, la sélection est devenue une autojointure :     
  SELECT *  
  FROM  Empl X, Empl Y 
  WHERE X.no_empl = Y.no_empl and 
    (X.nom = ‘Gagnon’ or X.nom=’Mathieu’ X.nom=’Valois’  or X.nom = ʹAudyʹ); 
 

 
Chapitre 11 Optimisation des requêtes   172

La jointure remplace le test dʹinclusion. 
‐ Chemin d’accès unique 
S’il y a un chemin d’accès unique, c’est‐à‐dire qui sous‐tend l’accès à un seul tuple d’une table 
de  base,  il  est  pris  en  compte  en  premier  lieu.  Par  exemple,  ce  sera  le  cas  si  la  requête  utilise 
explicitement ou implicitement : 
a) un index du type unique pour un attribut; le rowid est fourni par la recherche dans lʹindex; 
b) une référence à une valeur unique dans un ensemble : valeur de clé; 
c) une valeur particulière de ROWID dans le WHERE. 
                               
‐ Traitement de la négation : NOT 
L’index  n’est  pas  utilisé  pour  traiter  le  NOT  et  le !=    au    regard  d’une  valeur  simple.  Par 
exemple, on aura :  
  SELECT * FROM Ventes WHERE article != ʹa5ʹ ; 
 
Pour  traiter  certains  cas  de  négation,  il  est  plus  rapide  pour  l’optimiseur  d’éviter  le  balayage 
séquentiel  d’une  table  en  effectuant,  lorsque  c’est  possible,  les  transformations  automatiques 
suivantes de l’opérateur de négation : 
!>  sera transformé en <=  !>= sera transformé en < 
!<  sera transformé en >=  !<= sera transformé en >     
 
‐ Optimisation du connecteur logique OR 
Les index ne sont utilisés que si les attributs du OR dans le WHERE sont tous indexés. Si ce n’est 
pas  le  cas,  il  faut  placer  l’attribut  le  plus  spécifique  (sélectivité  sj  )  en  première  position  de  la 
cascade logique.  
Voici un exemple : 
  SELECT * 
  FROM Ventes 
  WHERE nom =ʹTedʹ OR  nom =ʹValérieʹ OR article = ʹa1ʹ ; 
 
Si le nom est indexé, mais pas l’article, alors l’index n’est pas très utile, puisque la table doit être 
parcourue entièrement pour les articles. 
 
‐ Clause order by 
Après une sélection, les tuples sont triés par une procédure de tri incorporée au système Oracle. 
L’argument  du  tri  peut  comporter  plusieurs  attributs  et  se  faire  en  ordre  descendant  ou 
ascendant. 
Traitement de la jointure par Oracle 
De façon générale, le calcul d’une jointure sera accéléré si les attributs de jointure sont indexés. Il 
faut faire attention, cependant aux structures syntaxiques qui annulent l’usage des index comme 
par exemple un prédicat avec une fonction : 
 
a)  Jointure  sans  attribut  indexé  :  si  aucun  attribut  indexé  n’est  disponible  faire  la  jointure,  le 
calcul est fait avec un tri dynamique lancé par l’optimiseur; 

 
Chapitre 11 Optimisation des requêtes   173

 
b) Jointure avec index : s’il n’y a qu’un seul index de jointure disponible, la table non indexée est 
celle qui est balayée en premier et doit pour cela apparaître en première position dans la clause 
FROM. S’il y a un index sur les deux attributs de jointure, le noyau détermine le chemin d’accès 
selon une pondération qui s’appuie sur l’indice de sélectivité des attributs de jointure (méthode 
heuristique). 
 
Il est possible de spécifier à l’optimiseur de privilégier certaines règles ou lʹapproche par le coût, 
et  cela,  au  moyen  des  indices  (hints)  inclus  sous  forme  de  commentaire  après  le  SELECT, 
DELETE et INSERT. La directive à lʹoptimiseur est incluse dans la requête : 
 
SELECT  /*+ COST  */ nom, ville   
FROM  Empl   
WHERE age > 25 and ville in (ʹQuebecʹ, ʹMontrealʹ, ʹTrois‐Rivieresʹ, ʹBostonʹ); 
 
Cette clause est optimisée avec lʹapproche par coût, même si le plan possible avec lʹapproche par 
les règles était plus performant.  
Sachant  quʹune  extension  sera  balayée  entièrement  pour  répondre  à  une  question,  il  sera  plus 
performant  dʹéviter  lʹusage  des  index  qui  prennent  de  la  place  en  mémoire  et  qui  doivent  être 
parcourus.  Pour  cela,  une    information  peut  être  transmise  à  lʹoptimiseur  à  savoir  de  faire  un 
balayage complet de la table Empl et dʹignorer  tout index sur âge ou sur ville.  
 
SELECT  /*+  FULL (Empl) */ nom, ville   
FROM  Empl   
WHERE age > 25 and ville in (ʹQuebecʹ, ʹMontrealʹ, ʹTrois‐Rivieresʹ, ʹBostonʹ); 
 
Conclusion 
Les  SGBD  de  bon  niveau  sont  dotés  dʹun  optimiseur  efficace  qui  permet  de  transformer  les 
requêtes  SQL  pour  générer  un  plan  dʹexécution  optimal  et  équivalent  à  la  requête  source.  Les 
transformations  de  la  requête  initiale  dépendent  dʹun  grand  nombre  de  paramètres  que  les 
modules dʹoptimisation exploitent différemment dʹun système à lʹautre. La règle de pratique est 
que la mise au point dʹune requête particulièrement lourde devrait tirer profit de lʹaccessibilité 
au plan dʹexécution. En effet, lʹexamen du plan dʹexécution généré par la commande EXPLAIN 
PLAN  peut  découvrir  lʹusage  ou  le  non  usage  des  index  et  permettre  ainsi  au  développeur  de 
réagir  soit  en  créant  un  index  approprié,  soit  en  fournissant  des  directives  particulières  à 
lʹoptimiseur pour accélérer le traitement. Finalement, malgré un module dʹoptimisation évolué 
et  performant,  ce  dernier  ne  peut  pas  toujours  supplanter  l’expérience  de  programmation.  Le 
développeur a donc toujours un rôle important à jouer dans la mise en forme des requêtes SQL 
externes. 
 

 
Chapitre 11 Optimisation des requêtes   175

 
INDEX 
 
Dépendance de clé, 122 
A  Dépendance de jointure (DJ), 114 
Algorithme de décomposition, 72  Dépendance fonctionnelle, 51 
ANALYZE, 153  Dépendance fonctionnelle totale, 52 
API, 48  dépendance multiple, 100 
appartenance dʹune DMV, 105  Dépendances multivaleurs, 92 
appartenance.i.Problème de lʹappartenance;  déterminant, 57 
à une fermeture;, 66  Deuxième forme normale, 77 
Attribut redondant, 67  DF, 52 
Axiomes dʹArmstrong, 60  DIN, 74 
Distinct, 170 
B  DISTINCT, 165 
DJ, 114 
Base de dépendance, 104 
DMUT, 111 
Base de dépendances, 71 
DMV, 96 
boucles imbriquées, 156 
DMV, 95 
C  DMV élémentaire, 104 
DMV triviale, 99 
cascade de sélections, 141  DMV triviale.i.DMV triviale, 99 
Chemin d’accès  unique;, 172  DMV.i.DMV, 96 
Cohérence par 2, 107  driver ), 49 
COMMIT WORK, 18 
Commutativité des sélections, 142  E 
Conséquence logique, 61 
Équivalence des relations, 54 
Conservation des DF, 81 
Exercices, 123 
Conservation des données, 73 
Exercices, 132 
Contrainte d’intégrité, 52 
Exercices résolus, 124 
Contraintes dʹinclusion (DIN), 74 
EXPLAIN PLAN, 162 
Couverture de F, 64 
couverture minimale, 66 

curseur explicite, 27 
Curseur implicite, 22  Factorisation des tris, 167 
fermeture, 64 
D  Fermeture, 62, 104 
fermeture dʹun attribut;, 63 
Décomposition de Fagin, 99 
Fermeture par les DMV, 101 
Décomposition dʹune relation, 55 
Fermeture.i.Fermeture, 62 
Dénormalisation, 88 
FN1, 76 
Dénormalisation, 90 
FN2, 77 
Dénormalisation.i.Dénormalisation, 88 
FN3, 78, 80 
Dépendance de clé, 115 
FN4, 102 

 
Chapitre 11 Optimisation des requêtes   176

FN5, 115, 123  Optimisation du schéma, 88 
FNBC, 80, 81  Optimiseur Oracle, 169 
FOR UPDATE, 25  Ordre des tables, 168 
Forme FNBC.i.FNBC, 80 
Forme normale FN4.i.FN4, 102  P 
Forme normale FN5, 115  Plan dʹexécution :, 156 
Formes normales, 75  Plan dʹexécution:, 154 
Plan dʹexécution; de cette requête, 89 

Précompilation, 15 
Généralisation de DMUT, 115  Première forme normale, 76 
GET DESCRIPTOR, 38  Problème de lʹappartenance; à une 
GROUP BY, 171  fermeture;, 66 
Projection, 152 
H  projections dans une cascade, 144 
Heuristique générale, 149  Propriétés de la DMUT, 113 
heuristique générale dʹoptimisation, 137  propriétés des DF, 56, 60 
Propriétés des DMV, 100 


Indicateur de variable, 25 
Interface API, 47  Recherche des clés, 67 
ISO SQL‐86, 17  relation universelle, 69 
RUNSTATS (DB2‐V2), 153 


jointure, 172 
Jointure, 153  SAG, 48 
Jointure complète dʹordre n, 106  Schéma cyclique, 107 
Jointure par hashing, 157  Sélection, 152 
jointure par tri‐fusion, 157  sélection avec index, 155 
Jointure sans clustering, 159  sélection sans index, 154 
Jointure sans index, 156  sous‐requête, 171 
jointures interdites, 119  SQL autonome, 7 
SQL dynamique, 30 
M  SQL intégré, 15 
SQLCA, 12 
Modèle de coût, 151 
SQLCA, 11 
N  SQLCODE, 9 
SQLSTATE, 10 
Normalisation, 53  synthèse de Bernstein, 84 
O  T 
ODBC, 48, 49  Test de DJ, 116 
ODBC, 49  Test de FNBC, 82 
Optimisation, 159 

 
Chapitre 11 Optimisation des requêtes   177

Test de FNBC, 82  U 
Test de la DJ, 122 
UPDATE STATISTICS, 153 
Théorème de Heath, 56 
USER CURSOR, 22 
Théorème de la fermeture, 66 
Traitement  ,des exceptions et mode de  V 
fonctionnement du SGBD, 9 
Variable hôte, 8 
Traitement du NOT, 172 
visibilité du curseur, 24 
Troisième forme normale, 78 
vue, 169 
Typologie des requêtes, 138 

Zone DIAGNOSTICS (mode AINSI), 10 

 
Chapitre 11 Optimisation des requêtes   179

 
                                                      
 
Références chapitre 9    
 
1 Programmer’s Guide to the Oracle Precompiler, version 1.5, ISBN 5315‐15‐1292 

2 NAREE, Christian, LEDANT Guy, SQL 2 Initiation à la Programmation, Armand Colin, 1994, 

ISBN 2‐200‐21411‐1 
3  LORIE,  Raymond  L.,  DAUDENARDE  Jean‐Jacques,  SQL  and  its  Applications,  Prentice  Hall 

1991, ISBN 0‐13‐837956‐4 
 
 

Références chapitre 10  
 
4  CODD,  E.  F.,  Normalized  Data  Base  Structure:  A  Brief  Survey,  Proceedings  of  1971  SIGFIDET  a 

Workshop on Data Description, Access, and Control, CA, USA, 1971. 
5  BEERI,  C.,  BERNSTEIN,  P.  A.,  GOODMAN,  N.,  A  sophisticate’s  introduction  to  database 

normalization theory , Proceedings of International Conference on Very Large Databases, 1980, p. 
126‐136. 
6  HEATH,  I.  J.,Unacceptable  File  Operations  in  a  Relational  Database,  Proceedings  of  the  ACM 

SIGFIDET, 1971. 
7  ARMSTRONG,  W.  W.,Dependency  Structures  of  Database  Relationships ,  Proceedings  of  the 

International Federation of Information Processing Societies (IFIP), 1974, p. 580‐583. 
8  ULLMAN,  J.,  Principles of Database and Knowledge Base Systems, Computer Science Press, 1988, 

p. 380. 
9  BEERI,  C.,  BERNSTEIN,  P.A.,  Computational  Problems  Related  to  the  Design  of  Normal  Form 

Relational Schemas , ACM Transactions on Database System, v. 4, no 1, 1979, p. 30‐59. 
 NUMMENMAA, J., TANISCH, P., Yet Another Note on Minimal Covers , SIGMOD Record, v. 19, 
10

no 3, 1990, p.30. 
 MAIER, D., The Theory of Relational Databases, Computer Science Press, 1983. 
11

 DIEDERICH, J., M., J., New methods and Fast Algorithms for Database Normalization ,  Transaction 
12

on Databases, v. 13, no 3, Sept 1988, p. 339‐365. 
13 MANNILA, H., RAIHA, K.‐J.,The Design of Relational Databases, Addison‐Wesley, 1992, ISBN 0‐
201‐56523‐4. 
14  DUTKA,  A.  F.,  HANSON,  H.  H.,  Fundamentals  of  Data  Normalization,  Addison‐Wesley,  1989, 

ISBN 201‐06645‐9. 
15 GARDARIN, Georges, Les systèmes et leurs langages,  Eyrolles, 1983. 

16  BERNSTEIN,  P.  A.,  Synthesizing  Third  Normal  Form  Relations  FROM    Functional 

Dependencies, ACM Trans. on Database Systems, v. 1, no 4, 1976. 
 
  SMINE,  H.,  ORACLE,  Architecture,  administration  et  optimisation,  Eyrolles,  1993,  ISBN  2‐212‐
17

08016‐6, 284 p. 

 
Chapitre 11 Optimisation des requêtes   180

                                                                                                                                                                            
18  MOULIN,  Bernard,  Méthodologie  EPAS,  Département  d’Informatique,  Université  Laval,  1990, 

p. 190. 
19  ABITEBOUL,  Serge,  HULL,  Richard,  VIANU,  Victor,  Foundations  of  Databases,  Addison‐

Wesley, 1995, ISBN‐0‐201‐53771‐0. 
20    DATE  C.J.,  FAGIN  R.,  Simple  Conditions  for  Guaranteeing  Higher  Normal  Forms  in  Relational 

Databases, ACM Transactions on Database v. 17, no 3, Sept 1992, pp. 465‐476.           
  
Références chapitre 11 
 
21  SMITH  J.M.,  CHANG  P.Y.,  Optimizing  the  performance  of  a  Relational  Algebra  Database 

Interface, CACM v. 18, no 10, pp. 68‐79, 1975 
22  FREYTAG  J.C.,  A  Rule‐based  View  of  Query  Optimization,  ACM  SIGMOD  International 

Conference San Francisco, 1987 
23  SHASHA,  Denis,  Database  Tuning;  A  Principled  Approach,  Prentice  Hall,  1992,  ISBN  0‐13‐

205246‐6 
24  GRAEFE  G.,  DeWITT  D.,  The  EXODUS    Optimizer  Generator,  ACM  SIGMOD  International 

Conference, San Francisco, 1987 
25  BABB  E.,  Implementing  a  relational  Database  by  means  of  Specialized  Hardware,  ACM 

Transaction on Data System, v. 4, no 1, 1979. 
 
 

Вам также может понравиться