Академический Документы
Профессиональный Документы
Культура Документы
Architectures, modèles et Langages de Données
Ingénierie des
bases de données
Hypercube
c,d
OLAP
Fascicule 4
© 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
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
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
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
Chapitre 9 Interface applicative 21
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
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
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
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.
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
Chapitre 9 Interface applicative 42
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
Chapitre 9 Interface applicative 45
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 */
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
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
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)
Chapitre 10 Théorie de la normalisation 80
Chapitre 10 Théorie de la normalisation 81
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
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))
T
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
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
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
1
6
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
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
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
1
1
0
1
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
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
F
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
G
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
I
R
Indicateur de variable, 25
Interface API, 47 Recherche des clés, 67
ISO SQL‐86, 17 relation universelle, 69
RUNSTATS (DB2‐V2), 153
J
S
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
Z
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.