Accueil

Traduction

Tutoriel Python - sommaire

Tutoriel Python - recherche

L'auteur : Patrick Darcheville

Vous pouvez me contacter via Facebook pour questions & suggestions : Page Facebook relative à mon site

SGBD SQLite et DB browser

Les Bases De Données s’imposent comme une façon efficace de stockage de données. Il est alors plutôt aisé d’interagir avec celles-ci en utilisant un Système de Gestion de Base de Données (SGBD).
Un SGBD est un logiciel spécialement conçu pour manipuler les bases de données à l’aide d’un langage normalisé : le SQL (Structured Query Language).

Les différents SGBD

Présentation sommaire de SQLite

Nous utiliserons SQLite car il est beaucoup plus simple à mettre en œuvre que les autres : pas nécessaire d'installer un serveur de bases de données sur votre PC.
Comme mySQL, SQLite est un SGBD de type relationnel ce qui signifie que les tables d'une même base peuvent être reliées entre-elles (jointures) et qu'au sein d'une table toutes les lignes ont la même strucure.

Gros avantage de SQLite : une base de données SQLite c'est physiquement un fichier unique. Cette caractéristique rend SQLite intéressante comme alternative aux fichiers textes, utilisés comme moyen de stockage intégré dans beaucoup d'applications.

Un SGBD très utilisé

Extrait de l'article de Wikipedia :

De par son extrême légèreté (moins de 600 KO), il est également très populaire sur les systèmes embarqués, notamment sur la plupart des smartphones et tablettes modernes : les systèmes d'exploitation mobiles iOS, Android et Symbian l'utilisent comme base de données embarquée.

Historique

Richard Hipp, le créateur de SQLite, commence la conception de ce moteur de SGBD en 2000 suite à une commande de la défense US ; SQLite devant équiper des missiles guidés.

Structure d'une base de données

Généralités

Une base de données a un nom et comprend une ou plusieurs tables. Elle comprend aussi éventuellement des vues.

Un table c'est comme un tableau : des lignes et des colonnes. Les colonnes sont aussi appelée champs ("fields") et les lignes sont aussi appelées enregistrements ("records").
A l'intersection d'une colonne et d'une ligne se trouve une cellule contenant une donnée.

Chaque ligne décrit un individu (au sens statistique du terme) : un salarié, un client, un contact, un article du catalogue, un patient, etc.
Tous les individus sont donc décrits de la même façon.

Notre thématique

Nous devons gérer les espèces animales d'un biotope marin.
Une espèce a un nom et appartient à un taxon (et un seul). Je rappelle qu'un taxon est un groupe d'animaux "cousins" (qui ont un ancêtre commun). Les taxons peuvent avoir des niveaux hiérarchiques différents : embranchement, classe ou ordre.
Ainsi "éponges & cténaires" sont des embranchements tandis que "bivalves, gastéropodes, céphalopodes" sont des classes de l'embranchement "mollusques". Mais quand on dit que telle espèce est un gastéropode on sous-entend naturellement qu'il s'agit d'un mollusque.

La base de données se nommera "animaux.db". Elle comprendra donc deux tables :

Structure de la table "taxons"

Ce que doit contenir la table taxons (extrait) :

Une ligne (ou enregistrement) par taxon.

Structure de la table "especes"

Ce que doit contenir la table "especes" (extrait) :

1	homard	deca
2	orange de mer	epon
3	fesses d'éléphant	epon
4	crevette grise	deca
5	bouquet	deca
6	étrille	deca
...

Remarque : une ligne (ou enregistrement ou "record") par espèce.

Dans toute table une colonne (et une seule) doit faire fonction de clé primaire.
Il ne peut y avoir de doublons pour cette colonne. Il y aura automatiquement création d'un index sur cette colonne. Donc la recherche d'une ligne sera extrèment rapide : recherche indéxée.

Liaison entre les deux tables

Il y a une colonne commune entre les deux tables ; elle porte d'ailleurs le même nom : "code" (mais ce n'est pas obligatoire).
Cette colonne commune aux deux tables est appelé "champ de jointure". En effet elle permet de mettre en relation les deux tables.
Ainsi à chaque enregistrement de "espèces" on pourra associer une ligne (et une seule) de "taxons". A contrario pour chaque ligne de "taxons" on peut associer 0 à N lignes de "especes".
On dit que "taxons" est mère par rapport à "espèces" OU que "espèces" est fille par rapport à "taxons".

