2 - Maximilien Charlier

publicité
Université de Mons
Faculté des sciences
Département d’Informatique
Service des Systèmes d’Information
Réalisation d’un outil pour l’analyse
et la normalisation de bases de données
Auteurs :
Jannou Brohée
Julien Delplanque
Année académique 2013-2014
Table des matières
1
2
3
4
5
6
7
8
Introduction . . . . . . . . . . . . . . . . . . . . . . . . .
1.1
But du projet . . . . . . . . . . . . . . . . . . . .
1.2
Aperçu rapide du résultat . . . . . . . . . . . . .
Présentation générale de l’outil . . . . . . . . . . . . . .
2.1
Aperçu de l’organisation projet . . . . . . . . . .
2.2
Lecture/écriture des dépendances fonctionnelles .
2.3
Vérification des dépendances fonctionnelles . . .
2.4
Identification des clés/superclés . . . . . . . . . .
2.5
Vérification des normes 3NF & BCNF . . . . . .
2.6
Interface utilisateur . . . . . . . . . . . . . . . . .
Gestion des sauvegardes . . . . . . . . . . . . . . . . . .
Gestion du paramétrage de l’application . . . . . . . . .
Description des notions théoriques et algorithmes utilisés
5.1
Les notions théoriques . . . . . . . . . . . . . . .
5.2
Les algorithmes . . . . . . . . . . . . . . . . . . .
Problèmes rencontrés et solutions . . . . . . . . . . . . .
Répartition du travail . . . . . . . . . . . . . . . . . . .
Sources et remerciements . . . . . . . . . . . . . . . . .
1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2
2
2
3
3
3
4
4
4
4
5
6
7
7
8
11
11
11
1
1.1
Introduction
But du projet
Le but principal de ce projet est d’appliquer les notions vues aux cours théoriques et pratiques
(MySQL) de Base de Données I. L’outil développé permet d’effectuer diverses opérations sur
les dépendances fonctionnelles présentes dans une base de données analysée. Parmi celles-ci,
proposer à l’utilisateur d’ajouter des dépendances fonctionnelles, les modifier voir les supprimer,
vérifier si celles-ci sont vérifiées, vérifier si un schéma est en 3NF ou en BCNF ou encore exporter
un schéma en 3NF.
1.2
Aperçu rapide du résultat
Le bouton "Select table" permet d’afficher les dépendances fonctionnelles, les attributs, les
superclés et les clés de la table selectionnée dans la liste Tables.
Le label "3NF" est en vert et "BCNF" est en rouge car la table selectionnée respecte le
norme 3NF mais pas la norme BCNF. La couleur de ces labels donne donc comme information
le respect ou non des normes 3NF et BCNF.
2
2
Présentation générale de l’outil
2.1
Aperçu de l’organisation projet
Le projet est consitué de cinq couches listées ci-dessous dans l’ordre de leurs importances :
• Lecture/écriture des dépendances fonctionnelles
· Lire la relation FuncDep.
· Ajouter des dépendances fonctionnelles à la relation FuncDep.
· Supprimer des dépendances fonctionnelles de la relation FuncDep.
· Modifier des dépendances fonctionnelles de la relation FuncDep.
• Vérification des dépendances fonctionnelles
· Détecter les dépendances fonctionnelles non satisfaites.
· Distinguer les dépendances fonctionnelles qui sont une conséquence logique d’autres
dépendances fonctionnelles.
· Supprimer les dépendances fonctionnelles inutiles, incohérentes, non satisfaites ou
concernant des relations ou des attributs n’existant pas.
• Identification des clés/superclés
· Identifier les clés.
· Identifier les superclés.
• Vérification des normes 3NF & BCNF
· Vérifier si une relation est en 3NF.
· Vérifier si une relation est en BCNF.
· Exporter une base de données au format 3NF.
• Interface utilisateur
· Interface graphique.
2.2
Lecture/écriture des dépendances fonctionnelles
Cette couche est la première qui a été implémentée. C’est le noyau de l’application, elle
contient les fonctions basiques indispensables à toutes les couches supérieures. Voici la liste
exhaustive de celles-ci :
• La classe Funcdep : Cette classe représente une dépendance fonctionnelle. Elle contient
des outils basiques pour manipuler une dépendance fonctionnelle.
· Surcharge de la méthode __init__ : À l’initialisation, trois variables sont créées,
self.table : le nom de la table concernée par la dépendance fontionnelle, self.lhs et
self.rhs : respectivement les attributs de gauche et droite de la dépendance fonctionnelle.
· Surcharge de la méthode __str__ : Une dépendance fonctionelle est représentée
sous forme de string comme "nom_table attribut1 , ..., attributn ->attribut1 , ..., attributn ".
· Surcharge de la méthode __eq__ : Deux dépendances fonctionnelles sont égales
ssi leurs noms de tables sont égaux et leurs attributs de gauche et droite sont égaux
deux à deux.
· La méthode generate_unique : Cette méthode génère les dépendances fonctionnelles singulières à partir d’une dépendance fonctionnelle quelconque.
· La méthode format_db : Cette méthode formate la dépendance fonctionelle afin de
pouvoir introduire le résultat de cette méthode directement dans une requête SQL.
• La classe FdReader :
· La méthode get_table_list : Cette méthode retourne la liste des tables présentes
dans la base de données.
3
· La méthode get_attribute_list : Cette méthode retourne les attributs de la table
dont le nom est passé en paramètre.
· La méthode add_functional_dependency : Cette méthode ajoute la dépendance
fonctionnelle passée en paramètre à la table dont le nom est passé en paramètre.
· La méthode remove_functional_dependency : Cette méthode supprime la dépendance fonctionnelle passée en paramètre de la table dont le nom est passé en
paramètre.
· La méthode modify_functional_dependency : Cette méthode remplace la dépendance fonctionnelle passée en paramètre de la table dont le nom est passé en
paramètre par la seconde dépendance fonctionnelle passée en paramètre.
· La méthode get_functional_dependencies : Cette méthode retourne toutes les
dépendances fonctionnelles de la table dont le nom est passé en paramètre.
2.3
Vérification des dépendances fonctionnelles
• La classe FdReader :
· La méthode is_satisfied : Retourne vrai si la dépendance fonctionnelle passée en
paramètre est vérifiée, faux sinon.
· La méthode get_not_satisfied : Retourne les tuples qui ne satisfont pas la dépendance fonctionnelle passée en paramètre.
· La méthode closure : Calcule la fermeture pour les dépendances fonctionnelles de la
table dont le nom est passé en paramètre.
· La méthode linear_closure : Idem que closure mais avec une complexité linéaire.
· La méthode member : Retourne vrai si la dépendance fonctionnelle passée en paramètre est une conséquence logique des dépendances fonctionnelles de la table concernée.
2.4
Identification des clés/superclés
• La classe FdReader :
· La méthode get_superkeys : Retourne les superclés d’une table dont le nom est
passé en paramètre.
· La méthode get_keys : Retourne les clés d’une table dont le nom est passé en
paramètre.
2.5
Vérification des normes 3NF & BCNF
• La classe FdReader :
· La méthode is_3nf : Retourne vrai si la table dont le nom est passé en paramètre
respecte la norme 3NF.
· La méthode is_bcnf : Retourne vrai si la table dont le nom est passé en paramètre
respecte la norme BCNF.
· La méthode export_to_3nf : Cette méthode exporte la table dont le nom est passé
en paramètre en 3NF dans la base de données dont le nom est passé en paramètre. Si
la base de données n’existe pas, celle-ci est créée.
2.6
Interface utilisateur
Pour la couche d’interface utilsateur, voici simplement la liste des classes utilisées.
• La classe MainFrame : Cette classe représente la fenêtre principale de l’application. Elle
hérite de la classe QMainWindow du module QtGui de la librairie PyQt4.
4
• La classe MainWidget : Cette classe représente le widget principal de l’application, c’est
dans celle-ci qu’une grande partie des informations sont représentées à l’utilisateur. Elle
hérite de la classe QWidget du module QtGui de la librairie PyQt4.
• La classe Popup : Cette classe représente le popup qui permet d’ajouter ou de modifier
une dépendance fonctionnelle. Elle hérite de la classe QDialog du module QtGui de la
librairie PyQt4.
• La classe Configuration : Cette classe représente le popup de configuration. C’est en
utilisant ce popup que l’utilisateur va configurer l’application. Elle hérite de la classe
QDialog du module QtGui de la librairie PyQt4.
• La classe ExportPopup : Cette classe représente le popup qui permet l’exportation
d’une base de données en 3NF. Elle hérite de la classe QDialog du module QtGui de la
librairie PyQt4.
3
Gestion des sauvegardes
Lorsque le paramètre "Modify directly the database" est déselectionné, une base de données
temporaire est créée simplement en demandant au système d’exploitation de faire une copie de
la base de données selectionnée par l’utilisateur. Les modifications sont faites sur cette base de
données qui diffère de l’originale uniquement de par son nom de fichier
Lorsque l’utilisateur appuie sur le bouton de sauvegarde, l’application demande au système
d’exploitation de faire une copie de la base de données temporaire vers la base de données originale. Cette action de copie écrase donc la base de données originale. Après une sauvegarde,
l’application continue le travail sur la base de données temporaire jusqu’à la prochaine sauvegarde et ainsi de suite.
Lorsque le paramètre "Modify directly the database" est selectionné, toutes les modifications
sont effectuées directement sur la base de données. Il n’y a donc aucun moyen de revenir en arrière en cas d’erreur.
Cette organisation des sauvegardes découle directement d’une réflexion sur les problèmes
que d’autres organisations peuvent poser. Nous avions en premier lieu pensé utiliser une base de
données en RAM qui serait copiée sur le disque uniquement lorsque l’utilisateur sauvegarde. Le
problème de cette approche est que s’il y a un arrêt de l’application ou de l’ordinateur, la base
de données est perdue. Les raisons pour lesquelles nous copions entièrement la base de données
et pas uniquement la table "FuncDep" dans une une base de données sont que : tout d’abord
l’approche est plus simple et puisque la copie est effectuée par l’OS, celle-ci est efficace. Ensuite,
si par la suite l’application est étendue pour pouvoir modifier les données dans la base donnée
afin de satisfaire des dépendances fonctionnelles, il sera dès lors plus judicieux de travailler sur
une base de données temporaire et de sauver les modifications à la demande de l’utilisateur.
5
4
Gestion du paramétrage de l’application
L’application peut être paramétrée via l’interface disponible dans l’application, pour y accéder il suffit de cliquer sur le bouton prévu à cet effet dans la barre d’outils.
Lors du premier démarrage de l’application, un fichier de configuration est créé et les paramètre par défaut y sont écrits. Par défaut, les deux options "Modify directly the database" et
"Allow to add logical consequencies" sont réglées au mode désactivé.
Si l’application a déjà été configurée auparavant, le fichier de configuration est lu et les checkboxes sont mises en l’état reflétant la configuration.
Lorsque que l’on modifie les paramètres (via les checkboxes) lorsque l’on clique sur le bouton
"Cancel", les paramètres sont ignorés et les checkboxes sont remises de façon à refléter le fichier
de configuration. Si on clique sur le bouton "Save", les modifications sont écrites dans le fichier
de configuration et les checkboxes sont laissées en l’état puisque qu’elles reflètent déjà le fichier
de configuration.
Nous avons décider de restreindre les moments où l’on peut configurer l’application par
soucis de sécurité. Il est possible de configurer l’application uniquement lorsque aucune base de
données n’est ouverte. Ainsi, pour configurer l’application, il suffit de cliquer sur l’icône du balai
(en ayant eventuellement sauvegardé les changements auparavant) afin de remettre l’application
dans son état original et ensuite modifier la configuration.
6
5
5.1
Description des notions théoriques et algorithmes utilisés
Les notions théoriques
• Dépendance fonctionnelle :
Soit U un ensemble d’attributs,
Une dépendance fonctionnelle (DF) sur U est une expression X → Y avec X, Y ⊆ U .
Une relation R sur U satisfait la DF X → Y , dénotée par R |= X → Y , ssi ∀t1 , t2 ∈ R,
si t1 [X] = t2 [X] alors t1 [Y ] = t2 [Y ].
• Dépendance fonctionnelle singulière :
Une DF X → Y est dite singulière ssi |Y | = 1.
• Conséquence logique :
Soit Σ un ensemble de DF sur l’ensemble U d’attributs, et X → Y une DF sur U .
On dit que X → Y est une conséquence logique de Σ, dénotée par Σ |= X → Y , ssi pour
toutes relations R sur U , si R |= Σ alors R |= X → Y .
• Superclé :
Une superclé pour un schéma (U, Σ) est un ensemble X ⊆ U tel que Σ |= X → U .
• Clé :
Une clé pour un schéma (U, Σ) est un ensemble X ⊆ U tel que :
· Σ |= X → U
· ∀X 0 ⊂ X, Σ |= X 0 → U est faux.
Une clé est donc une superclé minimale par rapport à ⊆.
• Fermeture :
La fermeture d’un ensemble d’attributs X représente l’ensemble des attributs de R qui
peuvent être déduits de X à partir d’une famille de dépendances fonctionnelles en appliquant les axiomes d’Armstrong (les axiomes d’Armstrong sont des propriétés qui permettent de déduire les conséquences logiques nous n’en parlons pas dans ce rapport car
ils ne sont pas utiles dans les algorithmes puisque inefficaces au niveau de la complexité).
• 3NF :
Un schéma (U, Σ) est en 3NF ssi pour toutes DF singulière X → A sur U telle que
Σ |= X → A, X est une superclé pour (U, Σ) ou l’attribut A fait partie d’une clé quelconque pour (U, Σ).
• BCNF :
Un schéma (U, Σ) est en 3NF ssi pour toutes DF singulière X → A sur U telle que
Σ |= X → A, X est une superclé pour (U, Σ).
7
5.2
Les algorithmes
• Calcul de la fermeture d’un ensemble d’attributs :
· Algorithme naïf :
1 def c l o s u r e ( s e l f , table_name , a t t r i b u t e _ s e t ) :
2
f u n c d e p _ l i s t = s e l f . g e t _ f u n c t i o n a l _ d e p e n d e n c i e s ( table_name )
3
old_dep = set ( )
4
new_dep = set ( copy . deepcopy ( a t t r i b u t e _ s e t ) )
5
while old_dep != new_dep :
6
old_dep = copy . deepcopy ( new_dep )
7
f o r funcdep in f u n c d e p _ l i s t :
8
i f new_dep . i s s u p e r s e t ( set ( funcdep . l h s ) ) :
9
new_dep = new_dep . union ( set ( funcdep . r h s ) )
10
return l i s t ( new_dep )
(Cet algorithme est une implémentation de l’algorithme écrit en pseudo-code à la page
64 du livre cité dans les sources.)
· Algorithme à la complexité linéaire O(n) :
1 def l i n e a r _ c l o s u r e ( s e l f , table_name , a t t r i b u t e _ s e t ) :
2
# Initialization
3
t a b l e _ a t t r i b u t e s = s e l f . g e t _ a t t r i b u t e _ l i s t ( table_name )
4
funcdep_list = s e l f . get_functional_dependencies (
table_name )
5
count = {}
6
l i s t _ d i c = {}
7
f o r a t t r i b u t e in t a b l e _ a t t r i b u t e s :
8
l i s t _ d i c [ a t t r i b u t e ] = set ( )
9
f o r funcdep in f u n c d e p _ l i s t :
10
count [ funcdep ] = len ( funcdep . l h s )
11
f o r A in funcdep . l h s :
12
l i s t _ d i c [A] = l i s t _ d i c [A ] . union ( set ( [ funcdep ] ) )
13
new_dep = set ( copy . deepcopy ( a t t r i b u t e _ s e t ) )
14
update = set ( copy . deepcopy ( a t t r i b u t e _ s e t ) )
15
# Computation
16
while len ( update ) != 0 :
17
A = update . pop ( )
18
f o r funcdep in l i s t _ d i c [A ] :
19
count [ funcdep ] = count [ funcdep ] − 1
20
i f count [ funcdep ] == 0 :
21
add = set ( funcdep . r h s ) − new_dep
22
new_dep = new_dep . union ( add )
23
update = update . union ( add )
24
# Return
25
return l i s t ( new_dep )
(Cet algorithme est une implémentation de l’algorithme écrit en pseudo-code à la page
66 du livre cité dans les sources.)
8
• Savoir si une DF est une conséquence logique d’un ensemble de DF :
1 def member ( s e l f , table_name , funcdep ) :
2
c l o s u r e = set ( s e l f . l i n e a r _ c l o s u r e ( table_name , funcdep . l h s ) )
3
i f set ( funcdep . r h s ) . i s s u b s e t ( c l o s u r e ) :
4
return True
5
else :
6
return F a l s e
(Cet algorithme est une implémentation de l’algorithme écrit en pseudo-code à la page
65 du livre cité dans les sources.)
• Calcul des superclés :
1 def g e t _ s u p e r k e y s ( s e l f , table_name ) :
2
superkeys = [ ]
3
a t t = s e l f . g e t _ a t t r i b u t e _ l i s t ( table_name )
4
att . sort ()
5
combi_att = [ ]
6
f o r i in range ( 1 , len ( a t t ) +1) :
7
combi_att += t o o l s . c o m b i n a i s o n _ l i s t ( a t t , i )
8
f o r a t t 2 in combi_att :
9
c l o s u r e = s e l f . l i n e a r _ c l o s u r e ( table_name , a t t 2 )
10
closure . sort ()
11
i f c l o s u r e == a t t :
12
s u p e r k e y s . append ( a t t 2 )
13
return s u p e r k e y s
• Calcul des clés :
1 def get_keys ( s e l f , table_name , s u p e r k e y s = [ ] ) :
2
i f s u p e r k e y s == [ ] :
3
s u p e r k e y s = s e l f . g e t _ s u p e r k e y s ( table_name )
4
k e y s = copy . deepcopy ( s u p e r k e y s )
5
oldlen = 0
6
f o r a t t in k e y s :
7
att . sort ()
8
keys . s o r t ( )
9
while len ( k e y s ) != o l d l e n :
10
o l d l e n = len ( k e y s )
11
f o r sk1 in k e y s :
12
add =F a l s e
13
f o r sk2 in k e y s :
14
v e r i f = True
15
f o r a t t in sk1 :
16
i f ( a t t not in sk2 ) and ( v e r i f != F a l s e ) :
17
v e r i f = False
18
if verif :
19
i f len ( sk1 ) < len ( sk2 ) :
20
k e y s . remove ( sk2 )
21
e l i f len ( sk1 ) > len ( sk2 ) :
22
k e y s . remove ( sk1 )
23
keys . r e v e r s e ( )
9
24
return k e y s
• Savoir si une relation est en 3NF :
1 def i s _ 3 n f ( s e l f , table_name ) :
2
n f 3 = True
3
f d _ l i s t = s e l f . g e t _ f u n c t i o n a l _ d e p e n d e n c i e s ( table_name )
4
i f len ( f d _ l i s t ) != 0 :
5
s u p e r k e y s _ l i s t = s e l f . g e t _ s u p e r k e y s ( table_name )
6
kl = [ ]
7
k e y s _ l i s t = s e l f . get_keys ( table_name )
8
f o r k e y s in k e y s _ l i s t :
9
f o r a t t in k e y s :
10
i f a t t not in k l :
11
k l . append ( a t t )
12
f o r f d in f d _ l i s t :
13
i f ( ( ( not ( ( f d . l h s in s u p e r k e y s _ l i s t ) or ( f d . r h s [ 0 ] in
k l ) ) ) and ( n f 3 != F a l s e ) ) ) :
14
nf3 = False
15
else :
16
n f 3 = True
17
return n f 3
• Savoir si une relation est en BCNF :
1 def i s _ b c n f ( s e l f , table_name ) :
2
funcdeps = s e l f . get_functional_dependencies ()
3
i f len ( f u n c d e p s ) != 0 :
4
s u p e r k e y s = s e l f . g e t _ s u p e r k e y s ( table_name )
5
f o r funcdep in f u n c d e p s :
6
i f not funcdep . l h s in s u p e r k e y s :
7
return F a l s e
8
return True
• Calcul d’une décomposition en 3NF qui préserve les DF et le contenu :
1 def export_to_3nf ( s e l f , table_name , db_name2=" exported3NF . db" ) :
2
i f ( s t r ( table_name ) != "FuncDep" ) :
3
cursor = s e l f . connection . cursor ()
4
funcdeps , r e q u e s t s = s e l f . get_new_funcdeps_and_requests (
table_name )
5
newdb = FdReader ( db_name2 )
6
newdb . c o n n e c t i o n = s q l i t e 3 . c o n n e c t ( db_name2 )
7
c u r s o r = newdb . c o n n e c t i o n . c u r s o r ( )
8
f o r r e q u e s t in r e q u e s t s :
9
cursor . execute ( request )
10
f o r f d in f u n c d e p s :
11
newdb . add_functional_dependency ( f d )
12
f o r t a b l e in newdb . g e t _ t a b l e _ l i s t ( ) :
13
j = len ( t a b l e )
14
i f ( ( s t r ( t a b l e ) != "FuncDep" ) and ( s t r ( t a b l e ) [ 0 : j ] ==
s t r ( table_name ) ) ) :
15
s e l f . f i l l _ t a b l e 3 n f ( table_name , t a b l e , db_name2 )
10
Remarque : Certaines fonctions que nous avons codées et qui ne sont pas présentées dans
ce rapport sont utilisées dans cet algorithme. Il est possible de les consulter dans le script
fdreader.py.
6
Problèmes rencontrés et solutions
Il n’y a pas eu de problèmes majeurs lors du développement de l’application mis a part lors
de l’exportation en 3NF où nous avons mis du temps a comprendre l’algorithme.
Néanmoins lors du démarrage de l’application sous Archlinux, un warning apparait. Il semblerait que cela vient d’un bug dans la librairie LibPng selon une page du wiki d’Archlinux
(https://wiki.archlinux.org/index.php/Libpng_errors). Ce warning semble ne poser aucun problème lors de l’utilisation de l’application.
7
Répartition du travail
Jannou Brohée
Identification des clés/superclés
Vérification des normes 3NF & BCNF
Tests unitaires
8
Julien Delplanque
Lecture/écriture des dépendances fonctionnelles
Vérification des dépendances fonctionnelles
Identification des clés/superclés
Vérification des normes 3NF & BCNF
Tests unitaires
Interface Utilisateur
Rapport
Sources et remerciements
• Livre de référence pour les algorithmes : http://web.cecs.pdx.edu/~maier/TheoryBook/
MAIER/.
• Site consulté : http://perso.univ-lr.fr/pboursie/BD_Niv1/.
• Icône point d’interrogation : http://www.xn--icne-wqa.com/tag-interrogation-0.
• Alexandre Delplanque pour la création du reste des images.
• Maximilien Charlier pour les comparaisons de résultats de tests sur des bases de données
identiques.
• Émeline Renard pour la relecture du rapport.
11
Téléchargement