Chapitre 9

Transcription

Chapitre 9
9 janvier 2010
Chapitre 9
Bases de données
9.1 Introduction
Les bases de données sont nécessaires au fonctionnement d'innombrables
applications sur la Toile. Chaque fois que vous commandez un billet pour
un spectacle ou un voyage, passez en revue des catalogues, recherchez des
informations, vous utilisez des bases de données.
Une base de données est composée d'un ensemble de tables telles que celle
qui est présentée ci-dessous.
+----+------------+----------+----------+---------+-------+
| id | name
| quantity | category | weight | price |
+----+------------+----------+----------+---------+-------+
| 1 | roue
|
76 | aa,c
| 145.000 | 12.45 |
| 2 | pneu
|
88 | b
| 319.600 | 62.60 |
| 3 | pare-brise |
44 |
| 59.100 | 52.20 |
+----+------------+----------+----------+---------+-------+
Une table comprend des lignes et des colonnes. Chaque colonne a un nom
et contient des valeurs du type (entier chaîne de caractères) attribué à la
colonne. Une ligne est aussi appelée enregistrement ou tuple.
Il y a plusieurs variantes du langage SQL, qui dièrent par de petits détails, mais nous nous tiendrons à la variante comprise par la base de données
MySQL, un environnement largement utilisé dans l'industrie et qui existe en
version gratuite. Les instructions que nous présentons ne représentent qu'un
sous-ensemble, mais il sura pour donner une bonne idée des concepts sousjacents et faire des applications intéressantes.
Le langage SQL permet de dénir la forme des tables, comme celle qui est
présentée ci-dessus, de les remplir, de rechercher des informations, de modi137
138
CHAPITRE 9.
BASES DE DONNÉES
er ces informations et nalement de détruire les tables ou des informations
qu'elles contiennent, quand elles sont périmées.
Dans les prochaines sections, nous présentons les commandes de SQL
(Structured Query Langage), puis nous verrons comment les mettre en ÷uvre
pour gérer des aspects plus globaux. Ces commandes peuvent être écrites sur
une ou plusieurs lignes et indentées comme bon vous semble.
9.2 Création d'une table (create)
La commande ci-dessous crée une table vide qui pourra contenir les informations apparaissant dans la table du paragraphe précédent.
create table produits (
id long auto_increment primary key,
nom varchar(20),
quantite int default 0,
categorie set('aa',"b",'c'),
poids double (10,3),
prix decimal (10,2)
);
Les deux premiers symboles ci-dessus, create table décrivent la commande.
Le troisième symbole est le nom de la table choisi par le développeur.
La suite consiste en une liste de types entre accolades et séparés par des
virgules. Le premier symbole de chaque ligne correspond à un nom choisi par
le développeur. Il ne doit évidemment pas comprendre de lettre accentuée,
ni d'espace. Le système ne fait pas de diérence entre une lettre majuscule
et la minuscule correspondante.
Le premier type est particulier. On met en général une colonne de ce type
dans chaque table. Il correspond à un numéro qui identie les lignes. Ces
numéros ne sont pas forcément dans l'ordre, mais chacun d'eux est unique,
c'est ce qui est assuré par les symboles primary key. auto_increment indique
que le numéro est géré par la base de données et que chaque fois qu'on entre
une nouvelle ligne dans la table (voir 9.5) la base de données lui attribue
automatiquement un nouveau numéro, de 1 plus élevé que le précédent.
La quatrième ligne montre comment dénir une valeur par défaut. Le
symbole default est valable pour toutes les lignes et permet de dénir une
valeur qui est utilisée si le programme entre une ligne sans dénir cette colonne.
9.2.
CREATE)
CRÉATION D'UNE TABLE (
139
9.2.1 Types et propriétés des colonnes
Voici les types de données principaux et quelques propriétés qu'on peut
attribuer aux colonnes.
Les lignes montrent les 6 types principaux :
int, long, integer
Représente un entier qui peut contenir des valeurs plus ou moins étendues.
Aujourd'hui, ces types permettent de mémoriser des nombres jusqu'à 263 .
varchar
Ce type correspond à une chaîne de caractères. Le nombre entre parenthèse indique le nombre maximal de caractères qui vont être mémorisés. Si
l'on essaie de déposer une chaîne plus longue, elle sera coupée.
double
Ce type mémorise un nombre réel, possédant donc des chires après la
virgule.
decimal(10,2)
Ce type permet de mémoriser des prix ou des valeurs ayant un nombre
déni de chires après la virgule. Le nombre de caractères après la virgule est
indiqué dans les paramètres. Le nombre ci-dessus possède 10 chires, dont 2
après la virgule. Ce type est utilisé pour représenter des sommes d'argent.
set ('aa', "b", 'c') (ensemble)
Ce type permet de créer un certain nombre de symboles représentant des
caractéristiques ou propriétés attribuées à un objet représenté dans la ligne
d'une table.
Par exemple on pourraît décider que les symboles aa, b et c 1 représentent
trois qualités : jante alu, sans chambre à air et enjoliveurs plastiques. En
lui attribuant ('aa,c'), la roue mémorisée ci-dessus aurait la première et la
dernière de ces qualités. Pour l'insertion dans une base de données, voir
paragraphe 9.5, attention, ne pas mettre d'espace dans la chaîne insérée.
1. les symboles ont été choisis pour que la table ne dépasse pas les bords de la page,
mais dans une application, on choisirait des symboles plus explicites, évidemment.
140
CHAPITRE 9.
BASES DE DONNÉES
time, date, datetime
Les champs time, date et datetime contiennent des dates et/ou des heures
sous forme de chaînes de caractères.
Une durée ou une heure est mise sous la forme "J HH :MM :SS",
"HH :MM :SS", "J HH", "HH :MM :SS", "HH :MM" ou "SS", où chaque
lettre représente un chire. La lettre J représente un nombre de jours.
Une date est représentée par la forme "AAAA-MM-JJ" où les lettres
représentent les années, jours et mois.
La concaténation des deux séparés par un espace, forme un tout mémorisé
dans un champ datetime.
Un certain nombre de fonctions permettent de manipuler des dates, voir
now() à date_add() page 149.
Pour l'insertion dans une base de données, voir paragraphe 9.12.4.
default
Le mot-clé placé sur la quatrième ligne de la commande de création cidessus indique la valeur que l'on veut voir introduite si l'on ne connaît pas
sa valeur au moment de l'introduction d'une nouvelle ligne.
9.3 Achage de la structure d'une table (describe)
La commande describe produits ; permet de visualiser la structure d'une
table, soit pour la vérier, soit pour générer des applications qui découvrent
toutes seules ce qu'il y a dans une table.
La commande ci-dessus, appliquée à la table précédente produit la description suivante, formatée en forme de table.
describe producis;
+-----------+------------------+------+-----+----------------+
| Field
| Type
| Null | Key | Extra
|
+-----------+------------------+------+-----+----------------+
| id
| int(11)
|
| PRI | auto_increment |
| nom
| varchar(20)
| YES |
|
|
| quantite | int(11)
|
|
|
|
| categorie | set('aa','b','c')| YES |
|
|
| poids
| double(10,3)
| YES |
|
|
| prix
| decimal(10,2)
| YES |
|
|
+-----------+------------------+------+-----+----------------+
9.4.
DESTRUCTION D'UNE TABLE (DROP)
141
On peut vérier que la table a une clé auto-incrementée et que la plupart
des colonnes peuvent être indénies (indiqué par la colonne Null).
9.4 Destruction d'une table (drop)
Une table peut être éliminée de la base au moyen de cette commande. On
élimine une table si on veut en construire une avec une autre structure, ce
qui arrive souvent au moment où l'on développe une nouvelle application et
que l'on a mal jugé les éléments dont on a besoin.
drop table produits;
9.5 Insertion de données (insert)
Quand une table a été créée, elle peut recevoir des lignes de données grâce
aux commandes suivantes. La première commande ajoute une ligne complète
dans la table produits. Comme le premier paramètre est une primary key, la
base de données vérie avant l'insertion que la valeur introduite pour cette
clé, dans la première position de values, n'est pas déjà présente dans une
autre ligne.
Dans la deuxième commande on a un 0 dans cette position. Comme la
première colonne contient également auto_increment, la base de données
introduit automatiquement le prochain index disponible.
insert into produits
values(7, 'porte', 93, "aa,cc", 67.200, 73.40);
insert into produits
values(0, 'roue', 76, "aa,cc", 145.0, 12.45)
(0, 'porte', default, "xx", 85.300, 136.20);
Cette deuxième forme montre également comment insérer simultanément
plusieurs lignes dans la base de données. Elle illustre nalement l'emploi de
default. Dans la dernière ligne, dans la colonne correspondant à quantite, on
a placé ce mot-clé. La base de données va donc charger la valeur dénie pour
ces cas dans la commande de création (section 9.2).
Notez que les valeurs numériques sont écrites sans délimiteurs et que les
chaînes de caractères sont encadrées par des guillemets ou des apostrophes.
La prochaine commande indique, après le nom de la table, les colonnes
qui vont apparaître dans la liste de valeurs. Comme l'identicateur n'est pas
inclus dans cette liste, la base de données introduit le prochain index, pour
142
CHAPITRE 9.
BASES DE DONNÉES
les raisons déjà invoquées. Les valeurs non dénies sont laissées vides dans la
base à moins qu'on ait déni des valeurs par défaut à la création.
insert into produits (nom, quantite, categorie, poids, prix)
values ('roue', 76, "", 145.0,12.45);
La commande ci-dessous introduit une troisième forme pour dénire l'insertion d'une ligne. Le paramètre de la clé primaire n'étant pas déni, il est
à nouveau généré automatiquement.
insert into produits
set name='pneu', quantite=88,categorie="bb",
weight=319.600, price=62.60;
9.6 Extraction d'information (select)
Les buts principaux d'une base de données sont l'extraction et le tri des
informations. Ainsi il y a de nombreuses formes de la commande aectée à
ces actions.
9.6.1 Sélection simple
Voici quelques exemples de la commande select suivis des tables qu'ils
peuvent produire.
Entre select et from, on place l'indication des colonnes dont on veut retourner les valeurs.
Dans le premier exemple, on a placé une étoile à cet endroit. Cela signie
que l'on désire voir toutes les colonnes.
select * from table1;
+----+------+------+------+
| id | aaa | bbb | ccc |
+----+------+------+------+
| 1 | x1 | x1 | x1 |
| 2 | x2 | x2 | x2 |
+----+------+------+------+
Dans la commande suivante,
9.6.
EXTRACTION D'INFORMATION (
SELECT)
143
select table2.* from table2;
+----+------+------+------+
| id | aa
| bb
| cc |
+----+------+------+------+
| 1 | y1 | y1 | y1 |
| 2 | y2 | y2 | y1 |
| 3 | y2 | y2 | y2 |
+----+------+------+------+
table2.* signie toutes les colonnes dénies dans table2. Dans la com-
mande ci-dessous, les colonnes à retourner sont identiées.
144
CHAPITRE 9.
BASES DE DONNÉES
select id, aa, bb, cc from table3;
+----+------+------+------+
| id | aa
| bb
| cc |
+----+------+------+------+
| 1 | y1 | y1 | y1 |
| 2 | y2 | y2 | y1 |
+----+------+------+------+
Aussi longtemps que les noms des colonnes ne sont pas ambigus, et donc
que soit il n'y a qu'un nom de table après from, soit les noms de colonnes des
tables dénies après from sont diérents, ils peuvent être inscrits sans nom
de table. Par contre, si l'on veut indiquer des colonnes de même nom dans
deux tables, il faut faire précéder le nom de colonne d'un nom de table et
d'un point (deux noms de table, voir paragraph 9.6.2).
select id, cc, table3.bb from table3;
+----+------+------+
| id | cc
| bb
|
+----+------+------+
| 1 | y1 | y1 |
| 2 | y1 | y2 |
+----+------+------+
9.6.2 Jointures
L'indication from peut être suivie par un nom seul ou par une liste de
noms de tables. Dans ce cas, il faut imaginer que cette liste crée une table 2
contenant le produit carthésien des lignes des tables listées, appelé jointure.
L'exemple ci-dessous illustre le produit des tables table1 et table2 apparaissant ci-dessus. La table résultante contiendra toutes les colonnes de table1
suivies des colonnes de table2. Chaque ligne de la pemière table apparaît en
combinaison avec chacune des lignes de la deuxième table. Comme ces tables
ont respectivement 2 et 3 lignes, la table résultante aura donc 6 lignes, ce
qu'on peut suivre en examinant les id des deux tables.
2. La plupart du temps, on extrait des éléments de cette table et le système ne la
construit pas en entier, mais crée directement l'extraction
9.6.
EXTRACTION D'INFORMATION (
SELECT)
145
select * from table1, table2;
+----+------+------+------+----+------+------+------+
| id | aaa | bbb | ccc | id | aa | bb | cc |
+----+------+------+------+----+------+------+------+
| 1 | x1 | x1 | x1 | 1 | y1 | y1 | y1
|
| 2 | x2 | x2 | x2 | 1 | y1 | y1 | y1
|
| 1 | x1 | x1 | x1 | 2 | y2 | y2 | y1
|
| 2 | x2 | x2 | x2 | 2 | y2 | y2 | y1
|
| 1 | x1 | x1 | x1 | 3 | y2 | y2 | y2
|
| 2 | x2 | x2 | x2 | 3 | y2 | y2 | y2
|
+----+------+------+------+----+------+------+------+
Si l'on ne dénit pas toutes les colonnes dans le select, il peut arriver que
des colonnes qui font la diérence entre deux lignes soient éliminées, auquel
cas des lignes risquent d'apparaître en doubles ou multiples exemplaires,
comme la table ci-dessous qui est extraite de la table précédente..
select aa,bb,bbb,ccc from table1, table2 ;
+------+------+------+------+
| aa
| bb
| bbb | ccc |
+------+------+------+------+
| y1
| y1
| x1
| x1 |
| y1
| y1
| x2
| x2 |
| y2
| y2
| x1
| x1 |
| y2
| y2
| x2
| x2 |
| y2
| y2
| x1
| x1 |
| y2
| y2
| x2
| x2 |
+------+------+------+------+
Les doublons peuvent être éliminés en utilisant le symbole DISTINCT,
comme montré ci-dessous.
select distinct aa,bb,bbb,ccc from table1,table2 ;
+------+------+------+------+
| aa
| bb
| bbb | ccc |
+------+------+------+------+
| y1
| y1
| x1
| x1 |
| y1
| y1
| x2
| x2 |
| y2
| y2
| x1
| x1 |
| y2
| y2
| x2
| x2 |
+------+------+------+------+
146
CHAPITRE 9.
BASES DE DONNÉES
9.6.3 Critères de sélection (where)
La clause where dénit les lignes qui sont sélectionnées dans la ou les tables
accédées. Cette clause contient une expression Booléenne qui peut être vraie
ou fausse pour chaque ligne. Seules les lignes pour lesquelles l'expression est
vraie sont retournées par la commande. Par exemple :
select * from table2 where id>1 and id<3;
+----+------+------+------+
| id | aa
| bb
| cc |
+----+------+------+------+
| 2 | y2 | y2 | y1 |
+----+------+------+------+
select table2.*,table3.* from table2, table3
where table2.aa=table3.aa and table2.bb=table3.bb
and table2.cc=table3.cc;
+----+------+------+------+----+------+------+------+
| id | aa
| bb
| cc | id | aa | bb | cc |
+----+------+------+------+----+------+------+------+
| 1 | y1 | y1 | y1 | 1 | y1 | y1 | y1
|
| 2 | y2 | y2 | y1 | 2 | y2 | y2 | y1
|
+----+------+------+------+----+------+------+------+
Dans la requête ci-dessous, les lignes qui sont prises en compte sont celles
dont la valeur de la colonne est contenue dans la liste retournée par le select
emboîté.
select * from table2 where id in (1, 2)
+----+------+------+------+
| id | aa
| bb
| cc |
+----+------+------+------+
| 1 | y1 | y1 | y1 |
| 2 | y2 | y2 | y1 |
+----+------+------+------+
L'ensemble placé à côté de IN peut également contenir des chaînes de
caractères. Cet ensemble peut être construit par un select qui retourne une
liste de valeurs. On appelle un tel select emboîté.
9.6.
EXTRACTION D'INFORMATION (
SELECT)
147
select * from table1 where id in (select id from table1)
+----+------+------+------+
| id | aa
| bb
| cc |
+----+------+------+------+
| 1 | y1 | y1 | y1 |
| 2 | y2 | y2 | y1 |
+----+------+------+------+
9.6.4 Fonction utilisables dans where
Le partie where d'une commande select peut utiliser les fonctions et opérateurs listés ci-dessous. On peut utiliser les parenthèses pour composer des
expressions.
=
and
>
or
<
<> (or !=)
not
is null
<=
>=
is not null
Pour illustrer de façon simple les résultats de certaines comparaisons,
elles sont introduites dans des sélections sans tables. Il est en eet possible
d'envoyer ces lignes à la base de données, pour vérier des résultats, mais elles
auraient évidemment le même eet si elles étaient placées entre des noms de
colonnes qui retournent les valeurs comparées. Le résultat d'une expression
Booléenne est soit 0 (false), soit 1 (true), soit null.
select 3<7,234+22 ; // produit une valeur Booléenne et un nombre
select 0=NULL ; // le résultat est NULL, pas false. Quand un des membres
risque d'être NULL il faut utiliser le comparateur suivant
select 1<=>NULL ; // le résultat est 0 (false). <=> a sinon la même signication que =
select 4="4aa" ; // une chaîne de caractères est transformée en un nombre
(jusqu'à la première lettre, comme en Javascript))
select 0="aa" ; // Si la chaîne ne commence par aucun chire, le système
la prend pour la valeur 0
select CURDATE()< DATE_ADD(CURDATE(), INTERVAL 1 DAY) ; // comparaison
de dates
9.6.5 Quelques-unes des fonctions dénies par MySQL
MySQL dénit une longue liste de fonctions qui peuvent être utilisées
après les select pour certaines d'entre elles et dans le corps des commandes
pour d'autres, en concordance avec les fonctions. Voici les plus utilisées.
148
CHAPITRE 9.
BASES DE DONNÉES
SUM (expr), AVG (expr), MIN (expr), MAX (expr)
Ces fonctions retournent la somme, la moyenne, le minimum et le maximum calculé sur toutes les lignes des colonnes désignées dans les expressions.
Un exemple est donné ci-dessous.
select avg(weight), sum(weight), min(weight) from table;
Il est possible d'utiliser une clause group by (voir paragraphe 9.6.7) pour
créer une liste de moyennes, par exemple pour chacun des étudiants.
count (*), count (expr), count (distinct expr1 . . .)
La première forme retourne le nombre de lignes sélectionnées par les paramètres qui suivent select.
select count(*) from table1;
La seconde forme retourne le nombre de fois que l'expression est non nulle.
select count(table1.bb) from table1;
La dernière forme retourne le nombre de lignes distinctes dont aucun des
champs placés dans les arguments n'est nul.
select count(distinct table1.bb, table1.cc) from table1;
LAST_insert_ID( )
Cette fonction retourne la dernière valeur créé par la clause auto_increment. Cela permet d'introduire cette valeur dans une autre ligne, par exemple
pour créer une relation (paragraphe 9.9)
insert
insert
insert
insert
into
into
into
into
products values( null, 'roue', 44, "", 59.1, 52.20);
reservations SET id={last_insert_id()};
products values( 0, 'door', 12, "xx", 85.3, 136.20);
reservations values ({ last_insert_id()}, default);
SUBSTRING(str, pos, len)
Cette fonction retourne la sous-chaîne partant de pos et de longueur len.
Attention, le premier caractère vaut 1.
9.6.
EXTRACTION D'INFORMATION (
SELECT)
149
strExpression like `xxx%yyy_zzz'
L'opérateur like retourne true si deux chaînes sont équivalentes. Le signe
% à droite de LIKE représente n'importe quelle séquence de caractères. Le
signe _ représente un seul caractère. Si un de ces caractères ou un \ doit être
déni, ils doivent être précédés par un \ : \%, \_ ou \\.
now()
Cette fonction retourne la date et l'heure dans une chaîne, telle que :
2003-06-1917:31:19
curdate()
Cette retourne la date courante.
curtime()
Cette fonction retourne l'heure courante.
date_add(date, interval 3 minute)
Cette fonction retourne une date augmentée de 3 minutes. Par exemple :
now()
date\_add(now(),interval 3 minute)
// 2003-06-19 17:35:32
// 2003-06-19 17:38:32
D'autres exemples de valeurs possibles sont données ci-dessous :
12 second
30 minute
2 hour
5 day
2 month
10 year
"1:30" minute\_second
"1:45" hour\_minute
"2:02:10" hour\_second
"1 12" day\_hour
"1 11:30" day\_minute
"1 11:30:10" day\_second
"1-6" year\_month
150
CHAPITRE 9.
BASES DE DONNÉES
date_sub(date, interval 3 minute)
Comme ci-dessus, mais pour la soustraction.
9.6.6 order by grouping_columns
Si l'on ajoute cette commande après un select, la base de données trie
les lignes selon les valeurs déposées dans les colonnes indiquées. On donne
ci-dessous quelques exemples. Les nombres correspondent aux colonnes spéciées à la suite du select. La première colonne a le numéro 1. Une colonne
suivie de desc est triée par ordre décroissant.
select table1.ccc, table2.cc
from table1, table2
order by 1 desc, 2 desc;
9.6.7 group by
Dans le cas simple, group by a le même eet qu'order. Dans le cas où un
select inclut des fonctions telles que AVG, count ou SUM, et fait référence
à plusieurs tables, group BY est obligatoire, et ces fonctions calculent alors
plusieurs valeurs, c'est-à-dire une pour chaque groupe indiqué après group
BY.
select table1.ccc, table2.cc
from table1, table2
group by table1.ccc, table2.cc desc;
9.7 Mise à jour des lignes d'une table
Les lignes peuvent être mises à jour au moyen des commandes suivantes.
update products
set weight=33.33
where id=2;
La commande suivante ajoute 2 au poids et ensuite 33.33, c'est-à-dire
35.33 au total.
update products
set weight=weight+2, weight=weight+33.33
where id=2;
9.8.
ELIMINATION D'UNE LIGNE D'UNE TABLE
9.8 Elimination d'une ligne d'une table
151
Les lignes peuvent être éliminées d'une table par la commande suivante.
delete from products
where products.weight<80;
La commande précédente eace les lignes dans lesquelles weight vaut
moins que 80. La commande retourne le nombre de lignes éliminées. S'il
n'y a pas de clause where toutes les lignes sont éliminées.
delete from products,reservations
where products.id=1 and reservations.id=products.id;
Une commande qui a plusieurs noms de table après le symbole from élimine des lignes dans plusieurs tables. Rappelez-vous que from products,
reservations représente le produit carthésien des lignes des deux tables,
c'est-à-dire une table dans laquelle les lignes sont faites d'une ligne de la
première table et d'une ligne de la seconde table.
9.9 Relations entre tables
Dans la plupart des applications des bases de données, une partie des
colonnes des tables font références à des lignes d'autres tables. On a, par
exemple, une table de clients, une table de factures une table de produits,
etc. Chaque facture est liée à un client et contient une liste de références à
des produits. Les connexions entre tables peuvent être très complexes, mais
nous resterons dans des cas relativement élémentaires.
La première table ci-dessous contient une liste de vins avec quelques caractéristiques et un numéro indiquant le marchand qui le livre. On pourrait
mettre directement le nom du marchand dans la table des vins, mais le marchand a d'autres caractéristiques : adresse, liste de livraisons en cours, etc.
et on ne peut pas mettre toutes ces caractristiques dans la table des vins. De
plus elles seront vraisemblablement utilisées dans d'autres tables.
Dans les tables ci-dessous, on sait par exemple que Jean livre du Bourgogne en lisant la colonne marchand de la table des vins. Le numéro mémorisé
dans la ligne Bourgogne est celui qui est attribué à Jean dans la colonne idm
de la table des marchands.
idv
1
2
3
region
Vins
cepage
Lavaux
chasselas
Chianti
sangiovese
Bourgogne pinot noir
annee marchand
2005
2002
2000
3
3
1
Marchands
idm
1
3
nom
Jean
Luc
Table marchands
refV
refM
relationVM
Table vins
152
CHAPITRE 9.
Table vins
0..n
Å livre
0..n
est livré par Æ
BASES DE DONNÉES
Table marchands
Figure 9.1 Relations entre vins et les marchands
Table produits
La relation entre ces tables est schématisée sur la gure 9.1. Notez que
dans une application réelle d'un peu d'importance, on a facilement une dizaine à plusieurs dizaines de tables.
no_p
En examinant les tables des vins
et des marchands,
onTable
pourrait
savoir qui
fournisseurs
no_u
no_f
Table usines
livre le chasselas en utilisant la première
instruction ci-dessous qui retourne le
livraisons
numéro du marchand et en exécutant la deuxième instruction avec le numéro
obtenu dans la première.
select marchand from vins where cepage="chasselas"
On obtient NUMERO. Le nom du marchand est donc obtenu par :
select nom from marchands where idm=NUMERO
Cependant il est possible de ne faire qu'une seule requête, ce qui est fait
normalement. Une première possibilité est donnée ci-dessous.
select nom from vins,marchands
where marchand=idm and cepage="chasselas"
Cette requête crée virtuellement le produit cartésien qui contient les lignes
suivantes :
idv
1
2
3
1
2
3
region
Lavaux
Chianti
Bourgogne
Lavaux
Chianti
Bourgogne
cepage
chasselas
sangiovese
pinot noir
chasselas
sangiovese
pinot noir
annee marchand idm nom
2005
2002
2000
2005
2002
2000
3
3
1
3
3
1
1
1
1
3
3
3
Jean
Jean
Jean
Luc
Luc
Luc
Parmi ces lignes, les lignes dans lesquelles les numéros des colonnes marchand et idm ne sont pas identiques ne représentent rien, alors que les autres
contiennent des informations cohérentes. Ces lignes sont retenues par la première partie du where ci-dessus (marchand=idm). Lorsque la requête précise
ensuite cepage="chasselas", on obtient alors la ligne recherchée et l'on peut
extraire la colonne qui nous intéresse, nom placé entre select et from.
Une autre façon de faire se base sur le select emboîté (9.6.3).
9.10.
153
RELATIONS N X M
refV
refM
relationVM
Table vins
Table marchands
Figure 9.2 Relation entre les marchands et les vins
Table vins
Å livre
0..n
est livré par Æ
marchands
0..n
Table marchands
select nom from
where idm in (select marchand from vins
where cepage="chasselas")
Table produits
Le select emboîté retourne la liste des numéros de marchands qui livrent
du chasselas. Le select principal retourne les noms pour les lignes dont la
colonne idm est contenue dans cette
liste.
no_p
Table usines
no_u
no_f
livraisons
9.10 Relations n × m
Table fournisseurs
Dans la section pécédente, on ne peut pas exprimer le fait qu'un marchand
peut fournir plusieurs vins et que chaque vin puisse être fourni par plusieurs
marchands. Pour cela, il faut construire une table supplémentaire qui établit
une telle relation entre deux autres tables.
idv
1
2
3
region
Vins
cepage
Lavaux
chasselas
Chianti
sangiovese
Bourgogne pinot noir
annee
2005
2002
2000
Marchands
idm
1
3
nom
Jean
Luc
relationVM
refV refM
2
3
3
1
1
1
3
3
La table ci-dessus nous indique que Jean (1) livre du Chianti (2) et du
Bourgogne (3). Luc (3) livre du Lavaux (1) et du Bourgogne (3).
Cette relation s'exprime au moyen du schéma de la gure 9.2.
refV
refM
relationVM
Table vins
154
0..n
Table vins
Table marchands
Å livre
0..n
Table marchands
est livré par Æ CHAPITRE 9. BASES
DE DONNÉES
Table produits
Table usines
no_p
no_u
no_f
livraisons
Table fournisseurs
Figure 9.3 Relations entre les 4 tables
9.11 Exercices
9.11.1 ex - Questions sur une structure existante
Le chier http://siteLivre/exoBD.txt contient une liste de commandes qui
créent les quatre tables ci-dessous, reliées entre elles selon la gure 9.3, et
qui y insère des données. Copiez le contenu de ce chier dans la fenêtre du
moniteur de SQL et exécutez-les.
Usines No_u, Nom_u, Ville
Produits No_p, Nom_p, Couleur, Poids
Fournisseurs No_f, Nom_f, Statut Ville
Livraisons No_p, No_u, No_f, Quantité
Déterminez ensuite les commandes qui répondent aux questions posées
ci-dessous.
1. Trouver le numéro, le nom et la ville de toutes les usines.
2. Trouver le numéro, le nom et la ville de toutes les usines de Lausanne.
3. Trouver les numéros des fournisseurs qui approvisionnent l'usine n◦ 1 en
produit n◦ 1.
4. Trouver le nom et la couleur des produits livrés par le fournisseur n◦ 1.
Ecrire les requêtes SQL permettant de répondre aux questions ci-dessous
5. Trouver les numéros des fournisseurs qui approvisionnent l'usine n◦ 1 en
un produit rouge.
6. Trouver les noms des fournisseurs qui approvisionnent soit une usine
de Lausanne soit une de Genève en un produit rouge.
7. Trouver les numéros des produits livrés à une usine par un fournisseur
de la même ville que l'usine.
9.12.
ACCÈS À LA BASE DE DONNÉES PAR JAVASCRIPT
155
8. Trouver les numéros des produits livrés à une usine de Lausanne par
un fournisseur de Lausanne.
9. Supprimer tous les produits de couleur rouge.
10. Changer la ville du fournisseur n◦ 1 : il a déménagé à Genève.
11. Trouver les numéros des usines qui ont au moins un fournisseur qui
n'est pas de la même ville que l'usine.
12. Trouver les numéros des fournisseurs qui approvisionnent à la fois les
usines n◦ 1 et n◦ 2.
13. Trouver les numéros des usines qui utilisent au moins un produit disponible chez le fournisseur n◦ 3 (c'est-à-dire un produit qu'il est capable
de livrer, mais que l'usine n'obtient pas forcément de ce fournisseur).
14. Trouver le numéro du produit le plus léger (les numéros, si plusieurs
produits ont ce même poids).
15. Trouver les numéros des usines qui ne reçoivent aucun produit rouge
d'un fournisseur de Lausanne.
16. Trouver les numéros des fournisseurs qui fournissent au moins un produit fourni par au moins un fournisseur qui fournit au moins un produit
rouge.
17. Trouver tous les triplets (VilleF, No_P, VilleU) tels qu'un fournisseur
de la première ville approvisionne une usine de la deuxième ville avec
un produit NP.
18. Même question qu'en 15, mais sans les triplets où les deux villes sont
identiques.
9.12 Accès à la base de données par Javascript
Les applications qui gérent des données placées dans une base, accèdent à
cette base au moyen de SQL. Or les réponses aux requêtes SQL sont adaptées
à la vision d'un humain, pas d'un programme. Pour aborder ce problème, on
utilise des librairies et des astuces présentés dans cette section.
Le système LemanOS permet à chaque utilisateur (pour lequel on a déni
un compte) d'accéder à une base de données et de faire des requêtes SQL et
de traiter les réponses de façon simple.
9.12.1 Librairies
Les librairies d'interface au système sont disponibles dans les chiers suivants, qu'il faut donc les importer dans les applications qui doivent gérer des
bases de données sur LemanOS.
156
CHAPITRE 9.
BASES DE DONNÉES
<script src='/LemanOS/dwr/engine.js'>
</script>
<script src='/LemanOS/database.js'>
</script>
9.12.2 Requêtes SQL en Javascript
Les requêtes SQL sont simplement placées dans des chaînes de caractères
et envoyées au moyen de l'instruction suivante :
var result = database.query("select * from vins")
Au retour des requêtes select, le résultat contient un tableau d'objets.
Pour les autres requêtes, le système retourne le nombre de lignes modiées ou
-1 s'il y a une erreur, comme le moniteur. Par exemple la requête précédente
eectuée sur la table ci-dessous :
idv
1
2
3
Vins
region
cepage
Lavaux
Bourgogne
chasselas
sangiovese
annee
2005
2002
retournerait les résultats qui suivent :
[
{'idv':1,'cepage':'chasselas','region':'Lavaux','annee':2005},
{'idv':2,'cepage':'sangiovese','annee':2002},
{'idv':3, 'region':'Bourgogne'}
]
Les champs nuls n'apparaissent pas dans les objets.
Supposant que le résultat soit déposé dans result comme indiqué dans la
requête, on obtient les diérents champs au moyen de
result[0].idv result[0].cepage result[0].region result[0].annee
result[1].idv result[1].cepage result[1].annee
Pour déterminer si un champ n'a pas été déni, il sut de faire le test :
if (!result[1].region) {
alert("Pas défini")
}
9.12.
ACCÈS À LA BASE DE DONNÉES PAR JAVASCRIPT
157
Le nombre de lignes retournées est évidemment obtenu par result.length.
Attention, même si le système ne retourne qu'une ligne, elle est placée dans
un tableau.
Pour détecter d'éventuelles erreurs d'exécution, on utilise la séquence
d'instructions ci-dessous :
var result
try {
result = database.query("select * from vins")
} catch(e) {
alert("Erreur: "+e+" <<"+database.readyQuery+">>")
return
}
// continue ici, en cas de succès
if (result.length==0) {
// pas d'erreur mais table vide
}
L'attribut database.readyQuery placé dans l'alerte contient la chaîne SQL
envoyée à la base de données après remplacement des ? . Cela aide certaines
fois à comprendre les erreurs.
9.12.3 Introduction de variables dans la requête SQL
Lorsque l'on veut introduire des données entrées par un utilisateur dans la
base de données, il faut bien adapter la commande SQL pour qu'elle contienne
les données actuelles. De même pour les sélections. Il y a plusieurs possibilités
de faire cela, ce qui permet d'entrer des données selon les diérentes formes
de SQL. Ces diérentes formes peuvent être combinées.
Concaténation
Dans la forme la plus simple la chaîne est formée par concaténation :
var laRegion = "Lavaux"
var result = database.query("select * from vins "
+"where region='"+laRegion+"'")
Attention, il faut introduire soi-même les apostrophes qui encadreront
Lavaux après la synthèse de la chaîne, comme sur le moniteur.
158
CHAPITRE 9.
BASES DE DONNÉES
Passage de paramètres
Pour simplier les instructions, on a introduit la possibilité de placer un
ou plusieurs points d'interrogations, qui seront remplacés par le système par
les paramètres qui suivent la chaîne SQL. Le système détecte lui-même s'il
s'agit de chaînes ou de nombres et ajoute lui-même les apostrophes.
var laRegion = "Lavaux"
var result = database.query("select * from vins "
+"where region=?", laRegion)
Tableau de valeurs
Dans la première forme de l'insert, il faut introduire une liste de valeurs.
Cela correspond exactement à un tableau, dans lequel l'ordre est respecté.
Dans l'exemple ci-dessous, le ? est remplacé par le tableau en deuxième paramètre.
var data = [0, 'Barolo', 'nebbiolo', null]
var result = database.query(
'insert into vins values (?)' ,
data )
data étant un tableau, le ? est remplacé par les valeurs de data séparées par des virgules. Les chaînes sont automatiquement encadrées par des
apostrophes.
Comme cette requête est une action, result reçoit le nombre de lignes
modiées : 1
Objet en paramètre
De même qu'on peut recevoir un objet, on peut transmettre un objet. Ce
dernier s'intègre parfaitement dans la deuxième forme d'insertion.
var vin = {'region':'Bordeaux', 'cepage':'merlot'}
var result = database.query(
'insert into vins set ?' , vin )
vin étant un objet, le ? est remplacé par les couples nom-valeur de la
variable vin séparés par des virgules. De nouveau, les chaînes sont encadrées
par des apostrophes.
9.12.
ACCÈS À LA BASE DE DONNÉES PAR JAVASCRIPT
159
9.12.4 Insertion d'une date
Le code ci-dessous crée la date courante et la stocke dans un champ
datetime.
date = new Date()
dateSQL = date.getFullYear() + "-" + (date.getMonth()+1)
+ "-" + date.getDate() + " " + date.getHours()
+ ":" + date.getMinutes() + ":" + date.getSeconds()
. . .
database.query("insert into unite set jourHeure=?",dateSQL)
Dans le code ci-dessous, on a complété l'objet Date de Javascript pour
faciliter la construction des dates en format SQL.
Date.prototype.sql = function () {
return this.getFullYear() + "-" + (this.getMonth()+1)
+ "-" + this.getDate() + " " + this.getHours()
+ ":" + this.getMinutes() + ":" + this.getSeconds()
}
. . .
database.query("insert into timeTable set jourHeure=?",
new Date().sql())
9.12.5 Obtention de la clé primaire
Lorsque la clé primaire est générée automatiquement, et qu'on veut l'introduire dans une autre table pour créer une relation, on peut l'obtenir en
appelant
idNb = database.last_insert_id()
juste après la requête qui a provoqué la création automatique d'une nouvelle
clé primaire.
9.12.6 Lecture de toutes les colonnes
Il est possible d'acher toutes les colonnes d'une réponse en les parcourant automatiquement. Notez que les champs sont dans n'importe quel ordre
et que cette façon de faire n'est utile que pour faire des essais. Dans une
application réelle, on ache les champs de façon spécique.
160
CHAPITRE 9.
BASES DE DONNÉES
Couche métier
Couche affichage
handleVins() {
lit les champs Æ objet
database.query("insert into Vins set ?", objet)
}
une ligne
Couche BD
tables de l’application
exec() {
var x = database.query("select * from Vins")
displayVinsM(x)
}
n lignes
Figure 9.4 Architecture 3 tiers
var result = [ {'a':2, 'b':3} ]
for ( key in result [0] ) {
document.write( result [0] [key] )
}
document.close()
9.13 Exercice
9.13.1 ex - Marchand de vins
On doit créer une application qui pourra gérer des vins, les marchands
qui les livrent et la quantité de bouteilles de chaque sorte de vin livrée par
chaque marchand.
Toute application de ce type comprend trois couches (voir gure 9.4) :
une couche d'achage, une couche de gestion des données et une couche
intermédiaire dit couche métier, qui va chercher les données dans la base de
données et les ache, ou lit ce que l'utilisateur a entré dans les tables HTML
d'achage et les insère dans la base de données.
La couche d'achage est contituée de pages HTML. Chaque page permet
de faire une ensemble cohérent d'opérations (gestion des marchands, des vins
gestion du stock, etc) en présentant deux sortes de tables HTML (gure 9.5 :
des tables qui ache les données d'une ligne d'une table SQL, les champs
étant placés les uns sous les autres et des tables HTML dont les lignes et les
colonnes reètent celles des tables SQL.
Des liens permettent de passer d'une page d'accueil aux diérentes pages
9.13.
161
EXERCICE
Figure 9.5 Tables HTML
d'opération, de revenir sur la page d'accueil, voire de sauter d'une page à
l'autre.
La couche de la base de données est accédée principalement au moyen
de SQL. Le moniteur SQL (menus d'outils) est utile dans cette couche pour
initialiser les tables et pour tester les commandes SQL avant de les intégrer
dans la couche métier.
Les diérentes opérations de la couche métier sont déclenchées au chargement des pages, lorsque l'utilisateur clique un bouton ou l'autre, ou lors
qu'il sélectionne les lignes des tables. Cette couche contient donc la logique
qui gère l'application.
Créez dans a base de données une table contenant des vins avec quelques
caractéristiques, une table de marchands avec leur nom ainsi qu'une
table dont chaque ligne représente un vin en stock, dont on connaît les
caractéristiques (référence dans la table des vins), le marchand qui l'a
livré et le nombre de bouteilles.
Créez une page HTML qui permet d'entrer des vins et des marchands.
Le système LemanOS permet de générer les tables HTML et des fonctions générales achant des lignes ou des groupes de lignes. Voir les
détails dans le générateur d'interfaces parmi les outils de ce système.
Créez une page qui permet d'acher tous les vins dans une première
table et tous les marchands dans une deuxième table. Ces tables peuvent
être chargées par l'attribut onload dans body, en faisant une requête
SQL sur les tables et utilisant les méthodes d'achages produites par
162
CHAPITRE 9.
BASES DE DONNÉES
le générateur.
Quand on clique sur une ligne d'une table à plusieurs lignes, une fonction est appelée. Cette fonction dépose le numéro de la ligne dans
une variable globale. Créer un bouton qui permet d'entrer un nombre
de bouteilles (champ d'input ) dans le stock en utilisant les lignes sélectionnées dans la table de vins et celle de marchands. Si le programme a gardé les tableaux de lignes qui ont servi à charger les tables
au point précédent, on peut lire les identicateurs dans ces tables :
vins[ligneSelectionnee].idv.
Créez un deuxième bouton qui permet de sortir du stock un nombre de
bouteilles correspondant au nombre dans le champ d'input.
Modiez la page précédente pour qu'on puisse sélectionner un vin et
ne voir dans la deuxième table que les marchands qui livrent ce vin.
Créez une page d'accueil qui permet d'appeler l'une des deux pages cidessus au moyen d'un lien indiquant les opérations disponibles. Mettez
des liens sur les deux pages pour revenir sur la page d'accueil.
9.13.2 ex - Application de gestion de fournisseurs
Dans cet exercice, on va gérer une base de données contenant des descriptions de fournisseurs, de produits et de commandes. Une table auxiliaire sera
utilisée pour dénir les quantités de produits en stock ou dans les commandes.
Une marche à suivre est disponible dans les résultats à obtenir liés aux
exercices de ce manuel.
9.13.3 ex- Organisation d'un musée
Dénir un diagramme entité-association représentant les faits suivants,
relatifs à un musée :
toute oeuvre du musée a un titre, un ou plusieurs auteurs, une date
d'acquisition et un numéro de catalogue (identiant) ;
une oeuvre est exposée dans l'une des salles du musée (qui est caractérisée par un numéro, son nom, le nombre d'oeuvres exposables, sol,
éclairage), ou est en prêt dans un autre musée (nom et adresse de ce
musée, début et durée du prêt) ;
certaines oeuvres exposées dans le musée peuvent avoir été empruntées
par le musée, soit à un autre musée, soit à un particulier (nom et
adresse). Dans ce cas, on connaît son titre, son (ou ses) auteur(s), la
date de début et la durée de l'emprunt. De plus, l'oeuvre doit alors être
assurée. On veut savoir le montant de la prime d'assurance, la valeur
9.13.
EXERCICE
163
pour laquelle l'oeuvre est assurée, le nom et l'adresse de la compagnie
qui l'assure ;
le conservateur garde le chier des musées et des particuliers qui ont
prêté ou qui sont susceptibles de prêter des oeuvres. Pour chacun (musée ou particulier), il garde le nom et l'adresse et la liste des collections
qui l'intéressent (art déco, art contemporain, antiquités, ...).

Documents pareils