L'utilitaire Dbbrowser for SQLite

Présentation

Le langage SQL peut paraitre difficile pour un débutant. Aussi nous allons créer et remplir les tables de notre base SQLite avec un logiciel intitulé  DB Brower for SQLliste. Ce programme est est l'équivalent de PHPMyAdmin pour MySQL.

En fait cette interface génère du SQL à partir de tous les renseignements que vous lui fournissez dans le cadre de grilles.
Disons qu'avec DB browser vous allez découvrir en douceur le SQL.

Installation de l'outil

Il suffit de se rendre dans la page de téléchargement du site sqlitebrower.org : https://sqlitebrowser.org/dl/

Création d'une base

Nous allons employer cette interface pour créer la base de données "animaux.db" et les différentes tables.

Suite

Il faut maintenant créer chacune des deux tables (définir leur structure) puis leur ajouter des données (insérer des enregistrements).
Il faut donc générer pour chaque table deux requêtes SQL : une de type CREATE TABLE ... et plusieurs de type INSERT INTO ...
Nous allons toujours faire appel à DB browser pour créer et remplir les deux tables.

Création et remplissage de la table "taxons"

Cliquez sur "Créer une table".
Vous remplissez la fenêtre qui s'affiche comme suit :

Notez que la grille est légendée "éditer la définition de la table".

Différence et U et CP : dans les deux cas les doublons pour cette colonne sont impossible. Mais avec CP il y a création d'un index. Ce qui n'est pas le cas avec U.

Observons attentivement le code SQL généré :

Pour la table "taxons" il n'y a que deux colonnes à définir.
Les deux colonnes sont de type "text". La première est clé primaire (pour identifier de manière unique un taxon). Mais comme le type est TEXT, l'autoincrémentation est bien sûr impossible.

Saisie dans la table "taxons"

La table "taxons" existe désormais (a une structure) mais est vide de toute données. Il faut désormais y ajouter des lignes (ou enregistrements).

Sélectionnez la table "taxons" à partir de la fenêtre "parcourir les donnees" puis cliquez sur l'icône "insérer un nouvel enregistrement dans la table" : vous pointez alors la fin de table. il suffit de remplir la ligne vierge du tableau.
Recommencez la procédure autant de fois que nécessaire.

Tri de la table "taxons"

Après avoir saisi plusieurs lignes dans la table "taxons" vous pouvez trier par valeurs croissantes/décroissantes les noms des taxons. Il suffit à partir du contenu sous forme d'un tableau (fenêtre "parcourir les données) de cliquer sur l'étiquette de colonne.

Création et remplissage de la table "especes"

Créer la table "especes"

Attention le code SQL de création de la table "especes" est plus complexe que pour la table "taxons".
Il y a effet une contrainte d'intégrité référentielle : on ne doit pouvoir saisir dans la colonne "especes.code" qu'une valeur qui existe déjà dans la colonne "taxons.code". Il faut donc définir la colonne "especes.code" comme clé étrangère par rapport à "taxons.code".

Le code SQL qu'il faut générer est donc :

CREATE TABLE "especes" ( "id" INTEGER NOT NULL, 
"nom_espece" TEXT NOT NULL, "code" TEXT NOT NULL, 
PRIMARY KEY("id" AUTOINCREMENT), 
FOREIGN KEY("code") REFERENCES "taxons"("code") )

Analyse de cette requête

Intéressons nous à la clause
FOREIGN KEY("code") REFERENCES "taxons"("code") :
la colonne "code" de la table 'especes" est clé étrangère par rapport à la colonne "taxons.code". Ce qui veut dire qu'on ne peut saisir dans la première qu'une valeur qui existe déjà dans la seconde.

Insertion d'enregistrements dans la table "especes"

À partir de la fenêtre "parcourir les données" sélectionnez "especes" dans la liste déroulante "table" puis cliquez sur l'icône "insérer un nouvel enregistement".
Notez que c'est désormais une grille de saisie qui apparait (et non pas un tableau du fait de l'intégrité référentielle).
Cette grille est légendée : "ajouter un enregistrement".

