Copyright © 2010 D.Gonzalez
lundi 11 octobre 2010 à 18h16
Table des matières
Liste des exemples
CREATE TABLE
Télécharger le fichier disques2009.sql qui contient toutes les instructions SQL nécessaires à la création de la base.
Utiliser d'abord la commande :
CREATE DATABASE base ;
où base est un nom à votre choix (qui ne doit pas déjà exister).
Connectez vous ensuite à votre base
\c base
Puis tapez (ou recopiez) la commande :
\i disques2009.sql
Cela aura pour effet de créer les tables et de les remplir.
Table des matières
Vous trouverez les réponses des exercices au Chapitre 5, Réponses aux exercices sur modification de base, etc..
L'utilisateur qui a créé les tables a tous les droits sur ces tables. Si rien n'est précisé aucun autre utilisateur ne peut faire la moindre modification sur les tables (contenu ou structure). Ce qui fait que vous ne pourrez tester les commandes de ce chapitre que sur une base de données que vous aurez créée personnellement (voir Chapitre 1, Créer votre propre base).
On étudiera plus tard (Chapitre 3, Les droits) la possibilité de donner (ou enlever) aux autres utilisateurs les droits de modifier la base de données.
Le principe de base des transactions est de grouper un ensemble de commandes de façon à être sûr que TOUTES les commandes seront exécutées, ou sinon qu'AUCUNE ne sera exécutée.
Dans quel but ? Pour pouvoir résoudre un certain de problèmes différents. Par exemple :
Imaginez une transaction bancaire : la banque doit débiter un compte pour en créditer un autre. Facile à réaliser avec deux commandes UPDATE. Mais que faire si une fois la première commande exécutée, on se rend compte que la deuxième ne peut pas l'être ? Il faut annuler la première. Regrouper les deux commandes dans une transaction permet d'automatiser ce comportement : si tout se passe bien les deux se feront, si un quelconque problème survient aucune ne se fera.
D'une manière plus générale, une suite de commandes quelconques qui entraîne des modifications de la base ne doit surtout pas être arrêtée en plein milieu, ce qui risquerait de laisser la base dans un état inconsistant. Une transaction transforme cette suite de commandes en un ensemble insécable (on dira aussi atomique) : ou TOUT se fait, ou RIEN ne sera fait.
Les transactions permettent aussi de gérer les accès concurrents dans une base en réseau : si deux (ou plus) utilisateurs utilisent en même temps la même base pour exécuter chacun une série de commandes du style :
commande1 : recherche d'une information dans une table (par exemple : calcul d'un stock disponible) ;
commande2 : modification de la table en fonction de l'information reçue (par exemple retrait d'un certain nombre d'articles dans la limite du stock diponible).
Dans ce cas rien ne permet de supposer que tout se passera bien. Il est tout à fait possible que la séquence de commandes de déroule de la façon suivante : commande1 pour le premier client, puis commande1 pour le deuxième client, puis commande2 pour le premier client, puis commande2 pour le deuxième client.
Dans ce cas la commande2 du deuxième client a toutes les chances d'entraîner des erreurs car elle est basée sur un résultat calculé avant l'exécution de la commande2 du premier client.
L'utilisation de transaction transforme chaque suite (commande1, commande2) en un bloc non interruptible (atomique) qui assure que l'information du deuxième ne sera fournie que quand le premier aura modifié la table.
Enfin il peut être utile de se ménager une porte de sortie quand on exécute une commande modifiant la table : on peut se tromper, changer d'avis, vouloir seulement faire un essai, etc. Il est toujours utile de disposer de l'équivalent du undo de la plupart des logiciels.
La seule solution est alors d'utiliser une transaction : une fois les commandes exécutées, on peut décider de tout abandonner (comme pour un undo) ou au contraire d'accepter tout.
Vous pourrez trouver d'autre explications sur l'utilité des transactions dans les pages suivantes :
Une transaction commence par « BEGIN ; » :
Si elle se termine par « ROLLBACK ; », aucune des modifications faites ne sera prise ne compte.
Si elle se termine par « COMMIT ; », toutes les modifications sont prises en compte.
Ne pas taper « BEGIN ; » avant de commencer vos modifications revient à taper chacune de vos commandes en mode autocommit : chaque instruction isolée est une transaction en elle-même, une fois tapée elle est exécutée comme si elle était précédée d'un « BEGIN ; » et suivie d'un « COMMIT ; », et elle ne peut plus être défaite.
Toutes les commandes qui vont suivre dans ce chapitre vont modifier votre base de données. Nul n'est à l'abri d'une erreur, il vous est donc fortement conseillé d'inclure vos commandes dans des transactions, pour vous permettre d'annuler éventuellement toute fausse manœuvre.
Dans notre cas il ne se produira rien de castrophique : cette base n'a pas d'importance vitale, et si vous y faites des erreurs, il vous reste toujours la possibilité de la recréer complétement (Chapitre 1, Créer votre propre base).
Dans la vraie vie c'est rarement le cas. C'est donc une bonne habitude à prendre...
La commande CREATE TABLE permet de créer une table. On la fait suivre d'une liste (entre parenthèses) des champs accompagnés de leur type.
Exemple 2.1. CREATE TABLE
La table CHANSON a été créée par l'instruction :
CREATE TABLE chanson (
cha_num INTEGER NOT NULL PRIMARY KEY,
cha_titre CHARACTER VARYING,
cha_genre INTEGER REFERENCES genre(gen_num),
cha_texte CHARACTER VARYING,
cha_libre BOOLEAN,
cha_modif TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP);Quelques détails :
REFERENCES genre(gen_num) signifie que le champ cha_genre est une clef étrangère (ou FOREIGN KEY) faisant référence au champ gen_num de la table GENRE.
DEFAULT désigne une valeur par défaut : si ce champ n'est pas rempli lors d'une commande INSERT (voir la section intitulée « Insertion de lignes ») c'est cette valeur qui sera utilisée.
La commande INSERT permet de créer une nouvelle ligne dans une table.
Par exemple, pour insérer une nouvelle ligne dans la table ARTISTE, et par la même occasion tester les transactions, essayez la suite de commandes que voici :
BEGIN ; /* début de transaction */ /* insertion d'une ligne dans la tableARTISTE*/ INSERT INTO artiste VALUES (9999 , 'Arnaud' , 'Bodinoz' , NULL ) ; SELECT * FROM artiste WHERE art_num>9000 ; /* liste, pour vérifier l'insertion */ ; ROLLBACK ; /* on annule la transaction */ SELECT * FROM artiste WHERE art_num>9000; /* la nouvelle ligne n'est plus là */ BEGIN ; /* on recommence... */ INSERT INTO artiste VALUES (9999 , 'Arnaud' , 'Bodinoz' , NULL ) ; SELECT * FROM artiste WHERE art_num>9000 ; /* on vérifie à nouveau */ COMMIT ; /* on confirme la transaction */ SELECT * FROM artiste WHERE art_num>9000 ; /* la nouvelle ligne est toujours là */
La mise à jour n'est prise en compte que si l'intégrité des données est satisfaite.
Par exemple, si on veut insérer une nouvelle ligne dans la table ARTISTE avec le numéro d'artiste non renseigné :
INSERT INTO artiste /* cela produit une erreur */ VALUES (null,'Sophie', 'Fonfec', NULL) ; SELECT * FROM artiste WHERE art_nom ='Fonfec' ; /* l'insertion n'a pas eu lieu */
Même quand on ne souhaite pas remplir toutes les colonnes on peut insérer une nouvelle ligne dans la table ARTISTE sans utiliser la clause NULL pour les valeurs non renseignées.
BEGIN ;
/* on crée une nouvelle ligne en ne remplissant que 2 champs */
INSERT INTO artiste (art_num, art_nom)
VALUES (10000, 'Steppenwolf') ;
SELECT *
FROM artiste
WHERE art_nom = 'Steppenwolf' ;
COMMIT ;
La commande UPDATE permet de mettre à jour dans une table une ou plusieurs colonnes pour une ou plusieurs lignes.
Pour mettre à jour le prix d'achat du disque dont le titre est Cannonball, en l'augmentant de 10%, il faudra taper :
BEGIN ; /* ça DOIT être un réflexe avant unUPDATE*/ SELECT * FROM disque WHERE dis_titre ='Cannonball' ; UPDATE disque /* modification */ SET dis_prixachat = dis_prixachat * 1.1 WHERE dis_titre = 'Cannonball' ; SELECT * /* vérification */ FROM disque WHERE dis_titre ='Cannonball' ; COMMIT ; /* confirmation */
La commande DELETE permet de supprimer une ou plusieurs lignes d'une table. Si elle n'est pas suivie d'une restriction (WHERE, avec la même syntaxe que pour SELECT), c'est tout le contenu de la table qui est effacé.
La commande DROP TABLE permet de détruire une table (lignes et structure).
Pour supprimer totalement la table AUTORISATIONS :
DROP TABLE autorisations ;
Pour la recréer voir Chapitre 1, Créer votre propre base.
La commande ALTER TABLE permet de modifier la structure de la table, même si elle contient des données.
Les principales façons de l'utiliser sont :
ALTER TABLEnomRENAME TOnouveau_nom;renommer une table
ALTER TABLEnomRENAMEcolonneTOnouvelle_colonne;renommer une colonne d'une table
ALTER TABLEnomADDcolonne type;ajouter une colonne à une table
ALTER TABLEnomDROPcolonn;supprimer une colonne
ALTER TABLEnomALTERcolonneTYPEtype;changer le type d'une colonne
ALTER TABLEnomOWNER TOnouveau_propriétaire;changer le propriétaire d'une table
Une vue est une table virtuelle, c'est-à-dire dont les données ne sont pas stockées dans une table de la base de données, et dans laquelle il est possible de rassembler des informations provenant de plusieurs tables. On parle de « vue » car il s'agit simplement d'une représentation des données dans le but d'une exploitation visuelle. Les données présentes dans une vue sont définies grâce à une clause SELECT.
Une vue est une définition dynamique dans le sens suivant : si on modifie, ou si on ajoute ou si on supprime des enregistrements, chaque exécution de la vue comprendra ces modifications.
La norme SQL propose un ensemble important de restrictions pour la modification ou l'insertion ou la modification des données dans les vues. Les systèmes de gestion de base de données ont aussi chacun leur implantation de ce concept et chacun leurs contraintes et restrictions. En particulier, peu d'opérations sont autorisées dès qu'une vue porte sur plusieurs tables ; aucune n'est possible si la vue comporte des opérateurs d'agrégation.
En PostgreSQL les vues ne sont que consultables par des instructions SELECT. Aucune autre opération n'est possible. Par contre, c'est la notion propre à PostgreSQL de règles qui assure cette fonctionnalité. Cette notion s'avère plus souple et puissante que les restrictions communément appliquées aux SGBD classiques.
La commande CREATE VIEW permet de créer une vue relative à une ou plusieurs tables de la base. Elle est suivie de AS puis d'une requête qui la définit.
Vous trouverez les réponses des exercices au Chapitre 6, Réponses aux exercices sur les droits.
Une table ayant été créée, son propriétaire peut accorder (GRANT) ou retirer (REVOKE) à un autre utilisateur (ou à tous : PUBLIC) les droits suivants :
La syntaxe en est :
GRANT droit ON table TO user ; REVOKE droit ON table FROM user ;
À tout moment vous pouvez afficher les droits actuellement donnés par la commande « \dp » ou
« \z »[1].
Accorder le droit de SELECT sur la table GENRE à un autre user.
Accorder les droits de INSERT et UPDATE sur GENRE à un autre user.
Accorder tous les droits sur la table DISQUE à un autre user.
Créer une vue relative au titre, à l'artiste (prénom+nom+groupe) et à l'année des disques. Puis accorder les droits de SELECT sur la vue à un autre user
Créer une vue relative à toutes les informations (titre de la chanson, titre du disque, atiste du disque, position sur le disque, année du disque) des chansons interpétées par David BOWIE. Puis accorder tous les droits sur la vue à un autre user.
[1] Ces commandes ne sont pas du SQL, mais des commandes de l'interface psql. Elles peuvent différer d'une version à l'autre. Vous pouvez obtenir le liste de toutes les commandes disponibles en tapant « \? ».
Table des matières
Vous trouverez les réponses des exercices au Chapitre 7, Réponses aux exercices sur les index.
Les index nous permettent de retrouver rapidement les données contenues dans une table. Utilisons un exemple pour illustrer l'utilité des index : supposons que nous sommes intéressés à lire des informations sur la culture de piments dans un livre de jardinage. Au lieu de commencer la lecture dès le début jusqu'à parvenir à la section sur les piments, il est beaucoup plus rapide d'aller à l'index en fin de livre, repérer les pages contenant les informations au sujet des piments, puis aller directement à ces pages. Aller à l'index en premier lieu nous permet d'épargner du temps et c'est une méthode largement plus efficace pour repérer les informations dont nous avons besoin.
Le principe est le même quant à retrouver des données d'une table de bases de données. Sans index, le système de base de données balaie la table entière (ce processus est appelé table scan (balayage de table)) pour trouver les informations recherchées. Avec l'index créé, le système de la base de données peut d'abord repérer dans l'index l'emplacement des données, puis aller directement à ces emplacements afin d'obtenir les données requises. C'est beaucoup plus rapide.
L'instruction CREATE INDEX construit un index sur la table spécifiée. Les index sont principalement utilisés pour améliorer les performances de la base de données (bien qu'une utilisation inappropriée puisse produire l'effet inverse).
La syntaxe de base est :
CREATE INDEX nom d'index
ON nom de table (nom de colonne) ;Les champs clé pour l'index sont spécifiés à l'aide de noms des colonnes ou par des expressions écrites entre parenthèses. Plusieurs champs peuvent être spécifiés si la méthode d'indexation supporte les index multi-colonnes.
Un champ d'index peut être une expression calculée à partir des valeurs d'une ou plusieurs colonnes de la ligne de table. Cette fonctionnalité peut être utilisée pour obtenir un accès rapide à des données obtenues par transformation des données basiques. Par exemple, un index calculé sur upper(col) autorise la clause WHERE upper(col) = 'JIM' à utiliser un index.
La gestion des index nécessite des calculs complexes qui peuvent s'avérer gourmands en temps de calcul. En cas d'ajouts massifs dans une table indexée (et encore plus si elle possède plusieurs index), le temps nécessaire à la mise à jour de l'index peut être prohibitif.
Il peut être alors préférable de supprimer les index, d'ajouter les enregistrements, et de recréer les index.
Ces inconvénients peuvent survenir en cas d'ajout d'un très grand nombre d'enregistrements, de l'ordre de plusieurs milliers. Même pour quelques centaines d'enregistrements ajoutés, ces inconvénients sont souvent négligeables.
Ces ralentissements sont proportionnels au nombre d'index existant sur la table. Il est donc important de savoir se limiter et de ne créer que les index nécessaires.
Vous trouverez les énoncés correspondant au Chapitre 2, Modification de base, transactions, tables et vues.
Solution de l'exercice 2.
BEGIN ; SELECT COUNT(*) FROM artiste WHERE art_modif IS NULL ; UPDATE artiste SET art_modif = CURRENT_TIMESTAMP WHERE art_modif IS NULL ; SELECT COUNT(*) FROM artiste WHERE art_modif IS NULL ; COMMIT ;
Solution de l'exercice 3.
BEGIN ;
SELECT *
FROM autorisations
WHERE aut_droits IN (SELECT dro_num
FROM droits
WHERE dro_nom='Membre') ;
UPDATE autorisations
SET aut_droits = (SELECT dro_num
FROM droits
WHERE dro_nom='Anonyme')
WHERE aut_droits IN (SELECT dro_num
FROM droits
WHERE dro_nom='Membre') ;
SELECT *
FROM autorisations
WHERE aut_droits IN (SELECT dro_num
FROM droits
WHERE dro_nom='Membre') ;
COMMIT ;
Solution de l'exercice 4.
BEGIN ; DELETE FROM artiste WHERE art_nom = 'Steppenwolf' ; COMMIT ;
Solution de l'exercice 5.
CREATE VIEW disqueartiste
AS SELECT dis_titre AS da_titre, art_prenom AS da_prenom,
art_nom AS da_nom, art_groupe AS da_groupe,
dis_num AS da_disnum, art_num AS da_artnnum
FROM disque, artiste
WHERE dis_artiste = art_num ;
SELECT *
FROM disqueartiste
WHERE da_nom LIKE 'Ar%' ;
UPDATE artiste
SET art_nom = 'Arnaud'
WHERE art_nom = 'Arno' ;
SELECT *
FROM disqueartiste
WHERE da_nom LIKE 'Ar%' ;
Vous trouverez les énoncés correspondant au Chapitre 3, Les droits.
Solution de l'exercice 1.
GRANT SELECT
ON genre
TO nom de l'autre user ;
Solution de l'exercice 2.
GRANT INSERT,UPDATE
ON genre
TO nom de l'autre user ;
Solution de l'exercice 3.
GRANT ALL ON disque TO nom de l'autre user ;
Solution de l'exercice 4.
CREATE VIEW disqvue
AS SELECT TRIM(art_prenom||' '||art_nom||' '||art_groupe) AS artiste,
dis_titre AS titre,
dis_annee AS annee
FROM disque,artiste
WHERE dis_artiste = art_num ;
GRANT SELECT ON disqvue
TO nom de l'autre user ;
Solution de l'exercice 5.
CREATE VIEW davidBowie
AS SELECT cha_titre AS "titre de la chanson",
dis_titre AS "titre du disque",
TRIM(d.art_prenom||' '||d.art_nom||' '||d.art_groupe) AS "artiste",
int_numero AS "position sur le disque",
dis_annee AS "année du disque"
FROM disque,artiste d,artiste b,chanson,interprete
WHERE cha_num = int_chanson
AND int_disque = dis_num
AND dis_artiste = d.art_num
AND int_artiste = b.art_num
AND LOWER(b.art_prenom) = 'david'
AND LOWER(b.art_nom) = 'bowie'
AND LOWER(b.art_groupe) = '' ;
GRANT ALL
ON davidBowie
TO nom de l'autre user ;
Solution de l'exercice 6.
REVOKE INSERT ON genre FROM nom de l'autre user ;
Vous trouverez les énoncés correspondant au Chapitre 4, Les index.
Solution de l'exercice 1. Écrire une ligne de commandes commençant par
SELECT NOW();
contenant ensuite 10 fois
SELECT cha_num,cha_titre FROM chanson WHERE cha_titre ='Lady Stardust';
et se terminant par
SELECT NOW();
Il vous reste à faire la différence des deux temps, et à diviser par 10... Vous avez alors la durée nécessaire à l'exécution de cette requête sans index.
Pour créer l'index :
CREATE INDEX idx_nom ON chanson (cha_titre) ;
En recommençant la première opération vous pourrez calculer la durée nécessaire à l'exécution de cette requête avec index. Cette dernière durée doit être beaucoup plus petite que la première.
Solution de l'exercice 2.
DROP INDEX idx_nom ;