TP 14 : Interaction entre une base de données et Python

publicité
MPSI 831, PCSI 833
Lycée Masséna
TP 14 : Interaction entre une base de données et Python
Dans ce TP, on va voir comment manipuler une base de données en utilisant Python (ce qui permettra d’automatiser
des processus en utilisant des boucles). Nous verrons en particulier comment créer, ouvrir, manipuler ou supprimer
des données, puis comment utiliser des données pour créer un histogramme.
1
Interaction d’une base de données avec Python
Le module permettant d’intégrer un SGBD à un environnement Python s’appelle sqlite3 ; une fois ce dernier
importé, on se connecte à une base de données par l’intermédiaire de la fonction connect, en précisant en paramètre
un chemin d’accès vers la base de données. Par exemple, pour utiliser la base de données communes_francaises.sqlite
du dernier TP (et en supposant que celle-ci soit dans le répertoire courant) on écrira :
import sqlite3 as sql
conn=sql.connect("communes_francaises.sqlite") #Changer le chemin ou utiliser os.chdir
L’objet conn est désormais en place, et vous allez pouvoir dialoguer avec lui à l’aide du langage SQL. On va
d’abord mettre en place ce que l’on appelle un curseur. Il s’agit d’une sorte de tampon mémoire intermédiaire, destiné
à mémoriser temporairement les données en cours de traitement, ainsi que les opérations que vous effectuez sur elles,
avant leur transfert définitif dans la base de données. Cette technique permet donc d’annuler si nécessaire une ou
plusieurs opérations qui se seraient révélées inadéquates (dans le cas où on modifie la base de données), et de revenir
en arrière dans le traitement, sans que la base de données n’en soit affectée.
cur=conn.cursor()
Une fois le curseur créé, la méthode execute du curseur permet de transmettre des requêtes rédigées en SQL sous
forme de chaîne de caractères :
cur.execute("SELECT * FROM communes WHERE dep_id=6 ORDER BY population DESC")
Dans le cas où l’on effectue des requêtes d’extraction de données dans la base (SELECT ...), on peut parcourir le
curseur comme un itérable (et par exemple afficher les résultats)
for resultat in cur:
print(resultat)
Ceci va vider le curseur, et l’on peut récupérer les résultats de la requête. Si l’on avait effectué plusieurs requêtes avant
de « vider » le curseur, les résultats se seraient « enfilés » dans le curseur.
Enfin, si des modifications ont été effectuées sur la BDD (ce qu’on verra plus tard), il faut appliquer la méthode
commit à la connexion créée pour qu’elles deviennent définitives. On peut ensuite refermer le curseur et la connexion :
conn.commit() #utile si une modification a eu lieu
cur.close()
conn.close()
Vous trouverez sur le site web un script permettant un dialogue interactif avec la base de données :
import sqlite3 as sql
conn=sql.connect("communes_francaises.sqlite") #changer le répertoire
cur=conn.cursor()
while True:
requete=input("Entrez une requête SQL ou STOP pour arrêter : ")
if requete=="STOP":
break
try:
Svartz
Page 1/6
2015/2016
MPSI 831, PCSI 833
Lycée Masséna
cur.execute(requete)
for result in cur:
print(result)
except:
print("Requête incorrecte")
cur.close()
conn.close()
Quelques explications/rappels :
— input permet de récupérer ce que l’utilisateur tape au clavier, et renvoie le résultat comme une chaîne de
caractères ;
— break permet d’interrompre une boucle (ici on sort donc de la boucle while uniquement si « STOP » est tapé
au clavier) ;
— On peut utiliser try et except pour qu’un code puisse se poursuivre même s’il y a des erreurs : si vous tapez
une requête syntaxiquement incorrecte, une erreur se produit (non visible) et le bloc except est exécuté. S’il n’y
a pas d’erreur lors de l’évaluation du bloc try, le bloc except n’est pas exécuté.
Rappelons la structure de la base de données communes_francaises.sqlite.
Pour la table depts :
— id (un entier) la clé primaire de l’enregistrement ;
— code (une chaîne de caractères) le code administratif du département ;
— nom (une chaîne de caractères) le nom du département ;
— chef_lieu (un entier) l’identifiant du chef-lieu du département.
Pour la table communes :
— id (un entier) la clé primaire de l’enregistrement ;
— code (une chaîne de caractères) le code administratif de la commune ;
— postal (une chaîne de caractères) le code postal de la commune ;
— nom (une chaîne de caractères) le nom de la commune ;
— superficie (un flottant) la superficie de la commune, en hectares ;
— altitude (un entier) l’altitude de la commune, en mètres ;
— population (un flottant) la population d’une commune, en millier d’habitants ;
— dep_id (un entier) l’identifiant du département où se trouve la commune.
Question 1. Utilisez le script interactif pour effectuer quelques requêtes de recherche du TP précédent, par exemple :
— afficher toutes les entrées de la table depts ;
— donner le nombre d’entrées de la table communes ;
— donner pour chaque département le nombre d’habitants (utiliser un GROUP BY) ;
— afficher la liste des chefs-lieu de France (utiliser une jointure)
2
Génération de requêtes par des fonctions Python
L’intérêt majeur de l’utilisation du module sqlite3 est de pouvoir écrire des fonctions, qui vont prendre en entrée
un certain paramètre, générer une requête dépendant du paramètre, exécuter la requête, récupérer le résultat, et le
renvoyer : l’utilisateur peut donc faire usage des fonctions pour récupérer des données dans la base, sans écrire la
moidre requête ! Bien sûr, c’est nous qui allons écrire ces fonctions. On rappelle que les requêtes doivent être fournies
à la méthode execute sous forme de chaînes de caractères. On fera donc usage de :
— str(x) pour convertir x (un entier, flottant...) en chaîne de caractères ;
— + pour concaténer des chaînes de caractères.
Les questions qui suivent demandent d’écrire des requêtes qui produisent un résultat, avec un seul attribut (sauf à
la question 6). Il est facile de récupérer le résultat en question à partir du curseur :
Svartz
Page 2/6
2015/2016
MPSI 831, PCSI 833
Lycée Masséna
— une fois la requête effectuée, L=list(cur) permet d’obtenir une liste à 1 seul élément (elle en aurait contenu
plusieurs si la requête fournissait plusieurs résultats) ;
— L[0] est cet élément : c’est un tuple à un seul élément (si la requête produisait bien un résultat à un seul
attribut) ;
— L[0][0] est donc ce résultat. On pourra utiliser int pour convertir en retour ce résultat en entier...
Question 2. Écrire une fonction chef_lieu(c) prenant en entrée une chaîne de caractère c et renvoyant le nom de
la commune qui est le chef lieu du département dont le code administratif est c. (On affichera une erreur si c ne
correspond à aucun code administratif).
>>> chef_lieu("83")
'TOULON'
>>> chef_lieu("06")
'NICE'
>>> chef_lieu("2A")
'AJACCIO'
>>> chef_lieu("979")
code erroné
Attention : la requête à exécuter doit contenir des guillemets autour du code de département. On rappelle l’on peut
faire usage de guillemets simples et doubles en SQLite comme en Python. Avant d’écrire la fonction, on cherchera à
écrire correctement une requête récupérant le bon résultat.
Question 3. Écrire de même une fonction nombre_communes_dep(c) prenant en entrée un code administratif et
renvoyant le nombre de communes (c’est un entier) dans le département de code c, affichant une erreur sinon.
>>> nombre_communes_dep("06")
163
>>> nombre_communes_dep("2B")
236
Question 4. Écrire une fonction nombre_communes_alt(mini,maxi), prenant en entrée deux entiers et retournant le
nombre de communes à une altitude située entre mini inclus et maxi exclus.
>>> nombre_communes_alt(100,400)
20968
>>> nombre_communes_alt(0,10000)
36613
>>> nombre_communes_alt(100,100)
0
Svartz
Page 3/6
2015/2016
MPSI 831, PCSI 833
Lycée Masséna
Question 5. Camembert. La fonction pie de matplotlib.pyplot permet le tracé d’un diagramme circulaire (« camembert » en français, « tarte » en anglais). Consulter l’aide (help(plt.pie) avec matplotlib.pyplot importé sous
le nom plt comme d’habitude) associée à cette fonction, puis tracer un diagramme circulaire dans lequel seront
représentés :
— le nombre de communes situées à une altitude inférieure à 50 m ;
— le nombre de communes situées entre 50 et 100 m d’altitude ;
— le nombre de communes situées entre 100 et 500 m d’altitude ;
— le nombre de communes situées entre 500 et 1000 m d’altitude ;
— le nombre de communes situées à plus de 1000 m d’altitude. (On rappelle que l’Everest culmine à 8848 mètres)
On utilisera bien sûr la fonction précédente.
Question 6. Histogramme. De même, la fonction plt.bar permet de réaliser un histogramme (diagramme en « rectangles »). Il s’utilise par exemple comme suit :
x = [1,2,3,4,5,6,7,8,9,10]
hauteurs_barres = [8,12,8,5,4,3,2,1,0,0]
largeur_barres = 0.1
plt.bar(x, hauteurs_barres, largeur_barres)
plt.show()
(Évidemment, x et hauteurs_barres doivent être deux listes de même taille)
On peut convertir le résultat d’une requête en liste (cur.execute(requete) puis list(cur)). Le résultat est une
liste de tuples, chaque tuple étant associé à une ligne du résultat de la requête. Les types des éléments du tuple sont
les mêmes que les attributs de la table associée au résultat de la requête. En exécutant une seule requête, réalisez un
histogramme représentant les populations des différents département par milliers d’habitants.
3
D’une base de données à un fichier
Question 7. Écrire dans un fichier departements.txt le nom de tous les départements de la table depts précédé de
leur code administratif, et suivi de leur population en milliers d’habitants (un département par ligne). La première
ligne est : 01, AIN, 588.8000000000008. (On pourra aussi utiliser round(x,1) pour couper au dixième et obtenir
plutôt 588.8.) On rappelle ici les fonctions principales pour travailler avec un fichier dans Python :
Svartz
Page 4/6
2015/2016
MPSI 831, PCSI 833
fonction
f=open(nom_du_fichier,'r')
f=open(nom_du_fichier,'w')
f=open(nom_du_fichier,'a')
f.close()
f.readlines()
f.write(s)
f.writelines(T)
4
Lycée Masséna
desciption
Ouvre le fichier nom_de_fichier (donné sous la forme d’une chaîne de caractères
indiquant son emplacement) en lecture (r comme read). Le fichier doit exister et
seule la lecture est autorisée.
Ouvre le fichier nom_de_fichier en écriture (w comme write). Si le fichier n’existe
pas, il est créé, sinon il est écrasé (vidé avant utilisation).
Ouvre le fichier nom_de_fichier en ajout (a comme append). Identique au mode 'w',
sauf que si le fichier existe, il n’est pas écrasé et ce qu’on écrit est ajouté à partir de
la fin du fichier.
Sur un fichier ouvert comme précédemment, le ferme. Cette ligne est impérative pour
les fichiers ouverts en écriture, puisque le fichier n’est réellement écrit complètement
qu’à la fermeture.
Lit tout le fichier et stocke le résultat dans une liste de chaînes de caractères, chaque
élément correspondant à une ligne. Attention, le saut de ligne \n est présent à la fin
de chaque chaîne.
Écrit la chaîne s à la suite du fichier.
Écrit l’ensemble des éléments de T dans le fichier f comme des lignes successives. T
est une liste, une séquence, un tuple... bref, un itérable.
Création d’une base de données à une seule table (à partir d’un fichier)
Nous allons voir dans cette section comment créer une base de données, et insérer des valeurs dedans. Fermez la
table communes_francaises.
Voici un exemple de script pour demander la création d’une nouvelle table, qui est sensée gérer les membres d’une
association (le code est téléchargeable comme exemple_code.py) :
conn=sql.connect("assoc.sqlite")
cur=conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS membres (id INTEGER PRIMARY KEY AUTOINCREMENT, age INTEGER,
nom TEXT, taille REAL)")
cur.execute("INSERT INTO membres(age,nom,taille) VALUES(21,'DUPOND',1.83)")
cur.execute("INSERT INTO membres(age,nom,taille) VALUES(15,'DURAND',1.57)")
cur.execute("INSERT INTO membres(age,nom,taille) VALUES(18,'DUGOMMIER',1.69)")
conn.commit()
cur.close()
conn.close()
Quelques explications :
— On crée une table « membres » à 4 attributs. Notez le champ « id » de type nombre entier qui sert de clef
primaire : ceci est spécifié, et ce champ est auto-incrémenté, ce qui signifie que lorsqu’on rentre de nouveaux
membres dans la base, on n’a pas à préciser nous-même le champ id.
— Le "IF NOT EXISTS" est optionnel mais conseillé car il évite un message d’erreur au cas ou la TABLE existe
déjà.
— Dans la rédaction, on fait suivre CREATE TABLE du nom de la table, puis entre parenthèses les noms de chaque
champs suivi du type de données et séparés par des virgules.
— L’insertion de membres dans la base se fait de lui-même, à l’aide de INSERT INTO. L’exemple parle de lui-même.
— Après l’insertion, les enregistrements sont dans le tampon du curseur, mais ils n’ont pas encore été transférés
véritablement dans la base de données. On pourrait donc tout annuler si nécessaire. Le transfert dans la base de
données est déclenché par la méthode commit() de l’objet conn.
— On peut refermer le curseur et la connexion ensuite.
Question 8. Récupérez le fichier membres_associations.txt et insérez les nouvelles entrées dans la base à partir
d’un script Python (en vrai c’est la liste des PC, avec des ages et tailles choisis au hasard). Vous pouvez laisser
Dupont, Durand, Dugommier. On rappelle que s.split(',') permet de séparer une chaîne de caractères s autour
du caractère , : une liste est renvoyée.
Question 9. Une fois la base créée, récupérer les informations suivantes :
Svartz
Page 5/6
2015/2016
MPSI 831, PCSI 833
Lycée Masséna
— nombre d’entrées dans la base ?
— moyenne d’âge ?
— taille moyenne ?
Voici les principales instructions utilisables pour manipuler une BDD :
Instruction
Effet produit
conn = sqlite3.connect(fichierDonnees)
Ouvre conn qui permet d’accéder à la Bdd
cur =conn.cursor()
Crée un curseur cur qui agit sur la Bdd et permet de
récupérer les résultats des requêtes
conn.commit()
Exécute toutes les modifs sur la Bdd
cur.close()
Ferme le curseur cur
conn.close()
Ferme la connexion conn
cur.execute("CREATE TABLE membres (
Crée une table en indiquant les noms et types des
age INTEGER, nom TEXT, taille REAL)")
champs
cur.execute("CREATE TABLE IF NOT EXISTS
Idem mais teste si cette table existe
membres (age INTEGER, nom TEXT, taille REAL)")
cur.execute("INSERT INTO membres(age,nom,taille)
Insère dans la table et dans les champs cités les donVALUES(21,'Dupont',1.83)")
nées suivantes
cur.execute("SELECT * FROM membres")
Un exemple de SELECT
cur.execute("UPDATE membres set nom ='Gerart'
On remplace le nom 'Ricard' de la table membre
WHERE nom='Ricard'")
par le nom 'Gerart'
cur.execute("DELETE FROM membres WHERE
Supprime l’enregistrement vérifiant le critère
nom='Machin'")
cur.execute("DROP TABLE nom_de_la_table")
On supprime la table nom_de_la_table
Question 10. Modifiez la base en suivant les instructions suivantes :
— DURAND est décédé. Supprimez-le.
— TEYSSIER n’a pas 61 ans, mais 28. Mettez l’information à jour.
5
Création d’une vraie base
Nous allons créer une base de données, concernant les élèves de PCSI 833, à l’aide de Python, via sqlite3. Les tables
et leurs attributs sont les suivants.
• Table etudiant :
— id : un entier (clé primaire) ;
— nom : une chaîne de caractères ;
— prenom : une chaîne de caractères ;
• Table cours :
— id : un entier (clé primaire) ;
— nom : une chaîne de caractères ;
• Table cours_suivi :
— id_etu : un entier (clé étrangère) ;
— id_cours : un entier (clé étrangère) ;
On impose que le couple (id_etu, id_cours) forme une clé primaire.
Question 11. Créez la base de données pcsi.sqlite (il suffit de créer une connexion vers pcsi.sqlite)
Question 12. Après avoir ouvert une connexion conn et un curseur cur, créez les tables etudiant et cours. (On
ne précisera pas AUTOINCREMENT pour les clés primaires, mais on précisera qu’elles sont des clés primaires).
Remplissez-les à l’aide des deux fichiers etudiant.txt et cours.txt.
Question 13. Voici comment créer la table cours_suivi :
cur.execute("CREATE TABLE IF NOT EXISTS cours_suivi (id_etu INTEGER, id_cours INTEGER,
PRIMARY KEY (id_etu,id_cours), FOREIGN KEY(id_etu) REFERENCES etudiant(id),
FOREIGN KEY(id_cours) REFERENCES cours(id))")
Voici des explications : on indique que id_etu et id_cours sont des clés étrangères vers les tables en question, et on
impose de plus que le couple composé des deux clés forme une clé primaire. Remplissez la table à l’aide du fichier
cours_suivi.txt.
Svartz
Page 6/6
2015/2016
Téléchargement