En haut de la grille et pour chaque champ, il faut saisir la valeur (sauf pour le champ ID qui se remplie automatiquement).
Pour "nom_espece" saisissez "cristatelle" et pour "code" saisissez "bryo" (comme bryozoaires) : un message d'erreur apparait : "Foreign key constraint failed ..." . La requête échoue ! En effet vous n'avez pas pas respecté l'intégrité référentielle ; la valeur "bryo" n'existe pas encore dans "taxons.code". Dans un langage imagé on dirait : "il ne peut y avoir de fille sans une mère".

Saisissez alors "mérou" & "osse".
Puis observez le code SQL que vous avez généré (affiché en bas de la grille):

 INSERT INTO "main"."especes"
("nom_espece", "code")
VALUES ('mérou', 'osse');

Cette fois l'insertion est autorisée puisque la valeur "osse" (poissons osseux) existe dans la colonne "taxons.code".

Dons si vous voulez ajouter des espèces appartenant au taxon des "bryozoaires" il faudra d'abord créer ce groupe dans la table "taxons" puis saisir les espèces de ce groupe dans la table 'especes".

Tri de la table "especes"

Après avoir saisi plusieurs lignes dans la table "especes" vous pouvez trier par valeurs croissantes/décroissantes les codes et les noms d'espèces. Il suffit à partir du contenu sous forme d'un tableau (fenêtre "parcourir les données) de cliquer sur l'étiquette de colonne ad hoc.

Les requêtes sélection

Les requêtes sélection ne modifient pas les données, à la différence des requêtes action telles INSERT INTO ...DELETE FROM ... DROP ... Elles se contentent d'afficher des données extraites d'une ou plusieurs tables avec éventuellement tri, limite et filtage.

Dans l'outil DB Browser vous êtes obligé de tapez les requêtes sélection en SQL (il n'y a pas d'interface qui génère cette requête).
Cliquez sur "exécutez le SQL" ; tapez le code de la requête sélection puis cliquez sur la flèche bleu orientée vers la droite.

Affichage d'une table (toutes les colonnes)

De la table "especes" : SELECT * FROM especes

De la table "taxons" : SELECT * FROM taxons
* veut dire "afficher toutes les colonnes" de la table

Afficher seulement de certaines colonnes

Colonne "nom_espece" & "code" : SELECT nom_espece, code FROM especes
Donc la colonne ID n'est pas affichée.

Je rappelle que l'usage veut que les mots réservés du langage SQL soient écrits en majuscules et que les noms des colonnes et des tables soit délimitées par des guillemets (simples ou doubles).
Mais le fait d'omettre les guillemets et d'écrire les mots réservées en minuscules ne constituent des erreurs de syntaxe.
Ainsi au lieu d'écrire SELECT 'nom_espece' FROM 'especes' vous pouvez écrire tout simplement select nom_espece from especes
Déns les deux cas la requête sera exécutée avec succès.

Limiter l'affichage à certaines lignes

Il s'agit d'une requête SELECT avec la clause LIMIT suivi de 1 ou 2 arguments.
SELECT * FROM especes LIMIT 10 : affichage des 10 premières lignes
SELECT * FROM especes LIMIT 10,30 : affichage de 22 lignes (de la 10ième à la 30ième incluses)

Filtrage avec la clause WHERE

Afficher seulement les décapodes (code ="deca") :
SELECT nom_espece, code FROM especes WHERE code ="deca"

Afficher seulement les poissons osseux (code ="osse") :
SELECT * FROM especes WHERE code ="osse"

Afficher poissons osseux et décapodes :
SELECT * FROM especes WHERE code ="osse" or code="deca"
La condition de filtrage devient complexe.

Filtrage avec la clause LIKE : "commence par ..." ou "contient ..."

SELECT * FROM especes WHERE nom_espece LIKE "h%"
On affiche seulement les espèces dont le nom commence par la lettre "h" (minuscule ou majuscule car LIKE est insensible à la casse).
LIKE s'associe à "%" qui remplace n'importe qu'elle chaîne de caractères et "_" qui remplacement un seul et unique caractère.

Vous pouvez écrire : SELECT * FROM especes WHERE nom_espece LIKE "%de mer%".
Cette requête retourne les lignes dont le champ "nom_espece" contient l'occurence "de mer" .
Dans le chapitre suivant vous verrez que le critère de filtrage peut être saisi par l'utilisateur : requêtes paramétrées.

Afficher un intervalle de lignes

SELECT * FROM especes WHERE id BETWEEN '1' AND '10'
On affiche uniquement les enregistrements dont l'id est compris entre 1 et 10, donc le début de table

SELECT * FROM especes WHERE nom_espece BETWEEN 'a%' AND 'd%' order by nom_espece
On affiche les espèces dont la première lettre du nom est compris entre a et d.

Trier les lignes

SELECT nom_espece, code FROM especes ORDER BY code ASC
Dans la liste retournée, les lignes sont triées par valeurs croissantes pour la colonne code. L'argument ASC (ascending) est facultatif : valeur par défaut est ASC.

SELECT nom_espece, code FROM especes ORDER BY code DESC
Dans la liste retournée, les lignes sont triées par valeurs décroissantes pour la colonne code. L'argument DESC est obligatoire.

Filtrage et tri

SELECT * FROM especes WHERE code ="osse" ORDER BY nom_espece
Dans la liste retournée, seuls les poissons osseux figurent et ils sont ordonnées par nom.
Attention la clause WHERE doit précéder la clause ORDER.

L'ordre physique des enregistrements dans la table n'est pas modifié par les tris et filtrages des requêtes SELECT

Requêtes sélection sur plusieurs tables

Nous voulons associer à chaque ligne de "especes" un enregistement (et un seul) de "taxons" puisque chaque espèce appartient à un taxon et un seul.
Entre "especes" et "taxons" nous avons un champ commun qui porte le même nom dans les deux tables : "code". Nous pouvons donc poser une jointure entre ces deux tables.

Requete sur especes & taxons

SELECT * FROM especes, taxons WHERE especes.code = taxons.code
La requête retourne autant de lignes que d'enregistements dans la table "especes".
Pour lever toute ambiguïté il faut employer les noms complets des champs de jointure (nomtable.nomColonne) : "especes.code" d'une part et "taxons.code" d'autre part.
D'ailleurs essayez SELECT * FROM especes, taxons WHERE code = code : échec !

Désigner des champs de jointure par le même nom (ici "code") est une maladresse.
Mais c'est une maladresse délibérée de ma part ; il faut parfois montrer ce qu'il ne faut pas faire.
Il aurait été préférable de désigner le champ "taxons.code" par "t_code" et le champ "especes.code" par "e_code" donc préfixer les champs de jointure.

Le rendu (extrait) :

 
1	homard			deca	deca	décapodes
4	crevette grise	deca	deca	décapodes
5	bouquet			deca	deca	décapodes
6	étrille			deca	deca	décapodes
7	tourteau		deca	deca	décapodes
8	araignée mer	deca	deca	décapodes
9	bulot			gast	gast	gastéropodes
...

Pour chaque ligne on a deux fois la même info ...

Solution

SELECT nom_espece, taxons.code, nom_taxon FROM especes, taxons WHERE especes.code = taxons.code
Cette fois on affiche que les champs précisés après le mot SELECT : le code du taxon n'est affiché qu'une fois !
Pour lever toute ambiguïté il faut écrire "taxons.code" OU "especes.code".

Le rendu (extrait) :

homard			deca	décapodes
crevette grise	deca	décapodes
bouquet			deca	décapodes
étrille			deca	décapodes
tourteau		deca	décapodes
araignée de mer	deca	décapodes
bulot			gast	gastéropodes
...

Sauvegarder les requêtes sélection complexes : les vues

Nous avons utilisé le mot clé WHERE pour poser une jointure entre deux tables et si nous voulions aussi effectuer un filtrage en plus on serait bien ennuyé ... on ne peut pas avoir deux clauses WHERE dans la même requête.

Il faut alors utiliser la clause INNER JOIN pour poser une jointure entre deux tables.
Le code SQL devient alors :
SELECT nom_espece, taxons.code as code, nom_taxon FROM especes INNER JOIN taxons ON especes.code = taxons.code
Notez l'emploi d'un alias pour une colonne sélectionnée : taxons.code as code.

Nous allons maintenant sauvegarder cette requête sous forme d'une vue.
A partir de la fenêtre "Exécuter le SQL" cliquez sur l'icône légendé "Enregistrer la vue des résultats". Nommons la vue "vue1".
Donc une vue c'est tout simplement une requête sélection enregistrée.

Une requête sélection multitables très complexe

Je vous propose maintenant une requête avec un code SQL assez lourd ...

SELECT nom_espece, taxons.code as code, nom_taxon FROM especes INNER JOIN taxons ON especes.code = taxons.code WHERE nom_espece BETWEEN "b%" AND "m%" ORDER BY nom_espece
Le rendu (totalité) :

nom_espece code	nom_taxon
balane		cirr	cirripèdes
bouquet		deca	décapodes
bulot		gast	gastéropodes
calmar		ceph	céphalopodes
cigale ...	deca	décapodes
cione		asci	ascidies
comatule	crin	crinoïdes
coque		biva	bivalves
crabe ..	deca	décapodes
crevette 	deca	décapodes
crépidule	gast	gastéropodes
dugong		sire	siréniens
fraise ...	asci	ascidies
homard		deca	décapodes
huitre 		biva	bivalves
lamantin	sire	siréniens
langouste	deca	décapodes
langoustine	deca	décapodes
lion de mer	pinn	pinnipèdes

Cette requête affiche les espèces dont le nom commence par les lettres "b" à "m" ; espèces classées par odre alphabétique.

Il est préférable, vu sa complexité, de la sauvegarder sous forme d'une vue ; je la nomme "vue2".

Requêtes statistiques

Parmi les requêtes SELECT il y a les requêtes statistiques.

Compter le nombre d'enregistrements d'une table / vue

Exemples :

Ces requêtes ne retournent qu'une ligne affichant un entier (le nombre de lignes de la source).

Compter le nombre de ligne par groupe

Dans la table "espèces" et pour chaque valeur de code il y a plusieurs lignes (plusieurs espèces par taxon).

SELECT code, COUNT(*) FROM especes GROUP BY code

Le rendu (extrait) :

 
...
biva	6
cart	2
ceph	3
cirr	2
crin	1
deca	11
...

Nous pouvons dire qu'il y a 4 biva(lves), 2 cart(ilagineux), etc.
Cette requête statistique comprend autant de lignes que de groupes ; requête statistique par groupe.

Les requêtes select MAX et select MIN

Ces deux requêtes retournent respectivement la valeur maxi et mini dans la colonne ID.
Remarque : suite à des suppressions de lignes, il peut y avoir des trous dans la numérotation : un enregistrement ne changera jamais d'ID même si des lignes qui le précédaient ont été supprimées.
a titre d'exemple la valeur maxi de ID dans ma table "especes" est de 56 alors que le nombre de lignes est 40. En effet de nombreuses lignes ont été supprimées dans cette table.

Autres types de requêtes

Pour supprimer tout ou partie des enregistrements dans une table il faut exécuter une requête "DELETE FROM nomtable".
Une requête DELETE FROM nomTable sans condition WHERE supprime toutes les lignes.
Par contre une requête DELETE FROM nomTable WHERE ... supprime seulement les lignes vérifiant la condition.

Cependant dès qu'une table est soumise à une contrainte d'intégrité référentielle ça devient un peu plus compliqué ...
Lisez le paragraphe qui suit.

Suppression de lignes dans la table "taxons"

Mais attention dans le cadre de l'intégrité référentielle on ne peut pas supprimer "une mère tant qu'elle a des filles".

Par contre rien n'interdit de supprimer des lignes dans la table "fille".
Essayez la commande : DELETE FROM especes WHERE code ='epon'.
C'est un succès

Structure actuelle de notre base

Compte tenu de toutes les modifications apportées la base "animaux.db" se présente comme suit :

Cet onglet affiche le code de création des tables mais aussi des vues.

La table "sqlite_sequence" est créée par le logiciel SQLite3. Elle est utilisée lorsqu'une colonne dans une table est AUTOINCREMENT .

Les dates & heures

Dans la base "animaux.db" aucun champ ne contient de date & heure.
Or c'est une problèmatique fréquente dans le cadre d'un SGBD. Ainsi il faut stocker des dates de naissance, des dates d'entrée, des dates de création de document, etc.

Je ne peux terminer ce chapitre sur SQLite sans vous montrer comment manipuler les dates & heures avec ce SGBD.
Plutôt que de créer une nouvelle base je vais rajouter (mais provisoirement) deux tables : "posts" & "eleves" à la base "animaux.db".

Concernant les types de champs, SQLite propose : integer, text, real, numérique mais pas vraiement de type date ...
Alors comment faire ???
Dans SQLite la problématique est différente selon qu'il s'agit de l'instant présent ou d'une date historique.

Stocker dans un champ l'instant de création de l'enregistrement

Par exemple dans le cadre d'un blog il faut afficher la date de création de chaque "post".

Structure de la table "posts"

CREATE TABLE "posts" ( "id" INTEGER, 
"date_creation" INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, 
"contenu" TEXT NOT NULL, 
PRIMARY KEY("id") )

Notez la clause : "date_creation" INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP

Pour définir cette table et plus précisément le champ "date_creation" il faut remplir la colonne "défaut" dans la grille avec la valeur "CURRENT_TIMESTAMP".
Ce qui veut dire que ce champ sera rempli automatique lors de la création de l'enregistrement avec le timestamp à ce moment précis. Le timestamp est le nombre de secondes écoulées depuis le 1er janvier 1970 et l'instant présent.

Contenu de la table "posts"

1	2023-12-01 18:40:17	Texte de la première ligne
2	2023-12-01 18:40:42	Texte de la deuxième ligne
3	2023-12-01 18:41:14	Texte de la troisième ligne

Heureusement un champ entier rempli avec des timestamps s'affiche sous forme d'une info lisible pour un francophone : date et heure (AAAA-MM-JJ HH:MM:SS)
Vous constatez que les lignes ont été créées le même jour (1er décembre 2023).

Requête sélection sur table "posts"

Je vous propose :
SELECT * FROM posts where date_creation < date('2024-01-01')
Requête qui retourne trois lignes, bien sûr.

Autre solution :
SELECT * FROM posts where year(date_creation) < 2024
Emploi de la fonction year() : pas d'erreur : affichage de trois lignes !

Saisir des dates historiques dans un champ ?

Cette fois le remplissage automatique du champ n'est pas envisageable puisque la date ne correspond pas à l'instant présent.
Il faut créer un champ de type text et saisir toutes les dates au même format : aaaa-mm-jj (avec des tirets)

Structure de la table "eleves"

CREATE TABLE "eleves" (
	"id"	INTEGER,
	"nom"	TEXT NOT NULL,
	"prenom"	TEXT NOT NULL,
	"sexe" integer default 1,
	"date_naissance"	TEXT NOT NULL,
	PRIMARY KEY("id" AUTOINCREMENT)
)

Excusez moi mesdames, je suis très macho : la valeur par défaut pour le champ "sexe" est 1 (masculin)
Je ferai remarquer cependant que les numéros de sécurité sociale commencent par 1 ou 2 pour préciser le sexe.

Contenu de la table

Saisir quatre individus dans cette table : un né en 1979, un autre en 1980, un troisième né en 1981 et un quatrième en 1982. Tous sont nés le 1er janvier ; deux femmes et deux hommes.

1	Dupont	louis	1	1979-01-01
2	Dubois	hélène	2	1980-01-01
3	Durant	julien	1	1981-01-01
4	Dumond  pauline	2	1982-01-01

Quelques requêtes sélection sur la table "eleves"

SELECT * FROM eleves WHERE date_naissance < date('1981-01-01')
Cette requête fonctionne : emploi de la fonction date argumentée avec une date au format (aaaa-mm-jj).

SELECT * FROM eleves WHERE year(date_naissance) = 1982
Cette fois ça ne marche pas car le champ date_naissance est de type text donc je ne peux employer la fonction year().

Vidage de tables

La commande "DELETE FROM nomTable" sans clause WHERE permet de vider une table de tout contenu ; par contre sa définition est sauvegardée. Donc vous obtenez une table vide.

Mais "eleves" & "posts" sont toujours dans la liste des tables et on retrouve toujours leur définition (create table ...) dans la structure de la base de données.

Il ne faut pas confondre vidage de table avec suppression de table ...

Suppression de tables

Il faut maintenant supprimer les tables provisoires "posts" & "eleves" de la base ; elles nous ont été utiles pour nos essais. Mais désormais nous n'en n'avons plus besoin.

Exécutez les commandes : DROP TABLE eleves & DROP TABLE posts
Les deux tables sont bien supprimées : elles ne sont plus dans la liste des tables et leur définition n'apparait plus.

Récupération de la base "animaux.db"

La base de données SQLite 'animaux.db'