le poly SQL
Transcription
le poly SQL
Présentation de SQL A. Terlutte December 11, 2006 Ce document est un mémento simplifié des principales commandes du Structured Query Language. Il ne se veut pas complet ; cela serait inutilement complexe (la documentation PostgreSQL contient 1500 pages). N’hésitez pas à vous reporter à l’aide en ligne, aux différents manuels ou à internet... Toutes les commandes données dans ce mémento sont des formes simplifiées des commandes SQL. Pour avoir la syntaxe complète, reportez-vous aux manuels de références. Différents manuels de référence de PostgreSQL sont téléchargeables à partir de l’adresse suivante : http://www.postgresql.org/docs/manuals/ Les commandes PSQL sont données en annexe. Préambule Prenez l’habitude de nommer correctement vos identifiants même si les noms sont longs. Cela permet de savoir à quoi correspond un identifiant... même si c’est plus long à écrire ! villes_codepostal_index chercheurs_nochercheur_pk reservations_refexpose_fk Par contre, à l’intérieur de certaines commandes, vous pourrez utiliser des alias. 1 INTERROGATION Toute la puissance d’interrogation du langage SQL est contenue dans une seule commande : la commande select SELECT [ ALL | DISTINCT ] * | expression [ AS AliasColonne ][, ...] [ FROM ClauseFrom [, ...]] [ WHERE condition ] , [ GROUP BY expression [, ...]] [ HAVING condition [, ...]] [ { UNION | INTERSECT | EXCEPT } [ ALL ] select ] [ ORDER BY expression [ ASC | DESC ][, ...]] où ClauseFrom peut être : NomTable [ [ AS ] AliasTable [ ( AliasColonne [, ...] ) ] ] ( select ) [ AS ] AliasTable [ ( AliasColonne [, ...] ) ] ClauseFrom [ NATURAL ] TypeJointure ClauseFrom [ ON ConditionJointure ] L’alias de colonne peut servir dans les sections GROUP BY ou ORDER BY mais pas dans les autres expressions de calcul ou de conditions. La section 2 développe quelques possibilités offertes pour écrire des expressions SQL. Les conditions sont des expressions à résultat booléen ; reportez-vous aussi à la section 2 pour les conditions. 1 Les expressions dans les colonnes peuvent utiliser les fonctions AVG(expression ), MIN(expression ), MAX(expression ), SUM(expression ), COUNT(expression ). On peut préciser ALL ou DISTINCT dans la fonction (qui ne tient compte que des valeurs non nulles). La fonction Count peut ne pas s’appliquer à un champ : COUNT(*). Si on ouvre deux tables simultanément, le résultat de la sélection est le produit (cartésien) des deux tables. Exécutez, pour vérifier cette assertion, la commande select count(*) from salles; select count(*) from exposes; select count(*) from exposes,salles; La jointure s’obtient par la sélection d’un attribut qui est commun aux deux tables. select codeexpose,codesalle from exposes,salles where codesalle=refsalle; Pour éviter toute ambiguïté, lorsque plusieurs tables sont ouvertes, il faut préciser le nom de la table avant le nom du champ select exposes.codeexpose,salles.codesalle from exposes,salles where salles.codesalle=exposes.refsalle; On peut utiliser des alias pour les noms de tables select ex.codeexpose,sl.codesalle from exposes as ex,salles as sl where sl.codesalle=ex.refsalle; On peut aussi relier deux tables par l’opérateur JOIN select ex.codeexpose,sl.codesalle from exposes as ex join salles as sl on sl.codesalle=ex.refsalle; Si les champs de la jointure portent le même nom dans les deux tables, on peut simplifier l’écriture avec NATURAL JOIN ; la jointure s’effectue alors sur tous les champs de même nom. Sur plus de deux tables, la technique est similaire select co.nomeco, au.nomeco from ((economistes as co left join exposes as ex on co.codeeco=ex.refconf) join reservations as re on ex.codeexpose=re.refexpose) join economistes as au on re.refeco=au.codeeco; L’opérateur LEFT JOIN permet de relier deux tables en faisant apparaître tous les éléments d’une table même si la condition de jointure n’est pas satisfaite. select co.nomeco, ex.titre from economistes as co left join exposes as ex on co.codeeco=ex.refconf); fera apparaître tous les économistes même s’ils ne font pas d’exposés. Il existe évidemment un opérateur RIGHT JOIN. Vous remarquerez aussi que la syntaxe de ClauseFrom autorise l’utilisation d’une autre clause select. On peut ainsi utiliser le résultat d’une requête comme étant une nouvelle table ; il faut obligatoirement utiliser un alias pour cette sous-requête. December 11, 2006 à 22h02 2/39 December 11, 2006 à 22h02 Une sous-requête peut aussi être utilisée dans une condition ; dans ce cas, l’alias n’est pas utilisé. Dans une condition, la sous-requête peut apparaître là où une liste aurait pu être utilisée. Il s’agit des conditions utilisant les opérateurs IN, > ALL, >= ALL,... > ANY,... ; la sous-requête doit fournir une liste, donc une seule colonne. Une sous-requête peut aussi apparaître, lorsqu’elle fournit une valeur unique, avec des opérateurs classiques de comparaison =, >... Avec l’opérateur EXISTS, une ligne de la requête principale sera affichée si la sous-requête est non vide. Dans la sous-requête, il y a souvent une référence à la requête principale. Dans le cas des opérations algébriques sur les requêtes (union, intersection, différence), il est nécessaire que les différentes requêtes aient la même structure (même nombre de colonnes et même type de données). 2 FONCTIONS ET EXPRESSIONS PostgreSQL offre de nombreuses fonctions. La plupart des fonctions que l’on rencontre dans les langages de programmation sont disponibles ; si vous avez besoin d’une opération ou d’un calcul sur certains types de données, consultez l’adresse suivante pour vérifier s’il n’existe pas déjà : http://www.postgresql.org/docs/8.1/interactive/functions.html Voici quelques fonctions ou opérateurs disponibles en SQL. 2.1 expressions arithmétiques Les opérateurs arithmétiques habituels sont disponibles mais SQL en propose d’autres % modulo 11%3 vaut 2 ˆ exposant 4ˆ3 vaut 4*4*4=64 |/ racine carrée |/ 9 vaut 3 ... Toutes les fonctions classiques existent et de nombreuses autres... 2.2 expressions caractères L’opérateur || effectue la concaténation de chaînes de caractères. Toutes les fonctions classiques existent : length(chaîne ), lower(chaîne ), substring(chaîne [from PositionDépart ] [for NombreCaractères ] ), position(SousChaîne in chaîne )... et de nombreuses autres, avec des syntaxes particulières. 2.3 expressions dates Les opérateurs + et - permettent aussi d’ajouter ou de soustraire des dates, des horaires et des entiers... Reportez-vous aux documentations pour connaître les différentes possibilités. Les dates devraient être, par défaut, sous la forme année-mois-jour heure:minute:seconde mais la configuration peut spécifier d’autres formats. Dans une expression, une date ou un horaire peuvent souvent être écrites directement entre apostrophes (exemple ’2006-11-27’) mais il faut parfois préciser le type (exemple date ’2006-11-27’) Les fonctions CURRENT_DATE et CURRENT_TIME fournissent la date et l’heure actuelles. La fonction EXTRACT permet d’extraire toutes sortes d’informations contenues dans une date ou d’un horaire (l’année, les minutes,... mais aussi le trimestre, le numéro de semaine...). Interval présente une quantité dans une certaine unité. December 11, 2006 à 22h02 3/39 December 11, 2006 à 22h02 Voici quelques exemples d’expressions valides : date ’2001-09-28’ + interval ’1 hour’ date ’2001-09-28’ + time ’03:00’ timestamp ’2001-09-28 01:00’ + interval ’23 hours’ date ’2001-10-01’ - date ’2001-09-28’ date ’2001-10-01’ - integer ’7’ 900 * interval ’1 second’ 2.4 vaut vaut vaut vaut vaut vaut ’2001-09-28 01:00:00’ ’2001-09-28 03:00:00’ ’2001-09-29 00:00:00’ integer ’3’ date ’2001-09-24’ interval ’00:15:00’ expressions booléennes On peut tester si une expression, souvent une colonne, est nulle expression IS NULL expression IS NOT NULL Les conditions peuvent utiliser les opérateurs habituels (=, <> etc...) mais aussi expression expression expression expression BETWEEN expression AND expression IS [ NOT ] NULL [ NOT ] LIKE ’modèle ’ avec les jokers % pour une chaîne quelconque et _ pour un caractère [ NOT ] IN (liste | selection ) Une structure CASE permet de faire un calcul alternatif CASE WHEN condition THEN expression WHEN condition THEN expression .... ELSE expression END 3 CREATION D’UTILISATEURS 3.1 Création de la base à partir du terminal CREATEUSER options NomUtilisateur Les options principales sont - -createdb l’utilisateur aura le droit de créer des bases de données - -no-createdb l’utilisateur n’aura pas le droit de créer des bases de données - -createrole l’utilisateur aura le droit de créer des utilisateurs - -no-createrole l’utilisateur n’aura pas le droit de créer des utilisateurs - -pwprompt l’utilisateur aura à utiliser un mot de passe - -host hote vous précisez la machine hote sur laquelle se trouve le serveur. - -username utilisateur vous précisez le nom de l’utilisateur qui crée ces nouveaux utilisateurs. Il existe des abréviations -d pour - -createdb ou -h pour - -host ; reportez-vous aux documentations pour les connaître. 3.2 Création d’un utilisateur ou d’un groupe CREATE USER NomUtilisateur [ WITH ] options est maintenant un alias de la commande CREATE ROLE NomUtilisateur [ WITH ] options Les options principales sont createdb l’utilisateur aura le droit de créer des bases de données December 11, 2006 à 22h02 4/39 December 11, 2006 à 22h02 nocreatedb l’utilisateur n’aura pas le droit de créer des bases de données createrole l’utilisateur aura le droit de créer d’autres utilisateurs. nocreaterole l’utilisateur n’aura pas le droit de créer d’utilisateur. login indique que l’utilisateur aura le droit de se connecter. nologin indique que l’utilisateur n’aura pas le droit de se connecter ; utile pour créer un compte d’administrateur. C’est l’option par défaut. in role NomGroupe crée l’utilisateur et le place dans un groupe. 4 CREATION DE BASE Une fois la base conçue, c’est à dire une fois le MCD et le MLD conçus, quelques commandes sont nécessaires pour créer d’abord la base, puis chacune des tables. 4.1 Création de la base à partir du terminal La commande createdb permet de créer une base sans avoir à lancer le terminal postgreSQL par la commande psql. CREATEDB - -host hote - -username utilisateur NomBase La documentation de postgreSQL renvoie vers les explications de la commande CREATE DATABASE mais il est plus logique de créer une base de données sans être connecté à une autre base. 4.2 Création de la base Lorsque vous êtes dans un terminal postgreSQL, vous pouvez créer une nouvelle base par la commande CREATE DATABASE mais cela signifie que vous êtes déjà connecté à une base... CREATE DATABASE NomBase [ [ WITH ] [ OWNER [=] Proprétaire ] [ TEMPLATE [=] BaseModèle ] [ ENCODING [=] encodage ] ] Le nom de la base ne doit pas exister. Par défaut, le créateur est propriétaire. La commande PSQL \c permet de se connecter à une base, donc de changer de base de travail. 4.3 Renommage de la base ALTER DATABASE NomBase RENAME TO NouveauNom 4.4 Suppression de la base DROP DATABASE NomBase 5 5.1 STRUCTURE DE TABLE Types de données Les types les plus courants sont : VARCHAR(longueur ) ou CHARACTER VARYING(longueur ) INTEGER REAL NUMERIC(LongueurTotale,NombreDécimales ) DATE December 11, 2006 à 22h02 5/39 December 11, 2006 à 22h02 BOOLEAN .... Mais voici la liste quasi-complète des types SQL (tout au moins en PostgreSQL 8.1.4) : Name Aliases Description bigint int8 signed eight-byte integer bigserial serial8 autoincrementing eight-byte integer bit [ (n) ] fixed-length bit string bit varying [ (n) ] varbit variable-length bit string boolean bool logical Boolean (true/false) box rectangular box in the plane bytea binary data ("byte array") character varying [ (n) ] varchar [ (n) ] variable-length character string character [ (n) ] char [ (n) ] fixed-length character string cidr IPv4 or IPv6 network address circle circle in the plane date calendar date (year, month, day) double precision float8 double precision floating-point number inet IPv4 or IPv6 host address integer int, int4 signed four-byte integer -2147483648 to +2147483647 interval [ (p) ] time span line infinite line in the plane lseg line segment in the plane macaddr MAC address money currency amount numeric [ (p, s) ] decimal [ (p, s) ] exact numeric of selectable precision path geometric path in the plane point geometric point in the plane polygon closed geometric path in the plane real float4 single precision floating-point number smallint int2 signed two-byte integer -32768 to +32767 serial serial4 autoincrementing four-byte integer text variable-length character string time [ (p) ] [ without time zone ] time of day time [ (p) ] with time zone timetz time of day, including time zone timestamp [ (p) ] [ without time zone ] date and time timestamp [ (p) ] with time zone timestamptz date and time, including time zone La time zone spécifie le décalage horaire. Le type money est déconseillé ; le type numeric est conseillé pour le remplacer. Les types serial et bigserial sont des types autoincrémentés, tout à fait adaptés aux identifiants. Définir une colonne de ce type définit une séquence qui servira pour la génération des valeurs insérées dans cette colonne. Il faudra utiliser le mot clé DEFAULT lors des insertions dans la table. La séquence sera suivie indépendamment des suppressions dans la table ou des insertions n’utilisant pas DEFAULT. Ceci peut mener à des doublons et la déclaration d’une colonne de type serial ne dispense pas de la contrainte UNIQUE. 5.2 Création de table La création d’une table nécessite la définition du nom de la table et la définition des colonnes qui la composent. Chaque colonne a un nom et un type de données. De plus, il est possible de définir certaines contraintes d’intégrité de la table : structure : la valeur de la clé primaire est unique et toujours définie, domaine : les valeurs prises par un attribut doivent vérifier des contraintes, December 11, 2006 à 22h02 6/39 December 11, 2006 à 22h02 référence : les valeurs d’une clé étrangère doivent correspondre à des valeurs existantes dans la table d’origine. La vérification de certaines contraintes peut être demandée dès la création de la table. Elle sera assurée par le système. CREATE TABLE NomTable ( { NomColonne Type [ DEFAULT expression ] [ ContrainteColonne [ ... | ContrainteTable } [, ... ] ) ] ] où ContrainteColonne peut être : [ CONSTRAINT NomContrainte ] { NOT NULL | NULL | UNIQUE | PRIMARY KEY | CHECK (expression ) | REFERENCES TableReliée [ ( ColonneReliée ) ] } et ContrainteTable peut être : [ CONSTRAINT NomContrainte ] { UNIQUE ( NomColonne [, ... ] ) | PRIMARY KEY ( NomColonne [, ... ] ) | CHECK ( expression ) | FOREIGN KEY ( NomColonne [, ... ] ) REFERENCES TableReliée [ ( ColonneReliée [, ... ] ) ] } D’après la syntaxe, le nom de contrainte est facultatif. Mais il est préférable de nommer toutes les contraintes ; dans certains cas, c’est même obligatoire (quand plusieurs contraintes portent sur une même colonne). 5.3 Modifications de table ALTER TABLE [ ONLY ] NomTable ADD [ COLUMN ] NomColonne type [ ContrainteColonne [ ... ] ] ALTER TABLE [ ONLY ] NomTable DROP [ COLUMN ] NomColonne ALTER TABLE [ ONLY ] NomTable ALTER [ COLUMN ] NomColonne { SET DEFAULT expression | DROP DEFAULT } ALTER TABLE [ ONLY ] NomTable ALTER [ COLUMN ] NomColonne { SET | DROP } NOT NULL ALTER TABLE [ ONLY ] NomTable RENAME [ COLUMN ] NomColonne TO NouveauNomColonne ALTER TABLE NomTable RENAME TO NouveauNomTable ALTER TABLE [ ONLY ] NomTable December 11, 2006 à 22h02 7/39 December 11, 2006 à 22h02 ADD NouveauNomTable ALTER TABLE [ ONLY ] NomTable ADD ContrainteTable ALTER TABLE [ ONLY ] NomTable DROP CONSTRAINT ContrainteTable ALTER TABLE NomTable OWNER TO NouveauPropriétaire 5.4 Suppression de table DROP TABLE NomTable [, ...] 5.5 Quelques exemples alter table chercheurs add constraint chercheurs_nochercheur_pk primary key (nochercheur); alter table parcelles add constraint parcelles_refsite_fk foreign key (refsite) references sites(nosite); alter table parcelles add constraint testlg check (longueur>largeur); 6 INDEXATION Les index accélèrent les recherches mais alourdissent la base. Si vous estimez qu’un champ va être l’objet de recherches fréquentes, il peut être judicieux de créer un index sur ce champ. Par exemple, PostgreSQL crée une table d’index lorsque l’utilisateur déclare une clé primaire. Les index servent évidemment principalement lors de la commande SELECT, pour les jointures... mais servent aussi pour les commandes UPDATE, DELETE... On peut aussi créer des index sur des expressions, en créer sur une partie de table... Il existe différents types d’index (B-Tree, R-Tree, Hash, GiST) correspondant à différentes organisations dans les tables d’index. Les B-Trees sont utilisés par défaut et correspondent aux utilisations les plus fréquentes, mais il faut savoir que d’autres types sont possibles : pour une recherche en deux dimensions,... Les Hash index n’ont d’intérêt que pour une recherche avec un critère d’égalité. Ils offrent des performances de recherche équivalentes à celles des B-Trees, mais leur taille et leur temps de construction sont meilleurs. Nous ne donnerons que l’utilisation des B-Trees. La commande la plus simple pour construire une table d’index est la suivante. CREATE INDEX NomIndex ON NomTable (NomChamp ) Si les recherches sont fréquentes, on peut aussi indexer sur une expression. CREATE INDEX NomIndex ON NomTable (expressions ); On peut aussi indexer sur plusieurs champs. December 11, 2006 à 22h02 8/39 December 11, 2006 à 22h02 CREATE INDEX test2_mm_idx ON test2 (major, minor); sera utile pour des recherches comme SELECT colonne FROM test2 WHERE major = constant AND minor = constant; Il peut être nécessaire de créer des index à entrées uniques. Il serait interdit d’avoir deux lignes de la table ayant même clé, même valeur d’index. CREATE UNIQUE INDEX NomIndex ON NomTable (NomColonne [, ...]) ATTENTION : quand l’utilisateur crée des contraintes primary key ou unique sur une table, PostgreSQL génère automatiquement des index uniques. Les créer par la commande create index dupliquerait les tables d’index. Enfin on peut créer un index sur une partie de table si on est sûr que les recherches ne porteront que sur cette partie. CREATE INDEX NomIndex ON NomTable (NomChamp ) WHERE condition Cela diminue la taille de la table d’index, accélère la recherche et la mise à jour. Si une recherche s’effectue en dehors des valeurs de la table d’index, celle-ci n’est tout simplement pas utilisée. CREATE INDEX factures_montant_idx ON factures (montant) WHERE paye is not true; L’index serait utilisé par postgreSQL même pour des requêtes n’utilisant pas le montant comme SELECT nofacture FROM factures WHERE paye is not true AND nofacture like ’2006*’; L’exemple suivant illustre une méthode garantissant des lignes uniques sur une partie de la table, multiples sur le reste. CREATE TABLE tests ( subject text, target text, success boolean, ... ); L’index va permettre de garantir que pour les lignes ’successfull’, la correspondance sujet/cible est unique. CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target) WHERE success; Dans les applications réelles, l’utilisation de tables d’index est une question majeure. Mais déterminer si un index va améliorer l’accès à une table est un problème complexe. PostgreSQL préconise l’emploi des commandes ANALYZE et EXPLAIN sur les données réelles. Lors de la création d’une table avec les lignes qu’elle contient, il est préférable d’insérer d’abord les lignes puis de créer les index plutôt que de mettre à jour un index à chaque insertion. December 11, 2006 à 22h02 9/39 December 11, 2006 à 22h02 7 CONTENU D’UNE TABLE 7.1 Insertion de lignes INSERT INTO NomTable [ ( NomColonne [, ...] ) ] { DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] ) | requête } Si aucune colonne n’est spécifiée les valeurs sont affectées aux colonnes dans l’ordre déterminée par la structure. Vous pouvez donc soit créer une ligne ayant les valeurs par défaut, soit créer une ligne en donnant explicitement les valeurs, soit remplir la table avec le résultat d’une requête. On peut aussi remplir une table avec des données contenues dans un fichier. COPY NomTable FROM ’chemin/nomfichier ’ copy essaitable from ’/Users/alain/Documents/basesdedonnees/donnees.txt’; Les données dans le fichier sont séparées par des tabulations, sans autres délimiteurs. 7.2 Modification de lignes UPDATE [ ONLY ] NomTable SET NomColonne = { expression | DEFAULT } [, ...] [ WHERE condition ] La mise à jour peut évidemment calculer une valeur à partir de la valeur actuellement dans la table. update parcelles set longueur=longueur-1 where noparcelle=1; 7.3 Suppression de lignes DELETE FROM [ ONLY ] NomTable [ WHERE condition ] Attention : la condition est facultative, mais sans condition, tous les enregistrements sont supprimés !!! 8 PRIVILÈGES Le propriétaire d’une table peut attribuer aux autres utilisateurs, les droits (privilèges) d’utilisation de la table. GRANT { { SELECT | INSERT | UPDATE | DELETE } [,...] | ALL [ PRIVILEGES ] } ON [ TABLE ] NomTable [, ...] TO { NomUtilisateur | GROUP NomGroupe | PUBLIC } [, ...] De même, il peut retirer les droits sur une table à ces utilisateurs. REVOKE [ GRANT OPTION FOR ] { { SELECT | INSERT | UPDATE | DELETE } [,...] | ALL [ PRIVILEGES ] } ON [ TABLE ] NomTable [, ...] FROM { NomUtilisateur | GROUP NomGroupe | PUBLIC } [, ...] December 11, 2006 à 22h02 10/39 December 11, 2006 à 22h02 Il existe aussi des droits ou privilèges pour une base de données (droit de création), pour des fonctions (droit d’exécution),... GRANT { { CREATE | TEMPORARY | TEMP } [,...] | ALL [ PRIVILEGES ] } ON DATABASE dbname [, ...] TO { username | GROUP groupname | PUBLIC } [, ...] [ WITH GRANT OPTION ] Expérimentation 1. 1. Concevez le MCD de la base filmo. Concevez le script de création de la base (avec le logiciel analyseSI, par exemple). 2. Créez votre base filmo. Afin de nommer les bases avec une certaine organisation, faites suivre le nom de votre base par votre propre nom. Exemple : create database filmo_terlutte; 3. Créez les tables avec leurs clés primaires, clés étrangères et autres contraintes. 4. Ajoutez quelques éléments dans chacune des tables. 5. Vérifiez auprès de votre voisin le nom de sa base de données. Connectez-vous à sa base. Essayez de consulter les différentes tables. 6. Demandez qu’il vous donne les droits de consulter et de modifier mais ni d’ajouter, ni de supprimer des lignes dans ses tables. 7. Faites quelques modifications de lignes. 9 TRANSACTION Toutes les commandes qui agissent sur le contenu d’une base sont “dangereuses” ; certaines sont irréversibles. Il est prudent de réaliser ses instructions à l’intérieur d’une transaction. Une transaction permet de regrouper une suite d’instructions pour la considérer comme une instruction élementaire. Les transactions permettent aussi de gérer les conflits. Une transaction commence par l’instruction START TRANSACTION ou par BEGIN et se termine par COMMIT si vous voulez valider la transaction ou ROLLBACK si vous voulez annuler la transaction. Expérimentation 2. December 11, 2006 à 22h02 11/39 December 11, 2006 à 22h02 1. Connectez-vous à votre base filmo 2. Commencez une transaction. 3. Ajoutez quelques éléments dans une des tables (la table rayons par exemple). 4. Affichez le contenu de la table. 5. Terminez la transaction par la commande Rollback. 6. Affichez de nouveau le contenu de la table. 7. Recommencez la transaction mais terminez par Commit. 8. Affichez le contenu de la table. 10 10.1 CONFLITS Présentation des conflits possibles Pour comprendre comment les transactions permettent de gérer les conflits, regardons les différents cas d’accès concurrents à une même information qui provoquent des conflits. Il faut imaginer deux personnes travaillant sur la même base de données. 10.1.1 Lecture inconsistante T1 T2 update enregistrement select enregistrement rollback Premier cas de conflit, l’utilisateur T2 met à jour un enregistrement. L’utilisateur T1 lit l’enregistrement. Mais l’utilisateur T2 annule sa modification. L’utilisateur T1 a donc lu une information qui n’a pas été validée. 10.1.2 Lecture non répétitive T2 T1 select enregistrement 1 (consultation) select enregistrement 2 (consultation) update enregistrement 1 update enregistrement 1 Deuxième cas de conflit, l’utilisateur T1 consulte un enregistrement, puis un autre afin de savoir lequel modifier. Il choisit de modifier l’enregistrement 1. December 11, 2006 à 22h02 12/39 December 11, 2006 à 22h02 Mais, après qu’il ait lu les informations et avant qu’il ne fasse sa modification, l’utilisateur T2 modifie l’enregistrement 1. L’utilisateur T1 modifie donc un enregistrement qui n’est pas celui qu’il a lu ; peut-être même, l’enregistrement ne vérifie plus les raisons pour lesquelles l’utilisateur T1 l’avait choisi. 10.1.3 Lignes fantômes T2 T1 select enregistrement 1 (sommation) select enregistrement 2 (sommation) select enregistrement 3 (sommation) update enregistrement 2 delete enregistrement 3 delete enregistrement 4 update enregistrement 5 insert enregistrement 6 select enregistrement 5 (sommation) select enregistrement 6 (sommation) Autre cas de conflit, l’utilisateur T1 commence une consultation d’une série d’enregistrements dans le but de faire un calcul sur l’ensemble de la série (par exemple, une sommation), Mais, pendant qu’il effectue son parcours , l’utilisateur T2 modifie des enregistrements : il en modifie, en supprime, en ajoute. Il est clair que l’utilisateur T1 ne fera pas son calcul sur les enregistrements qui existaient au début du calcul. Au début du calcul, les enregistrements corrects sont les enregistrements 1, 2, 3, 4 et 5. Mais il ne verra pas l’enregistrement 4 qui est supprimé entre temps et il verra l’enregistrement 6 qui est ajouté ; de plus, l’enregistrement 3 est supprimé et les enregistrements 2 et 5 sont modifiés. 10.2 Outils de gestion des conflits Pour assurer que l’utilisateur T1 effectue des instructions cohérentes, il faut qu’il puisse verrouiller les enregistrements sur lesquels il veut travailler. Une solution serait de verrouiller complètement la table, c’est à dire d’interdir l’accès à cette table pendant qu’on travaille dessus. Mais cela serait au dépend de l’efficacité. Il existe donc différents niveaux de verrouillage. verrou partagé (shared lock) utilisé pour lire un enregistrement. Les autres utilisateurs peuvent lire l’enregistrement et peuvent poser le même type de verrou sur cet enregistrement. Mais il ne peuvent pas poser un autre type de verrou. verrou exclusif (exclusive lock) utilisé pour modifier un enregistrement. Les autres utilisateurs ne peuvent pas lire l’enregistrement et ne peuvent poser aucun verrou sur l’enregistrement. verrou global (global lock) utilisé pour bloquer une série d’enregistrements. Les autres utilisateurs ne peuvent pas lire la série d’enregistrements et ne peuvent poser aucun verrou sur la série. 10.3 Solutions aux conflits L’utilisation des verrous permet d’apporter des solutions aux conflits. Il s’agit de trouver le meilleur verrou à appliquer pour éviter un problème sans gêner abusivement les autres utilisateurs. December 11, 2006 à 22h02 13/39 December 11, 2006 à 22h02 10.3.1 Solution à la lecture inconsistante Pour éviter la lecture inconsistante, l’utilisateur T2 devrait verrouiller l’enregistrement qu’il pense modifier afin qu’aucun autre utilisateur ne risque de lire une information erronée. T1 T2 verrou exclusif sur enregistrement update enregistrement attente enregistrement ... rollback select enregistrement En posant un verrou exclusif, l’utilisateur T2 protège les autres utilisateurs contre des lectures inconsistantes. L’enregistrement est libéré à la fin de la transaction. 10.3.2 Solution à la lecture non répétitive Vous pouvez vérifier que si l’utilisateur T2 pose un verrou exclusif sur l’enregistrement, l’utilisateur T1 n’est pas protégé contre la lecture non répétitive. Ici c’est l’utilisateur T1 qui doit se protéger contre ce problème. On voit donc que l’utilisation des verrous constitue une stratégie que tous les utilisateurs doivent respecter. Si l’utilisateur T1 veut garantir qu’un enregistrement ne changera pas de valeur, il doit le verrouiller. Il peut le faire de telle façon que les autres puissent lire mais pas modifier l’enregistrement. T1 T2 verrou partagé sur enregistrement 1 select enregistrement 1 verrou partagé sur enregistrement 2 select enregistrement 2 attente verrou exclusif sur enregistrement 1 .... update enregistrement 1 commit verrou exclusif sur enregistrement 1 update enregistrement 1 commit En posant un verrou partagé, l’utilisateur T1 protège l’accès à l’enregistrement 1 ; il autorise la lecture de l’enregistrement mais interdit sa modification. 10.3.3 Solution aux lignes fantômes Dans le cas d’un traitement qui nécessite la cohérence d’une série d’enregistrements, l’utilisateur doit verrouiller la totalité de la série. December 11, 2006 à 22h02 14/39 December 11, 2006 à 22h02 T1 T2 verrou global select enregistrement 1 (sommation) select enregistrement 2 (sommation) select enregistrement 3 (sommation) attente verrou exclusif sur enregistrement 2 ... select enregistrement 4 (sommation) select enregistrement 5 (sommation) verrou exclusif sur enregistrement 2 update enregistrement 2 verrou exclusif sur enregistrement 3 delete enregistrement 3 ... L’utilisateur T1 pose un verrou sur la totalité de la table ; aucun autre utilisateur ne peut modifier les enregistrements concernés. 10.4 Modes de transaction Le langage SQL propose quatre modes d’isolation qui permettent d’éviter les cas de conflits que nous venons de voir. La théorie définit READ UNCOMMITTED aucun verrou READ COMMITTED toute transaction qui désire modifier un enregistrement acquiert un verrou exclusif sur celui-ci, jusqu’à la fin de ladite transaction. REPEATABLE READ la transaction acquiert des verrous partagés sur tous les enregistrements lus. SERIALIZABLE verrou global (souvent par blocs d’enregistrements). niveau d’isolation read uncommitted read committed repeatable read serializable lecture inconsistante possible impossible impossible impossible lecture non répétitive possible possible impossible impossible lignes fantômes possible possible possible impossible Evidemment, le mode serializable est le plus protégé, mais il interdit les accès concurrents. PostgreSQL n’utilise que deux niveaux d’isolation et les traite d’une façon particulière. READ COMMITTED La transaction voit des copies de la base prises au début des requêtes. Ces copies contiennent les modifications effectuées par des transactions qui se sont terminées par commit. C’est le fonctionnement par défaut. Deux requêtes successives peuvent donc afficher des résultats différents si des transactions ayant modifié les enregistrements se sont terminées par commit. Pour résumer, vous pouvez lire les données mais vous n’êtes pas sûrs qu’elles ne seront pas modifiées à la prochaine lecture. Mais si vous voulez lire les données, autant lire leurs mises à jour. SERIALIZABLE December 11, 2006 à 22h02 15/39 December 11, 2006 à 22h02 La transaction voit les enregistrements effectivement modifiés (et validés par un commit) avant le début de la transaction. PostgreSQL essaiera de réaliser les différentes transactions concurrentes. Mais il est possible que des modifications soient impossibles parce que d’autres transactions impliquant les mêmes enregistrements auront été validées par commit. Pour résumer, vous accédez aux données prises au début de la transaction. Pendant des transactions concurrentes, chacun voit ses modifications. Si vous modifiez l’enregistrement A pendant qu’une transaction concurrente modifie l’enregistrement B, postgreSQL synchronisera les deux transactions. A la fin des transactions, les enregistrements A et B seront modifiés. Mais si vous essayez de modifier un enregistrement qu’une autre transaction (pas forcément serializable) a déjà modifié, vous êtes bloqué en attendant le rollback ou le commit du concurrent. Si la transaction concurrente valide sa modification, un message vous indiquera que la transaction a échoué. Dans ce cas, toute votre transaction doit être annulée. READ UNCOMMITTED est traité comme READ COMMITTED REPEATABLE READ est traité comme SERIALIZABLE. Vous définissez le mode d’isolation lors de la commande de début de transaction. START TRANSACTION [ ISOLATION LEVEL { READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE } ] [ READ WRITE | READ ONLY ] La commande set transaction précise les caractéristiques des transactions suivantes qui commencent par begin. SET TRANSACTION [ ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE } ] [ READ WRITE | READ ONLY ] Expérimentation 3. 1. Ouvrez deux terminaux. 2. Connectez-vous à votre base filmo dans les deux. 3. Créez une table essai avec un seul champ entier . create table essai (e int); 4. Exécutez les commandes suivantes en respectant la chronologie. dans le terminal 1 dans le terminal 2 insert into essai values (1); insert into essai values (2); insert into essai values (3); insert into essai values (4); insert into essai values (5); insert into essai values (6); insert into essai values (7); select * from essai; Constat. 5. Continuez dans le terminal 1 dans le terminal 2 start transaction isolation level read committed; start transaction isolation level read committed; update essai set e=11 where e=1; select * from essai; select * from essai; update essai set e=22 where e=2; select * from essai; select * from essai; commit; select * from essai; select * from essai; commit; December 11, 2006 à 22h02 16/39 December 11, 2006 à 22h02 Constat : chacun voit ses modifications. Il voit celles des autres lorsqu’elles sont validées par commit. 6. Continuez dans le terminal 1 dans le terminal 2 start transaction isolation level serializable; start transaction isolation level serializable; update essai set e=33 where e=3; update essai set e=44 where e=4; select * from essai; select * from essai; commit; select * from essai; select * from essai; commit; select * from essai; Constat : chacun voit ses modifications. Mais il voit celles des autres seulement lorsque les validations par commit sont faites. PostgreSQL effectue les deux modifications parce qu’elles portent sur des enregistrements différents. 7. Continuez dans le terminal 1 dans le terminal 2 start transaction isolation level serializable; start transaction isolation level serializable; update essai set e=55 where e=5; select * from essai; select * from essai; update essai set e=555 where e=5; commit; rollback; Constat : le terminal 1 va être bloqué en attente soit de commit, soit de rollback. Si terminal 2 valide sa modification par commit, terminal 1 ne peut qu’annuler la sienne. 8. Continuez dans le terminal 1 dans le terminal 2 start transaction isolation level serializable; start transaction isolation level serializable; update essai set e=66 where e=6; select * from essai; select * from essai; update essai set e=666 where e=6; rollback; rollback; Constat : Sa modification passera si terminal 2 invalide la sienne. 9. Continuez dans le terminal 1 start transaction isolation level read committed; dans le terminal 2 start transaction isolation level serializable; select * from essai; update essai set e=77 where e=7; select * from essai; update essai set e=777 where e=7; commit; select * from essai; commit; select * from essai; December 11, 2006 à 22h02 17/39 December 11, 2006 à 22h02 Constat : terminal 2 va être bloqué. Après déblocage par le commit, il ne pourra pas continuer ses modifications. Et même son commit sera remplacé par rollback. 10. Pour bien comprendre la différence dans le terminal 1 delete from essai; insert into essai values (1); insert into essai values (2); insert into essai values (3); start transaction isolation level read committed; dans le terminal 2 start transaction isolation level read committed; select * from essai; update essai set e=11 where e=1; update essai set e=222 where e=2; select * from essai; select * from essai; update essai set e=333 where e=3; update essai set e=33 where e=3; commit; select * from essai; commit; Constat : chacun travaille sur sa copie. Le terminal 1 va être bloqué lorsqu’il accédera au même enregistrement qu’une transaction concurrente. Après déblocage par le commit, il voit tout de suite les modifications. Les données lues peuvent être modifiées à tout moment. Et recommencez avec l’autre mode de transaction. dans le terminal 1 delete from essai; insert into essai values (1); insert into essai values (2); insert into essai values (3); start transaction isolation level serializable; dans le terminal 2 start transaction isolation level serializable; select * from essai; insert into essai values (7); insert into essai values (8); update essai set e=11 where e=1; update essai set e=222 where e=2; select * from essai; select * from essai; update essai set e=333 where e=3; update essai set e=33 where e=3; commit; rollback; Constat : chacun travaille sur sa copie. Le terminal 1 va être bloqué lorsqu’il accédera au même enregistrement qu’une transaction concurrente. Après déblocage par le commit, il ne peut plus qu’annuler toutes ses modifications. 11. Pour vraiment bien comprendre la différence entre la théorie et la pratique. December 11, 2006 à 22h02 18/39 December 11, 2006 à 22h02 dans le terminal 1 delete from essai; insert into essai values (1); insert into essai values (2); insert into essai values (3); start transaction isolation level serializable; dans le terminal 2 start transaction isolation level serializable; select * from essai; select * from essai; select sum(e) from essai; insert into essai values (7); delete from essai where e=2; select * from essai; select * from essai; commit; select * from select sum(e) commit; select sum(e) select * from essai; from essai; from essai; essai; Constat : le mode serializable évite-t-il le problème des lignes fantômes ? Selon moi, non ! Le mode serializable devrait garantir qu’aucune intervention sur les éléments consultés ne puisse être validée pendant la transaction. Certains logiciels l’interprètent ainsi (SQL Anywhere Studio, par exemple). Mais cette interprétation du mode serializable se fait au dépend de l’efficacité. Et bon nombre de logiciels interprètent le problème des lignes fantômes par la condition suivante : deux requêtes successives de la transaction ne doivent pas donner des résultats différents. PostgreSQL vérifie bien cette condition... même si à l’issue de la transaction, les résultats n’ont aucune pertinence puisque la base de données a été modifiée. 10.5 Conclusion Faites des transactions courtes et faites des transactions correspondant à vos besoins. N’oubliez pas qu’avec PostgreSQL, une transaction s’exécute sur une copie de la base de données. Utilisez le verrouillage explicite de tables si votre intervention sur la base nécessite absolument que les données ne puissent être modifiées pendant votre action. 11 VERROUILLAGE EXPLICITE Comme vous avez pu le constater, des conflits persistent. Dans certaines situations, il faut garantir que l’on possède un accès exclusif à une table (pour en modifier la structure, par exemple). Il est possible de verrouiller explicitement une table par la commande LOCK [ TABLE ] NomTable [, ...] [ IN ModeVerrou MODE ] [ NOWAIT ] avec ModeVerrou qui peut prendre les valeurs suivantes : ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE | SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE Le verrouillage se fera pour la transaction, après résolution des éventuels conflits. L’option NoWait, comme son nom l’indique, n’attend pas. Mais si la table n’est pas disponible, la commande sera annulée et un message d’erreur sera reçu. Quelques mode de verrouillage : December 11, 2006 à 22h02 19/39 December 11, 2006 à 22h02 ACCESS SHARE : l’accès est partagé. C’est le mode à utiliser pour lire des enregistrements. Ce mode rentrera en conflit uniquement avec une transaction en access exclusive. SHARE : Ce mode protège contre les changements de données par d’autres transactions. Il rentrera en conflit avec les transactions en row exclusive, share update exclusive, share row exclusive, exclusive et access exclusive. ACCESS EXCLUSIVE : Ce mode ne permet aucune autre transaction. Il rentrera en conflit avec tout mode de verrouillage posé sur cette table. Expérimentation 4. 1. Ouvrez deux terminaux. 2. Connectez-vous à votre base filmo dans les deux. 3. Créez une table essai avec un seul champ entier . create table essai (e int); 4. Exécutez les commandes suivantes en respectant la chronologie. dans le terminal 1 insert into essai values (1); insert into essai values (2); dans le terminal 2 Constat. 5. Continuez dans le terminal 1 dans le terminal 2 start transaction isolation level read committed; start transaction isolation level read committed; lock essai in access share mode; insert into essai values (3); lock essai in access share mode; insert into essai values (4); insert into essai values (5); select * from essai; select * from essai; commit; commit; Constat : chacun a accès à la table. 6. Continuez dans le terminal 1 dans le terminal 2 start transaction isolation level read committed; start transaction isolation level read committed; lock essai in access share mode; insert into essai values (6); lock essai in share mode; insert into essai values (7); commit; commit; Constat : blocage de terminal 1. 7. Continuez December 11, 2006 à 22h02 20/39 December 11, 2006 à 22h02 dans le terminal 1 dans le terminal 2 start transaction isolation level read committed; start transaction isolation level read committed; lock essai in access share mode; insert into essai values (8); lock essai in access exclusive mode; commit; start transaction isolation level read committed; lock essai in access share mode; insert into essai values (9); commit; select * from essai; commit; Constat : terminal 2 bloquera dans un premier temps pour obtenir l’accès. Une fois obtenu, il interdit l’accès aux autres. Le verrouillage d’enregistrements est réalisé par la commande SELECT, telle qu’elle a été présentée pour la sélection/affichage, à laquelle on ajoute la clause FOR UPDATE. SELECT ........FROM ......... FOR UPDATE [ OF NomTable [, ...] ] [ NOWAIT ] Les enregistrements sélectionnés par cette commande seront verrouillés pour éviter toute tentative de modification ou suppression par d’autres transactions. Cette commande se met en attente de libération de verrou si les enregistrements sont déjà verrouillés par une autre transaction. La commande peut spécifier une clause FOR TABLE de sorte que seuls les enregistrements de cette table seront verrouillés même si la requête porte sur des enregistrements d’autres tables aussi. Bien sûr, la commande n’est valide que si elle désigne bien des enregistrements. Particulièrement, SELECT FOR UPDATE ne fonctionnera pas avec des agrégats (GROUP BY). 11.1 Conclusion Faites des transactions courtes. Verrouillez les tables si vous avez besoin de garantir la permanence de toutes les données sur lesquelles vous travaillez. Verrouillez uniquement les enregistrements si vous avez besoin de garantir simplement la permanence de l’enregistrement sur lequel vous travaillez. Libérez les tables dès que leur verrouillage n’est plus nécessaire. 12 COMMENTAIRES EN SQL Des commentaires peuvent être écrits entre /* et */ dans un script SQL. 13 DEFINITION DE FONCTIONS SQL Les fonctions SQL sont une suite d’instructions qui retourne un résultat (qui peut être vide). La fonction possède des paramètres dont on indique simplement le type. Ils seront référencés par $1, $2... dans la fonction. Les instructions effectuent des actions. Le résultat de la dernière instruction définit le résultat de la fonction. C’est évidemment au concepteur de la fonction de faire la correspondance entre cette instruction et le résultat. La fonction peut renvoyer un ensemble de lignes (setof type). December 11, 2006 à 22h02 21/39 December 11, 2006 à 22h02 CREATE FUNCTION NomFonction (types ) RETURNS type AS $$ instructions $$ LANGUAGE sql; Attention, c’est la syntaxe de postgreSQL 8.1 ; dans les versions précédentes, le texte de la fonction doit être entre apostrophes... ce qui oblige à doubler les apostrophes quand la fonction contient une valeur date ou texte. On a les instructions suivantes pour changer ou pour supprimer une fonction : CREATE OR REPLACE FUNCTION NomFonction (types ) RETURNS type AS $$ instructions $$ LANGUAGE sql; DROP FUNCTION NomFonction (types ) [ CASCADE | RESTRICT ] Voici quelques exemples de fonctions (que vous pouvez essayer) : CREATE FUNCTION addition(integer, integer) RETURNS integer AS $$ SELECT $1 + $2 AS resultat; $$ LANGUAGE SQL; SELECT addition(1, 2) AS reponse; \c crash CREATE FUNCTION capacitetotale() RETURNS integer AS $$ BEGIN SELECT sum(capacite) AS RESULT from salles ; END; $$ LANGUAGE sql; SELECT capacitetotale() - capacite FROM salles; Une fonction qui fait une action sans renvoyer de résultat peut être définie avec un type de résultat void. \c filmo CREATE FUNCTION AjoutVente (integer, integer, integer) RETURNS void AS $$ UPDATE BilletsVendus SET nombre = nombre + $3 WHERE idfseance = $1 and codetarif = $2; $$ LANGUAGE SQL; SELECT AjoutVente(1, 2, 5); ajoute 5 places vendues à la séance dont l’identifiant est 1 et du tarif dont le code est 2. December 11, 2006 à 22h02 22/39 December 11, 2006 à 22h02 create or replace function programme() returns setof record as ’ select titre ,jour from films, seances where films.idffilm=seances.idffilm; ’ language sql; select * from programme() as (film varchar(30),jour date); CREATE TYPE ProgrammeJour AS (titrefilm varchar(50), horairefilm time); create or replace function programme(date) returns setof ProgrammeJour as ’ select titre ,horaire from films, seances where films.idffilm=seances.idffilm and jour=$1; ’ language sql; select * from programme(’2006/11/28’) ; Expérimentation 5. 1. Une fonction permettra de ne pas réécrire un calcul fréquemment utilisé. Sachant que l’expression pour ajouter une durée numérique à un horaire de type time suit la syntaxe horaire + (ValeurNumerique*interval ’1 hour’) créez une fonction qui possède deux paramètres (l’horaire de début et la durée) et renvoie l’horaire de fin. Testez-la ! CREATE FUNCTION HeureFin(time, numeric) RETURNS time AS ’ SELECT $1 + ($2*interval ”1 hour”) as result; ’ LANGUAGE SQL; select seances.jour,seances.horaire, HeureFin(seances.horaire,films.duree) from seances,films where seances.idffilm=films.idffilm; S’il existe des films qui sont programmés à une heure tardive, que constatez-vous pour l’affichage de l’horaire de fin ? S’il n’en existe pas, ajoutez une séance vers 23h30 et testez votre fonction. 2. Une fonction peut être utile pour décomposer un problème. Sur la base de données crash, répondez à la question "Quel est le nom du conférencier qui a fait le plus de conférences" en utilisant des fonctions. December 11, 2006 à 22h02 23/39 December 11, 2006 à 22h02 CREATE TYPE ConfNbConf AS (nom varchar(60), nb bigint); create or replace function NbConference() returns setof ConfNbConf as ’ select nomeco ,count(codeexpose) from economistes,exposes where codeeco=refconf group by nomeco; ’ language sql; create or replace function MaxConf() returns bigint as ’ select max(nb) from NbConference(); ’ language sql; select nomeco from economistes,exposes where codeeco=refconf group by nomeco having count(codeexpose)=maxconf(); 3. Sachant que la salle de cinéma contient 200 places, écrire une fonction qui calcule le nombre de places restantes pour une séance donnée en paramètre. Procédez par étapes • Afficher les séances et le nombre de places vendues. select idfseance,sum(nombre) from BilletsVendus group by IdfSeance; • Ecrire une fonction pour afficher les places vendues d’une séance. CREATE FUNCTION NbVendus(integer) RETURNS bigint AS ’ select sum(nombre) as resultat from BilletsVendus where IdfSeance = $1 ; ’ LANGUAGE SQL; select idfseance, nbvendus(idfseance) from seances; • Terminer par la fonction demandée. CREATE OR REPLACE FUNCTION NbRestantes(integer) RETURNS bigint AS ’ SELECT 200 - (select sum(nombre) from BilletsVendus where IdfSeance = $1) as resultat; ’ LANGUAGE SQL; ou CREATE OR REPLACE FUNCTION NbRestantes(integer) RETURNS bigint AS ’ SELECT 200 - VentesFilm.nbvendus as resultat from (select sum(nombre) as nbvendus from BilletsVendus where IdfSeance = $1) as VentesFilm; ’ LANGUAGE SQL; select idfseance, nbrestantes(idfseance) from seances; Testez-la ! December 11, 2006 à 22h02 24/39 December 11, 2006 à 22h02 A partir d’ici, ce sont des prises de notes non rédigées 14 PROGRAMMATION EN PLPGSQL Pour utiliser le langage PLPGSQL, exécuter les trois commandes décrites dans la doc PostgreSQL, page 645. CREATE FUNCTION plpgsql_call_handler() RETURNS language_handler AS ’$libdir/plpgsql’ LANGUAGE C; CREATE FUNCTION plpgsql_validator(oid) RETURNS void AS ’$libdir/plpgsql’ LANGUAGE C; CREATE TRUSTED PROCEDURAL LANGUAGE plpgsql HANDLER plpgsql_call_handler VALIDATOR plpgsql_validator; Ces commandes doivent être exécutées une fois dans chaque base de données pour laquelle on veut utiliser PLPGSQL. Le langage est interprété. Ceci signifie que les erreurs se remarquent à l’exécution. On peut passer dans une partie d’alternative sans erreur alors qu’une autre partie est erronée. 14.1 Commentaires en PLpgSQL – pour un commentaire de fin de ligne /* bloc de commentaires */ 14.2 Déclarations de fonctions CREATE FUNCTION NomFonction (parametres ) RETURNS type AS $$ DECLARE déclarations BEGIN instructions RETURN resultat ; END; $$ LANGUAGE plpgsql; L’emploi de $$ comme délimiteur de chaîne permet d’imbriquer plus facilement les chaînes en indiquant un label entre les $. Ce qui donne CREATE FUNCTION NomFonction (parametres ) RETURNS type AS $LabelFonction $ DECLARE déclarations BEGIN RETURN quantity; END; $LabelFonction $ LANGUAGE plpgsql; December 11, 2006 à 22h02 25/39 December 11, 2006 à 22h02 Si la fonction renvoie un résultat unique on emploie la commande RETURN expression; Si la fonction est censée renvoyer un ensemble de lignes, elle contiendra une itération sur la commande RETURN NEXT expression; et se terminera par un RETURN sans argument. En général, les fonctions qui construisent un ensemble de lignes sont utilisées comme des tables. SELECT * FROM some_func(); On peut définir des blocs délimités par BEGIN et END qui permettront de traiter les erreurs par une section EXCEPTION. 14.3 Suppression des fonctions DROP FUNCTION nomfunc(typeparametres); 14.4 syntaxe des déclarations: name [ CONSTANT ] type [ NOT NULL ] [ { DEFAULT | := } expression ]; exemples : quantity numeric; qx integer := 10; Dans les versions antérieures, pour les paramètres, on ne mettait que les types et les paramètres étaient ensuite nommés $1, $2... Mais on peut faire directement CREATE FUNCTION nomfunc(nomparam nomtype, nomparam nomtype) RETURNS type AS $$ on peut faire des appels de fonctions avec des paramètres passés par variables CREATE FUNCTION sum_n_product(x int, y int, OUT sum int, OUT prod int) AS $$ Pour info, cela revient à faire un RETURNS record. On peut utiliser des paramètres de type table. A l’exécution, la variable prendra la valeur de chaque ligne. Exemple CREATE FUNCTION augm(unesalle salles) RETURNS integer AS $$ BEGIN IF unesalle.capacite<100 THEN RETURN 3; ELSE RETURN 2; END IF END; $$ LANGUAGE plpgsql; select augm(salles)*capacite from salles; December 11, 2006 à 22h02 26/39 December 11, 2006 à 22h02 aura pour effet d’afficher les capacités des salles multipliées par 2 ou 3 selon qu’elles sont inférieures à 100. On peut déclarer des variables en copiant le type d’autres variables, particulièrement utile pour une variable du type d’un champ de table. VariableADéfinir VariableDéfinie%TYPE On peut déclarer des variables de type "composite", type ligne de table VariableADéfinir NomTable%ROWTYPE Changement de nom de variables (à éviter ?? on peut utiliser aussi ALIAS) RENAME oldname TO newname; 14.5 évaluation des expressions ********** attention Dans logfunc1, le précompilateur peut évaluer que NOW représente une constante de type texte, que la colonne de la table est de type date et peut évaluer la constante tout de suite (avec la conversion texte/date). La valeur insérée sera celle de la précompilation à chaque appel de fonction. L’instruction RETURN $$NOW$$ fournit bien la date différente à chaque exécution, mais celle insérée dans la table est toujours la même !!! CREATE FUNCTION logfunc1(logtxt text) RETURNS timestamp AS $f1$ BEGIN INSERT INTO logtable VALUES (logtxt, $$NOW$$); RETURN $$NOW$$; END; $f1$ LANGUAGE plpgsql; Dans logfunc2, le précompilateur ne sait pas ce que deviendra la constante de type texte et conserve $$NOW$$ comme une chaîne de caractère qui sera évaluée plus tard. Pourquoi ???? puisque la variable est déclarée de type timestamp !?!? L’interpréteur pourrait aussi l’évaluation/conversion tout de suite. Mystère ! CREATE FUNCTION logfunc2(logtxt text) RETURNS timestamp AS $f2$ DECLARE curtime timestamp; BEGIN curtime := $$NOW$$; INSERT INTO logtable VALUES (logtxt, curtime); RETURN curtime; END; $f2$ LANGUAGE plpgsql; 14.6 Instructions Ce qui n’est pas reconnu comme PLpgSQL est envoyé à l’interpréteur SQL. 14.7 Affectation identificateur := expression ; December 11, 2006 à 22h02 27/39 December 11, 2006 à 22h02 14.8 Selection dans table SELECT INTO variable ExpressionsColonnes FROM ...; ExpressionsColonnes représente les colonnes comme dans une commande SQL normale, et la suite de la commande correspond à la syntaxe de la commande select. Attention, il existe une commande SELECT INTO en SQL qui crée une table. Pour créer une table, il faudrait faire CREATE TABLE AS SELECT... L’instruction place la première ligne de la sélection dans la variable. Le résultat est NULL si aucune ligne n’est sélectionnée. Cela peut se tester avec la condition IS NULL ou dans la variable booléenne FOUND. Il n’est pas possible de déterminer si le résultat de la sélection contient plus d’une ligne. 14.9 Instruction générée dynamiquement EXECUTE ChaîneCommande [ INTO variable ]; La chaîne de caractère peut être construite avec des résultats d’expressions. Par contre, on ne peut pas construire une chaîne SELECT...INTO Cette commande peut être utile pour exécuter une instruction qu’on veut absolument non précompilée 14.10 Instruction sans résultat PERFORM requête ; La syntaxe est la même que celle de SELECT en remplaçant SELECT par PERFORM. L’instruction ne produit pas de résultat !?!? Ceci est utile pour des instructions ayant des effets de bord, par exemple, une commande SELECT avec une fonction trigger. 14.11 Instruction vide NULL; 14.12 Statut de l’exécution d’une commande GET DIAGNOSTICS variable = item [ , ... ]; ou utiliser la variable booléenne FOUND. 14.13 IF IF IF IF IF Alternatives ...THEN ... THEN ... THEN ... THEN ... THEN ... ... ... ... ELSE ELSE IF ELSIF ... THEN ... ELSE ELSEIF ... THEN ... ELSE Les formes classiques December 11, 2006 à 22h02 28/39 December 11, 2006 à 22h02 IF condition THEN instructions END IF; IF condition THEN instructions ELSE instructions END IF; Les alternatives imbriquées IF condition THEN instructions [ ELSIF condition THEN instructions [ ELSIF condition THEN instructions ...]] [ ELSE instructions ] END IF; ELSEIF is an alias for ELSIF. 14.14 Itérations [ «label» ] LOOP instructions END LOOP [ label ]; Cela définit une boucle infinie dont on sort par l’instruction EXIT ou RETURN. Le label permet d’indiquer quelle boucle il faut arrêter. EXIT [ label ] [ WHEN expression ]; si l’expression est vraie, la boucle indiquée par le label se termine ; sinon la boucle poursuit avec l’instruction suivante. CONTINUE [ label ] [ WHEN expression ]; On reprend l’itération si l’expression est vraie. exemple : LOOP – some computations EXIT WHEN count > 100; CONTINUE WHEN count < 50; – some computations for count IN [50 .. END LOOP; December 11, 2006 à 22h02 100] 29/39 December 11, 2006 à 22h02 EXIT et CONTINUE peuvent être utilisées avec toutes itérations. [ «label» ] WHILE condition LOOP instructions END LOOP [ label ]; [ «label» ] FOR name IN [ REVERSE ] expression .. instructions END LOOP [ label ]; expression LOOP La variable name sera de type INTEGER. exemples : FOR i IN 1..10 LOOP – some computations here RAISE NOTICE Ši is %Š, i; END LOOP; FOR i IN REVERSE 10..1 LOOP – some computations here END LOOP; 14.15 Itération dans une requête [ «label» ] FOR record_or_row IN query LOOP instructions END LOOP [ label ]; Chaque ligne sera affectée à la variable qui peut être de type RECORD pour simplifier. CREATE FUNCTION cs_refresh_mviews() RETURNS integer AS $func_cs$ DECLARE mviews RECORD; BEGIN FOR mviews IN SELECT * FROM cs_materialized_views ORDER BY sort_key LOOP – now "mviews" has one record from cs_materialized_views .... END LOOP; RETURN ...; END; $func_cs$ LANGUAGE plpgsql; Si la boucle se termine par EXIT, la dernière ligne affectée est encore accessible. December 11, 2006 à 22h02 30/39 December 11, 2006 à 22h02 [ «label» ] FOR record_or_row IN EXECUTE text_expression LOOP instructions END LOOP [ label ]; Dans cette forme, la requête est réévaluée à chaque étape de l’itération. 14.16 Traitements des erreurs [ «label» ] [ DECLARE declarations ] BEGIN instructions EXCEPTION WHEN condition [ OR condition ... ] THEN handler_instructions [ WHEN condition [ OR condition ... ] THEN handler_instructions ... ] END; Si une erreur apparaît à l’exécution, la section EXCEPTION est exécutée. Si une condition est vérifiée, le traitement correspondant est exécuté et l’exécution se poursuit après le END. Sinon l’erreur se propage à la suite. Voir la documentation pour les codes d’erreurs. 14.17 Fonctions récursives On peut écrire des fonctions récursives... Il suffit de bien maîtriser l’arrêt. Ici, une fonction qui parcourt un arbre, codé par un systême père/fils, dans une table. Voici la structure de la table si_id int si_parentid int 1 2 1 3 2 4 2 5 1 6 5 7 5 8 5 si_item Paper Recycled 20 lb 40 lb Non-Recycled 20 lb 40 lb Scraps CREATE FUNCTION cp_getitemfullname(int8) RETURNS varchar AS $fr$ DECLARE itemid ALIAS FOR $1; itemfullname varchar(255); itemrecord RECORD; BEGIN SELECT s.* INTO itemrecord FROM supplyitem s where si_id=itemid; itemfullname := itemfullname + itemrecord.si_item; IF itemrecord.si_parentid IS NOT NULL THEN itemfullname := cp_getitemfullname(itemrecord.si_parentid) + ”− >” + itemfullname ; RETURN itemfullname; ELSE RETURN itemfullname; END IF; END$fr$ LANGUAGE ’plpgsql’ December 11, 2006 à 22h02 31/39 December 11, 2006 à 22h02 SELECT cp_getitemfullname(8) This returns Affichage Paper − > Non-Recycled − > Scraps 15 TRIGGERS Un trigger spécifie qu’une fonction doit être exécutée lors de certaines commandes SQL : Insert, Update ou Delete. Le trigger peut avoir une action au niveau de la ligne (per-row trigger) ou au niveau global (per-statement trigger). Dans chaque catégorie, il peut être before ou after trigger. On définit la fonction, puis on définit le trigger. La fonction ne peut pas être une fonction SQL ; il faut qu’elle soit écrite dans un autre langage, comme plpgSQL, Perl, Python... CREATE TRIGGER NomTrigger { BEFORE | AFTER } { evenement [ OR ... ON NomTable [ FOR [ EACH ] { ROW | STATEMENT } ] EXECUTE PROCEDURE NomFonction ( arguments ) ] } DROP TRIGGER NomTrigger ON table [ CASCADE | RESTRICT ] L’événement peut être insert, update ou delete et il peut y en avoir plusieurs séparés par OR. Un row-level before trigger doit renvoyer soit un résultat NULL, soit une ligne de table. Si un row-level before trigger renvoie un résultat NULL, la commande n’est pas exécutée. S’il renvoie un résultat ligne (en général la variable new ou old, éventuellement modifiée), la commande est exécutée avec cette ligne. Les autres triggers devraient renvoyer NULL. Les row before triggers servent à modifier des lignes insérées ou modifiées. Les row after trigger peuvent servir à mettre à jour des tables liées. un trigger possède des variables prédéfinies : NEW, OLD.... NEW est de type record et contient la nouvelle ligne pour les commandes INSERT et UPDATE OLD est de type record et contient l’ancienne ligne pour les commandes UPDATE et DELETE TG_NAME (type name) le nom du trigger TG_WHEN (type texte) contient after ou before TG_LEVEL (type texte) contient row ou statement TG_OP (type texte) contient insert, update ou delete TG_RELNAME (type name) contient le nom de la table et quelques autres variables Voici quelques exemples sur la base crash. Aucune salle n’étant de capacité supérieure à 300 places, on va corriger une insertion erronée plutôt que de la refuser. \c crash create or replace function verif() returns trigger as $verifplus$ begin if new.capacite > 300 then new.capacite := 300; raise notice ’capacité ramenée à 300’; end if; December 11, 2006 à 22h02 32/39 December 11, 2006 à 22h02 return new; end; $verifplus$ language plpgsql; create trigger verifplus before insert or update on salles for each row execute procedure verif(); insert into salles values (’TR1’,’Labo Langue’,360); select * from salles; drop trigger verifplus on salles; drop function verif(); Pour un row-level before trigger, si la fonction renvoie un résultat null, la commande Insert ou update n’est pas exécutée. Par exemple, la fonction ci-dessous renvoie un résultat null si la capacité est supérieure à 1000 et n’insérerait aucun élément avec une telle capacité. create or replace function verif() returns trigger as $verifplus$ begin if new.capacite > 1000 then raise notice ’insertion annulée, capacité trop grande’; return null; elsif new.capacite > 300 then new.capacite := 300; raise notice ’capacité trop grande, ramenée à 300’; return new; else return new; end if; end; $verifplus$ language plpgsql; Expérimentation 6. 1. Supposons qu’à chaque fois qu’on ajoute un film, on ajoute une séance pour le lendemain même à midi. Réalisez cette insertion d’une séance par un trigger. create or replace function AjoutSeance() returns trigger as ’ begin insert into seances values((select max(idfseance) from seances) +1,current_date+1,”12:00:00”,new.idffilm); return new; end; ’ language plpgsql; create trigger NouvelleSeance after insert on films for each row execute procedure AjoutSeance(); December 11, 2006 à 22h02 33/39 December 11, 2006 à 22h02 2. Sachant que la salle de cinéma contient 200 places, voici un trigger qui ne fait l’insertion ou la modification que si le nombre de billets est inférieur à 200. Testez-le ! create or replace function VerificationCapacite() returns trigger as ’ begin if new.nombre >= 200 then raise exception ”vente impossible, trop de billets”; return null; else return new; end if; end; ’ language plpgsql; create trigger tropventes before insert or update on billetsvendus for each row execute procedure VerificationCapacite(); insert into billetsvendus values (2,3,285); Modifiez-le pour créer un trigger qui tient compte du nombre de places vendues pour la séance et ne fait l’insertion ou la modification que si le nombre est acceptable. Vous pouvez utiliser la fonction NbVendus déjà créée. create or replace function VerificationCapacite() returns trigger as ’ begin if new.nombre > 200-NbVendus(new.idfseance) then raise exception ”vente impossible, trop de billets”; return null; else return new; end if; end; ’ language plpgsql; 3. Voici une fonction qui examine si une séance est programmée un jour et n’est pas terminée. Si c’est le cas l’insertion d’une nouvelle séance à l’horaire prévu n’est pas possible. Etudiez cette fonction create function existant() returns trigger as ’ declare res record; begin select count(*) as nb into res from seances,films where seances.idffilm=films.idffilm and seances.jour+seances.horaire <= new.jour+new.horaire and seances.jour+seances.horaire+(films.duree*interval ”1 hour”) > new.jour+new.horaire; if res.nb=0 then return new; December 11, 2006 à 22h02 34/39 December 11, 2006 à 22h02 else raise exception ”un film pas termine”; return null; end if; end; ’ language plpgsql; create trigger filmavant before insert on seances for each row execute procedure existant(); insert into seances values (2,’2006/09/29’,’21:45:00’,2); affichera, s’il existe un film dont la séance est programmée le même jour et qui ne sera pas terminée, ERROR: un film pas termine ERROR: un film pas termine Transformez la fonction pour qu’elle regarde aussi si la séance à ajouter ne se termine pas trop tard, c’est à dire que l’heure de fin dépasse l’heure de début d’un film déjà programmé. create or replace function existant() returns trigger as ’ declare resavant record; resapres record; begin select count(*) as nb into resavant from seances,films where seances.idffilm=films.idffilm and seances.jour+seances.horaire <= new.jour+new.horaire and seances.jour+seances.horaire+(films.duree*interval ”1 hour”) > new.jour+new.horaire; select count(*) as nb into resapres from seances,films where seances.idffilm=films.idffilm and new.jour+new.horaire <= seances.jour+seances.horaire and new.jour+new.horaire +((select duree from films where idffilm=new.idffilm)*interval ”1 hour”) > seances.jour+seances.horaire; if resavant.nb<>0 then raise exception ”un film pas termine”; return null; elsif resapres.nb<>0 then raise exception ”trop long pour le film suivant”; return null; else return new; end if; end; ’ language plpgsql; Contents 1 INTERROGATION December 11, 2006 à 22h02 1 35/39 December 11, 2006 à 22h02 2 FONCTIONS ET EXPRESSIONS 2.1 expressions arithmétiques . . . . . 2.2 expressions caractères . . . . . . . 2.3 expressions dates . . . . . . . . . . 2.4 expressions booléennes . . . . . . . . . . . 3 3 3 3 4 3 CREATION D’UTILISATEURS 3.1 Création de la base à partir du terminal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Création d’un utilisateur ou d’un groupe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 4 4 4 CREATION DE BASE 4.1 Création de la base à partir du terminal 4.2 Création de la base . . . . . . . . . . . . 4.3 Renommage de la base . . . . . . . . . . 4.4 Suppression de la base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5 5 5 5 5 STRUCTURE DE TABLE 5.1 Types de données . . . . . 5.2 Création de table . . . . . 5.3 Modifications de table . . 5.4 Suppression de table . . . 5.5 Quelques exemples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5 6 7 8 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 INDEXATION 8 7 CONTENU D’UNE TABLE 7.1 Insertion de lignes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.2 Modification de lignes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3 Suppression de lignes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 10 10 10 8 PRIVILÈGES 10 9 TRANSACTION 11 10 CONFLITS 10.1 Présentation des conflits possibles . . . . . 10.1.1 Lecture inconsistante . . . . . . . . 10.1.2 Lecture non répétitive . . . . . . . 10.1.3 Lignes fantômes . . . . . . . . . . 10.2 Outils de gestion des conflits . . . . . . . 10.3 Solutions aux conflits . . . . . . . . . . . . 10.3.1 Solution à la lecture inconsistante 10.3.2 Solution à la lecture non répétitive 10.3.3 Solution aux lignes fantômes . . . 10.4 Modes de transaction . . . . . . . . . . . . 10.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 12 12 12 13 13 13 14 14 14 15 19 11 VERROUILLAGE EXPLICITE 11.1 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 21 12 COMMENTAIRES EN SQL 21 13 DEFINITION DE FONCTIONS SQL 21 December 11, 2006 à 22h02 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36/39 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . December 11, 2006 à 22h02 14 PROGRAMMATION EN PLPGSQL 14.1 Commentaires en PLpgSQL . . . . . . 14.2 Déclarations de fonctions . . . . . . . 14.3 Suppression des fonctions . . . . . . . 14.4 syntaxe des déclarations: . . . . . . . 14.5 évaluation des expressions . . . . . . . 14.6 Instructions . . . . . . . . . . . . . . . 14.7 Affectation . . . . . . . . . . . . . . . 14.8 Selection dans table . . . . . . . . . . 14.9 Instruction générée dynamiquement . 14.10Instruction sans résultat . . . . . . . . 14.11Instruction vide . . . . . . . . . . . . . 14.12Statut de l’exécution d’une commande 14.13Alternatives . . . . . . . . . . . . . . . 14.14Itérations . . . . . . . . . . . . . . . . 14.15Itération dans une requête . . . . . . . 14.16Traitements des erreurs . . . . . . . . 14.17Fonctions récursives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 25 25 26 26 27 27 27 28 28 28 28 28 28 29 30 31 31 15 TRIGGERS 32 A Commandes PSQL 38 December 11, 2006 à 22h02 37/39 December 11, 2006 à 22h02 A Commandes PSQL Général \? \c[onnect] [NomBase |- [utilisateur ]] \cd [répertoire ] \h [nom ] \q \set [nom [valeur ]] \unset nom \! [commande ] aide psql connecte à une autre base de données change de répertoire courant aide-mémoire pour les commandes SQL, * pour toutes les commandes quitte psql initialise la variable interne ou les affiche toutes s’il n’y a aucun paramètre désinitialise (supprime) la variable interne exécute la commande dans un shell ou lance un shell interactif Tampon de requête (concerne la dernière requête select) \e [fichier ] édite le tampon de requête ou le fichier avec un éditeur externe (par ex, emacs) \g [fichier ] envoie le tampon de requêtes au serveur (et les résultats au fichier ou | tube) \p affiche le contenu du tampon de requête \r efface le tampon de requêtes \s [fichier ] affiche l’historique ou le sauvegarde dans un fichier \w [fichier ] écrit le contenu du tampon de requêtes dans un fichier Entrée/Sortie \echo [texte ] \i fichier \o [fichier ] \o \qecho [texte ] écrit un texte sur la sortie standard exécute les commandes du fichier écrit les résultats des requêtes et autres commandes dans un fichier (ou | tube) termine la sortie dans fichier écrit un texte sur la sortie pour les résultats des requêtes Information \d [nom ] \d{t|i|s|v|S} [modèle ] \da [modèle ] \dc [modèle ] \dC \dd [modèle ] \dD [modèle ] \df [modèle ] \dn [modèle ] \do [modèle ] \dl \dp [modèle ] \dT [modèle ] \du [modèle ] \l \z [modèle ] Formatage \a \C [chaîne ] \f [chaîne ] \H \pset nom [valeur ] \t \T [chaîne ] \x December 11, 2006 à 22h02 décrit la table, l’index, la séquence ou la vue liste les tables/index/séquences/vues/tables système (ajoutez "+" pour plus de détails) affiche la liste des fonctions d’aggrégation affiche la liste des conversions affiche la liste des conversions explicites affiche les commentaires pour un objet affiche la liste des domaines affiche la liste des fonctions (ajoutez "+" pour plus de détails) affiche la liste des schémas affiche la liste des operateurs affiche la liste des objets larges affiche la liste des privilèges d’accès aux tables affiche la liste des types de données (ajoutez "+" pour plus de détails) affiche la liste des utilisateurs affiche toutes les bases de données (ajoutez "+" pour plus de détails) affiche la liste des privilèges d’accès aux tables (identique à \dp) bascule entre les modes de sortie aligné et non aligné initialise le titre d’une table, ou initialise à rien si sans argument affiche ou initialise le séparateur de champ pour une sortie non alignée des requêtes bascule le mode de sortie HTML, avec des balises HTML initialise les variables de sortie (nom est format | border | expanded | fieldsep | footer | null | recordsep | tuples_only | title | tableattr | pager ) affiche seulement les lignes, pas les titres des colonnes initialise les attributs HTML de la balise <table>, ou l’annule si aucun argument bascule l’affichage étendu (résultat affiché par ligne ou par fiche) 38/39 December 11, 2006 à 22h02 Le modèle est un masque utilisant des caractères spéciaux tels que ? pour remplacer un caractère quelconque et * pour remplacer une suite de caractères. \dt ?a* affiche les tables qui possèdent un a en deuxième caractère. December 11, 2006 à 22h02 39/39 December 11, 2006 à 22h02