"Grande quantité de données (ou ensemble d'informations), centralisées ou non, servant pour les besoins d'une ou plusieurs applications, interrogeables et modifiables par un groupe d'utilisateurs travaillant en parallèle."
Ensemble de logiciels permettant aux utilisateurs
Requête la plus simple : affiche des (combinaisons de) colonnes.
Exemples dans le contexte d'une médiathèque, sur la table
Document(Code,Type,Description,Titre,Auteur,Date,Emprunteur)
-- Afficher toute la table
SELECT *
FROM Document;
-- Afficher seulement le code et l'auteur
SELECT Code,Auteur
FROM Document;
-- Afficher aussi l'identifiant de l'emprunteur
SELECT Code,Auteur,Emprunteur AS IdentifiantAdherent
FROM Document;
Requête la plus courante : filtre sur les colonnes.
Document(Code,Type,Description,Titre,Auteur,Date,Emprunteur)
-- Les identifiants de documents non empruntés
SELECT Code
FROM Document
WHERE Emprunteur IS NULL;
-- Les documents produits entre 1980 et 1990
SELECT *
FROM Document
WHERE Date >= '1980-01-01' AND Date <= '1990-01-01';
-- Documents dont le nom de l'auteur commence par un B
SELECT *
FROM Document
WHERE Auteur LIKE 'B%';
Document(Code,Type,Description,Titre,Auteur,Date,Emprunteur)
1] Titre et date des documents empruntés par Alice.
SELECT Titre,Date
FROM Document
WHERE Emprunteur = 'Alice';
2] Titre et auteur des documents qui ne sont pas des BDs.
SELECT Titre,Auteur
FROM Document
WHERE Type <> 'BD';
Concatène des résultats de requêtes.
Exemple : caractéristiques de divers moyens de transport.
SELECT 'voiture',vitesse_max,masse
FROM Voiture
WHERE marque = 'Nissan'
UNION
SELECT 'moto',vitesse_max,masse
FROM Moto
UNION
SELECT 'sous-marin',vitesse_max,masse
FROM SousMarins
WHERE pays = 'Japon';
Compte les lignes vérifiant un certain critère.
-- Nombre de ligne d'une table
SELECT COUNT(*)
FROM Commande;
-- Nombre de commandes effectuées avant le 9/01/2009
SELECT COUNT(*)
FROM Commande
WHERE Date < '2009-01-09'
-- ou
SELECT COUNT(Date < '2009-01-09')
FROM Commande;
-- Nombre de commandes effectuées apres le 9/01/2009
-- dont la quantite est superieure a 5
SELECT COUNT(Date >= '2009-01-09' AND Quantité > 5)
FROM Commande;
£f(e) = f(e(r_1), ..., e(r_n))£, où
-- Prix maximum d'une commande
SELECT MAX(Prix * Quantité)
FROM Commande;
-- Quantité minimum commandée par Bob
SELECT MIN(Quantité)
FROM Commande
WHERE Client = 'Bob';
-- Somme des quantités commandées par Alice
SELECT SUM(Quantité)
FROM Commande
WHERE Client = 'Alice';
...Plusieurs autres fonctions : voir l'aide PostGreSQL.
Concert(ID, Groupe, Lieu, NomSalle, CapacitéSalle, NbEntrées)
1] Nombre total d'entrées réalisé par le groupe 'Rhapsody'.
SELECT SUM(NbEntrees)
FROM Concert
WHERE Groupe = 'Rhapsody'
2] Capacité moyenne des salles de Berlin ayant totalisé plus de 300 entrées au moins une fois. NB : les salles ont des capacités toutes différentes.
SELECT AVG(subQuery.CapaciteSalle)
FROM
(SELECT DISTINCT CapacitéSalle
FROM Concert
WHERE Lieu = 'Berlin' AND NbEntrées >= 300) AS subQuery;
Commande(ID,Client,Produit,Prix,Quantité,Date)
-- Prix total pour les achats effectues le 12/10/2010
SELECT DISTINCT Client,Produit,Prix * Quantité AS prixTotal
FROM Commande
WHERE Date = 'October 12, 2010';
-- Sommes des prix totaux par client,
-- pour les achats effectués le 12/10/2010
SELECT Client,SUM(Prix * Quantité) AS sommePrixTotal
FROM Commande
WHERE Date = '2010-10-12'
GROUP BY Client;
-- Clients qui ont commandé quelque chose le 9/01/2009
SELECT Client
FROM Commande
WHERE Date = '2009-01-09'
GROUP BY Client
HAVING SUM(Quantité) > 0;
-- Prix total par ordre croissant
SELECT DISTINCT Client,Produit,Prix * Quantité AS prixTotal
FROM Commande
WHERE Date = 'October 12, 2010'
ORDER BY prixTotal;
-- Sommes des prix totaux par client, par ordre decroissant,
-- pour les achats effectues le 12/10/2010
SELECT Client,SUM(Prix * Quantité) AS sommePrixTotal
FROM Commande
WHERE Date = '2010-10-12'
GROUP BY Client
ORDER BY sommePrixTotal DESC;
-- Clients qui ont commandé quelque chose le 9/01/2009
-- Tri alphabetique puis décroissant sur les quantités
SELECT Client,Quantité
FROM Commande
WHERE Date = '2009-01-09'
ORDER BY Client, Quantite DESC;
Document(Code,Type,Description,Titre,Auteur,Date,Emprunteur)
1] Nombres de documents datant de 2004 empruntés par chaque utilisateur, rangés par ordre croissant.
SELECT Emprunteur,COUNT(*) AS nbDocs
FROM Document
WHERE Date BETWEEN '2004-01-01' AND '2004-12-31';
GROUP BY Emprunteur
ORDER BY nbDocs;
2] Nombres totaux d'emprunts par Emilie pour chaque "tranche alphabétique" de titre ("A...", "B...", etc.), en excluant les totaux inférieurs à 2.
SELECT subQuery.firstLetter,COUNT(*) AS total
FROM
(SELECT substring(Titre from 1 for 1) AS firstLetter
FROM Document
WHERE Emprunteur = 'Emilie') AS subQuery
GROUP BY subQuery.firstLetter
HAVING COUNT(*) >= 2;
Expression conditionnelle, similaire au IF/ELSE dans les autres langages
Exemple :
SELECT * FROM test;
a
---
1
2
3
SELECT a,
CASE WHEN a=1 THEN 'one'
WHEN a=2 THEN 'two'
ELSE 'other'
END
FROM test;
a | case
---+-------
1 | one
2 | two
3 | other
Note : peut être considéré comme un opérateur sur les attributs.
Identifiant d'une rangée dans une table.
Exemple : Client(ID, Nom, Prenom, DateDeNaissance),
Exemple : où ID est un entier incrémenté à chaque nouveau client.
Alternative : clé = (Nom,Prénom).
Inconvénients :Attribut qui est une clé primaire dans une autre table.
Exemple : Client(ID, Nom, Prenom, DateDeNaissance)
Exemple : Commande(ID,ClientID,Quantite,Prix,Produit,Date)
Les requêtes nécessitent souvent plusieurs tables :
Première possibilité :
SELECT Prenom, Nom
FROM Client, Produit
WHERE Client.ID = Produit.ClientID (AND autres_conditions);
Réécriture de la requête :
SELECT Prenom, Nom
FROM Client
JOIN Produit
ON Client.ID = Produit.ClientID
WHERE possibles_filtres;
On sépare clairement
Possible problème :
si une valeur d'attribut de jointure est absente d'une des tables,
toutes les lignes la contenant seront omises.
Exemple :
JOIN Groupe(Nom,Membres,Style), Concert(Lieu,NomGroupe,Date)
£\rightarrow£ pas d'affichage des groupes sans concerts.
Assure que toutes les rangées des tables sont dans le résultat.
Exemple :SELECT Lieu,Nom AS Groupe
FROM Groupe
OUTER JOIN Concert
ON Groupe.Nom = Concert.NomGroupe
WHERE possibles_filtres;
affiche aussi les groupes sans concerts.
Client(ID,Nom,Prenom,Adresse,Telephone)
Commande(ID,ClientID,ProduitID,Prix,Quantité,Date)
Produit(ID,Nom,Categorie,Description)
Afficher les prénoms+noms des clients ayant commandé une cuisine le 10 octobre 2012 (en utilisant JOIN).
SELECT cl.Prenom,cl.Nom
FROM Client AS cl
JOIN Commande AS co
ON cl.ID = co.ClientID
JOIN Produit AS p
ON co.ProduitID = p.ID
WHERE co.Date = '2012-10-10' AND p.Nom = 'Cuisine';
Principe : sous-requête (souvent) dans la clause WHERE.
Exemple : films loués plus souvent que la moyenne.SELECT film_id,title,rental_rate
FROM film
WHERE rental_rate >
(SELECT
AVG (rental_rate)
FROM film);
SELECT g.Nom
FROM Groupe AS g
WHERE EXISTS
(SELECT 1
FROM Concert AS c
WHERE c.NomGroupe = g.Nom);
Remarque : possible aussi avec INSERT, DELETE ou UPDATE.
SELECT *
FROM Company
WHERE ID IN
(SELECT ID
FROM Company
WHERE Salary > 45000) ;
("NOT IN" afficherait le complémentaire.)
SELECT *
FROM
(SELECT Nom,Style
FROM Groupe
WHERE Nom LIKE 'E%') AS tableTemp
WHERE tableTemp.Style = 'Symphonic Metal';
SELECT *
FROM Commande
WHERE ClientID > ANY
(SELECT cl.ID
FROM Client AS cl
JOIN Commande AS co
ON cl.ID = co.ClientID
WHERE co.Quantite = 5);
SELECT DISTINCT Groupe.Nom
FROM Groupe
JOIN Concert
ON Groupe.Nom = Concert.NomGroupe
WHERE Concert.Lieu <> ALL
(SELECT Lieu
FROM Concert
WHERE Lieu LIKE '%O');
Client(ID,Nom,Prenom,Adresse,Telephone)
Commande(ID,ClientID,ProduitID,Prix,Quantité,Date)
Produit(ID,Nom,Categorie,Description)
Afficher les prénoms+noms des clients ayant commandé une cuisine le 10 octobre 2012 (en utilisant des sous-requêtes).
SELECT Prenom, Nom
FROM Client
WHERE ID IN
(SELECT ClientID
FROM Commande
WHERE Date = '2012-10-10' AND ProduitID IN
(SELECT ID
FROM Produit
WHERE Nom = 'Cuisine'))
Isole une (ou plusieurs) sous-requête(s) dont les résultats servent aux requêtes suivantes.
Exemple du manuel en ligne :WITH regional_sales AS
(SELECT region, SUM(amount) AS total_sales
FROM orders
GROUP BY region),
top_regions AS
(SELECT region
FROM regional_sales
WHERE total_sales >
(SELECT SUM(total_sales)/10 FROM regional_sales))
SELECT region,product,SUM(quantity) AS product_units,
SUM(amount) AS product_sales
FROM orders
WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product;
Comme WITH, mais mémorise le résultat.
£\Rightarrow£ On peut l'utiliser dans d'autres requêtes.
CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] [ RECURSIVE ]
VIEW name [ ( column_name [, ...] ) ]
[ WITH ( view_option_name [= view_option_value] [, ... ] ) ]
AS query
CREATE VIEW comedies AS
SELECT *
FROM films
WHERE kind = 'Comedy';
(...)
-- Affiche le titre du film comique emprunté
SELECT u.nom, u.prenom, c.title
FROM users AS u
LEFT OUTER JOIN comedies AS c
ON u.rentID = c.ID;
Illustration :
WITH RECURSIVE t(n) AS (
VALUES (1)
UNION ALL
SELECT n+1 FROM t WHERE n < 100
)
SELECT sum(n) FROM t; -- affiche 5050
La table t se construit "récursivement" lors de la requête finale.
Exemple d'utilisation plus réaliste
Note : avec le mot-clé UNION on élimine les doublons en 1, 2.b et 2.c.
Note : Avec UNION ALL, on les garde.
WITH RECURSIVE rec_fact (n, fact) AS (
SELECT 1, 1 -- Requête initiale
UNION ALL
SELECT n+1, (n+1)*fact FROM temp -- Requête récursive
WHERE n < 3 -- Condition d'arrêt
)
SELECT MAX(fact) FROM rec_fact;
T
n | fact |
---|
tmp
n | fact |
---|
R
n | fact |
---|
1 | 1 |
1 | 1 |
1 | 1 |
2 | 2 |
2 | 2 |
1 | 1 |
2 | 2 |
3 | 6 |
3 | 6 |
1 | 1 |
2 | 2 |
3 | 6 |
-- DDL: create table; label = name of the node; p_id = parent ID
CREATE TABLE Node (id SERIAL,p_id INT,
label TEXT NOT NULL,PRIMARY KEY(id));
-- DML: insert data
INSERT INTO node (label,p_id) VALUES ('n1',NULL),('n2',1),
('n3',2),('n4',3),('g1',NULL),('g2',NULL),('g3',NULL),('g4',6);
-- DQL: request, print graph structure
WITH RECURSIVE nodes_rec(id, label, p_id, depth, path) AS (
SELECT n.id,n.label,n.p_id,1::INT,n.id::TEXT FROM node AS n
WHERE n.p_id IS NULL
UNION ALL
SELECT n.id,n.label,n.p_id,nr.depth+1,(nr.path||'->'||n.id::
TEXT) FROM nodes_rec AS nr, node AS n WHERE n.p_id = nr.id)
SELECT * FROM nodes_rec AS nr ORDER BY nr.id ASC;
id | label | parent_id | depth | path
1 | n1 | | 1 | 1
2 | n2 | 1 | 2 | 1->2
3 | n3 | 2 | 3 | 1->2->3
4 | n4 | 3 | 4 | 1->2->3->4
5 | g1 | | 1 | 5
6 | g2 | | 1 | 6
7 | g3 | | 1 | 7
8 | g4 | 6 | 2 | 6->8
Expressions régulières
LIKE, SIMILAR TO
string [NOT] LIKE pattern [ESCAPE escape-character]
pattern : une chaîne de caractères pouvant comporter
- _ : remplace n'importe quel caractère ;
- % : remplace n'importe quelle sous-chaîne.
Exemples :
'abc' LIKE 'abc' true
'abc' LIKE 'a%' true
'abc' LIKE '_b_' true
'abc' LIKE 'c' false
Dans une requête :
SELECT Titre,Auteur
FROM Document
WHERE Auteur LIKE '%a_' -- match 'Vargas', 'Curval', etc.
SIMILAR TO
string [NOT] SIMILAR TO pattern [ESCAPE escape-character]
Syntaxe de LIKE, plus un peu d'expressions régulières POSIX :
- | denotes alternation (either of two alternatives).
- * denotes repetition of the previous item zero or more times.
- + denotes repetition of the previous item one or more times.
- ? denotes repetition of the previous item zero or one time.
- {m[,[n]]} denotes repetition of the previous item exactly m times [or more [but no more than n times]].
- Parentheses () can group items into a single logical item.
- A bracket expression [...] specifies a character class.
'abcde' SIMILAR TO '(a|b)[c-e]+'
'abcccde' SIMILAR TO 'abc{2,4}de*'
'http://archlinux.fr' SIMILAR TO 'https?:/+[a-z]*\.fr'
false
true
true
Expressions régulières POSIX
Extension de la syntaxe de SIMILAR TO.
- _ est remplacé par . £\rightarrow£ 'a_c' s'écrit 'a.c'
- % n'a pas d'équivalent en un caractère. Utiliser '.*'
Capture de motifs : (re) isole l'expression régulière £re£ et "capture" la sous-chaîne qu'elle
reconnaît (pour l'utiliser plus tard).
Pour ne pas capturer : (?:re)
\k où £k£ est un caractère non alphanumérique, représente ce caractère.
*?, +?, {m[,[n]]}? sont les versions "non gloutonnes" de *, +, et {m[,[n]]} : elles s'arrêtent
dès que la condition est vérifiée.
^ (resp. $) représente le début (resp. la fin) de la chaîne.
...etc.
Exemples
^0[1-9]([-. ]?[0-9]{2}){4}$
Un numéro de téléphone.
[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?
Un nombre flottant.
^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).{8,}$
Un mot de passe d'au moins 8 caractères avec au moins une minuscule, une majuscule et un chiffre.
^[a-z0-9!#$%&'*+/=?^_`{|}~-]+
(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@
(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+
[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$
Une adresse email (version simplifiée !)
Bilan
Une base de données PostGreSQL s'interroge ...
- avec la clause SELECT FROM (WHERE) ;
- en utilisant des jointures ;
- en utilisant des sous-requêtes ;
- avec la clause WITH RECURSIVE ;
- ...etc. : voir documentation.
Des opérations préliminaires sur les tables peuvent être effectuées : UNION, INTERSECT, EXCEPT ....
Les expressions régulières permettent de reconnaître des motifs complexes dans une chaîne de caractères.
Administration d'une BdD
Création/modification/suppression d'une base
CREATE DATABASE name
[ [ WITH ] [ OWNER [=] user_name ]
[ TEMPLATE [=] template ]
[ ENCODING [=] encoding ]
[ LC_COLLATE [=] lc_collate ]
[ LC_CTYPE [=] lc_ctype ]
[ TABLESPACE [=] tablespace ]
[ CONNECTION LIMIT [=] connlimit ] ]
ALTER DATABASE name [ [ WITH ] option [ ... ] ]
DROP DATABASE [ IF EXISTS ] name
Exemple :
CREATE DATABASE dbTest WITH OWNER = userTest;
ALTER DATABASE dbTest SET ENCODING = 'UTF-8';
DROP DATABASE dbTest;
DCL : définir les utilisateurs et leurs droits
Création des (groupes d') utilisateurs
CREATE ROLE name [ [ WITH ] option [ ... ] ]
Option(s) :
SUPERUSER | NOSUPERUSER
| CREATEDB | NOCREATEDB
| CREATEROLE | NOCREATEROLE
| CREATEUSER | NOCREATEUSER
| INHERIT | NOINHERIT
| LOGIN | NOLOGIN
| REPLICATION | NOREPLICATION
| CONNECTION LIMIT connlimit
| [ ENCRYPTED | UNENCRYPTED ] PASSWORD 'password'
| VALID UNTIL 'timestamp'
| IN ROLE role_name [, ...]
| IN GROUP role_name [, ...]
| ROLE role_name [, ...]
| ADMIN role_name [, ...]
| USER role_name [, ...]
| SYSID uid
Exemple :
CREATE ROLE admin WITH CREATEDB CREATEROLE REPLICATION;
Modifications des (droits des) utilisateurs
Modification :
ALTER ROLE name [ [ WITH ] option [ ... ] ]
Suppression :
DROP ROLE [ IF EXISTS ] name [, ...]
Accorder des droits :
GRANT { une liste de privileges }
ON une liste de zones de la BD
TO { un certain role }
...Les révoquer :
REVOKE { une liste de privileges }
ON une liste de zones de la BD
FROM { un certain role }
DDL : définir la structure des tables
Création et suppression de tables
Création :
CREATE TABLE my_first_table (
first_column text,
second_column integer
);
Suppression :
DROP TABLE my_first_table;
Exemples :
CREATE TABLE Livres (
Auteur varchar(64),
Titre text,
Date date,
ISBN integer
);
DROP TABLE Livres;
Modification des tables
ALTER TABLE [ IF EXISTS ] [ ONLY ] name [ * ]
action [, ... ]
Action :
ADD [ COLUMN ] column_name data_type [ COLLATE collation ]
[ column_constraint [ ... ] ]
DROP [ COLUMN ] [ IF EXISTS ] column_name
[ RESTRICT | CASCADE ]
ALTER [ COLUMN ] column_name [ SET DATA ] TYPE data_type
[ COLLATE collation ] [ USING expression ]
DROP CONSTRAINT [ IF EXISTS ] constraint_name
[ RESTRICT | CASCADE ]
INHERIT parent_table
(...)
Renommer une table :
ALTER TABLE my_table RENAME TO new_name;
Types de données : numériques
Name Size Range
smallint 2 bytes £-2^{15}£ to £+2^{15}-1£
integer 4 bytes £-2^{31}£ to £+2^{31}-1£
bigint 8 bytes £-2^{63}£ to £+2^{63}-1£
decimal variable up to £10^{2 ^{\wedge} 17}£, +16k fractional digits
numeric variable up to £10^{2 ^{\wedge} 17}£, +16k fractional digits
real 4 bytes 6 decimal digits precision
double precision 8 bytes 15 decimal digits precision
smallserial 2 bytes 1 to £2^{15}-1£, autoincrementing
serial 4 bytes 1 to £2^{31}-1£, autoincrementing
bigserial 8 bytes 1 to £2^{63}-1£, autoincrementing
Remarque : pas d'entiers non signés (0 à £2^{\{16,32,64\}}-1£).
Remarque : ...car impossible de deviner les types dans £2^{14}+2^{14}£ ...
Des entiers non signés ?
Définir un nouveau type de base non existant en PostGreSQL peut se faire en écrivant une librairie en C :
- Écrire un programme C définissant le type (typedef ...) ;
- Toujours en C, écrire les fonctions d'entrée/sortie ;
- Compiler la librairie (utilisable par PostGreSQL) ;
- Écrire les fonctions d'entrée/sortie dans PostGreSQL ;
- Enfin, CREATE TYPE <nom_du_type> { ... }.
Exemple :
CREATE TYPE uint64 (
internallength = 8,
input = uint64_in,
output = uint64_out,
receive = uint64_recv,
send = uint64_send,
alignment = 8
);
Référence
Types de données : chaînes de caractères
Name Description
character varying(n), varchar(n) variable-length with limit
character(n), char(n) fixed-length, blank padded
text variable unlimited length
Exemple :
CREATE TABLE test (b varchar(5));
INSERT INTO test VALUES ('ok');
INSERT INTO test VALUES ('good ');
INSERT INTO test VALUES ('too long');
--ERROR: value too long for type character varying(5)
-- explicit truncation
INSERT INTO test VALUES ('too long'::varchar(5));
SELECT b, char_length(b) FROM test;
b | char_length
-------+-------------
ok | 2
good | 5
too l | 5
Types de données : heure et date
Version légèrement simplifiée (cf. doc) :
Name Size Description
timestamp [ (p) ] 8 bytes both date and time
date 4 bytes date (no time of day)
time [ (p) ] 8 bytes time of day (no date)
interval [ fields ] [ (p) ] 12 bytes time interval
Exemple :
CREATE TABLE test (t timestamp, d date NOT NULL);
INSERT INTO test VALUES ('1999-01-08 04:05:06', '2013-12-20');
INSERT INTO test (t) VALUES ('January 8 04:05:06 1999');
--ERROR: null value in column "d" violates not-null constraint
INSERT INTO test (d) VALUES ('January 6, 2012');
INSERT INTO test VALUES ('2099-06-18 01:23:55', '2011-11-01');
SELECT t, d FROM test;
t | d
---------------------+------------
1999-01-08 04:05:06 | 2013-12-20
| 2012-01-06
2099-06-18 01:23:55 | 2011-11-01
DML : altérer le contenu des tables
Insert : exemples
CREATE TABLE t (
qte integer,
nom varchar(64) DEFAULT 'generic',
prix numeric
);
-- add one full line
INSERT INTO t (nom, qte, prix) VALUES ('Cheese', 1, 9.99);
-- with some default values
INSERT INTO t VALUES (1, DEFAULT, 9.99); -- or:
INSERT INTO t (qte, prix) VALUES (1, 9.99);
-- multiple rows in a single command
INSERT INTO t (qte, nom, prix) VALUES
(1, 'Cheese', 9.99),
(2, 'Bread', 1.99),
(3, 'Milk', 2.99);
Importer beaucoup de lignes depuis un fichier :
COPY table_name [ ( column_name [, ...] ) ]
FROM { 'filename' | PROGRAM 'command' | STDIN }
[ [ WITH ] ( option [, ...] ) ]
-- ou vers un fichier : 'COPY ... TO ...'
Update
[ WITH [ RECURSIVE ] with_query [, ...] ]
UPDATE [ ONLY ] table_name [ * ] [ [ AS ] alias ]
SET { column_name = { expression | DEFAULT } |
( column_name [, ...] ) =
( { expression | DEFAULT } [, ...] ) } [, ...]
[ FROM from_list ]
[ WHERE condition | WHERE CURRENT OF cursor_name ]
[ RETURNING * | output_expression [ [ AS ] output_name ]
[, ...] ]
Exemples :
UPDATE weather SET (temp1,temp2,pcp)=(temp1+1,temp1+15,DEFAULT)
WHERE city = 'San Francisco' AND date = '2003-07-03'
[ RETURNING temp1, temp2 AS max_temperature, pcp; ]
UPDATE employees SET sales_count = sales_count + 1 FROM accounts
WHERE accounts.name = 'Acme Corp.'
AND employees.id = accounts.sales_person;
UPDATE employees SET sales_count = sales_count + 1 WHERE id =
(SELECT sales_person FROM accounts WHERE name = 'Acme Corp.');
Upsert ? (Update or insert)
But
Envoyer des données dans une table, sans se soucier si elles
existent déjà (update) ou non (insert).
Idée : 1] essayer un update ; 2] en cas d'échec, effectuer un insert.
-- Possible loop
UPDATE db SET b = data WHERE a = key;
IF found THEN
RETURN;
END IF;
-- not there, so try to insert the key
BEGIN
INSERT INTO db(a,b) VALUES (key, data);
RETURN;
EXCEPTION WHEN unique_violation THEN
-- Do nothing, and loop to try the UPDATE again.
END;
Explications
Delete (a table)
[ WITH [ RECURSIVE ] with_query [, ...] ]
DELETE FROM [ ONLY ] table_name [ * ] [ [ AS ] alias ]
[ USING using_list ]
[ WHERE condition | WHERE CURRENT OF cursor_name ]
[ RETURNING * | output_expression
[ [ AS ] output_name ] [, ...] ]
Exemples :
-- Delete all films but musicals
DELETE FROM films WHERE kind <> 'Musical';
-- Clear the table films
DELETE FROM films;
TRUNCATE films; -- Faster, PostGreSQL extension
-- Delete completed tasks, returning the deleted rows
DELETE FROM tasks WHERE status = 'DONE' RETURNING *;
-- Delete the row on which the index c_tasks is positioned
DELETE FROM tasks WHERE CURRENT OF c_tasks;
TCL : Transaction Control Language
Contrôle la façon dont les changements (DML) s'appliquent.
Permet de regrouper les instructions SQL en transactions logiques.
COMMIT -- save work done
SAVEPOINT -- identify a point in a transaction
-- to which you can later roll back
ROLLBACK -- restore database to original since
-- the last COMMIT
SET TRANSACTION -- Change transaction options like isolation
-- level and what rollback segment to use
Exemple :
BEGIN [ TRANSACTION ISOLATION LEVEL SERIALIZABLE ] ;
UPDATE accounts SET balance=balance+100.0 WHERE acctnum=12345;
UPDATE accounts SET balance=balance-100.0 WHERE acctnum=7534;
COMMIT;
SQL : ACID
Atomicité : une transaction s'effectue complètement ou pas du tout.
Cohérence : toute transaction amène le système dans un état valide.
Isolation : l'exécution simultanée de transactions produit le même état que celui obtenu par l'exécution en série des transactions.
Durabilité : une transaction confirmée est assurée d'être exécutée.
Les SGBDR "classiques" ont ces propriétés :
- [propriétaires] Oracle, Microsoft Access, ...
- [open source] PostgreSQL, SQLite, ...
- (suivant le moteur de stockage) MySQL, MariaDB.
Page Wikipedia
Exemple : échec ACID
CREATE TABLE acidtest (A INTEGER, B INTEGER CHECK (A+B=100));
£T_1£ transfert 10 de A à B, et £T_2£ transfert 5 de B à A.
Possible exécution simultanée :
- £T_1^{(1)}£ : A est réduit de 10 ;
- £T_2^{(1)}£ : B est réduit de 5 ;
- £T_2^{(2)}£ : A est augmenté de 5 ;
- £T_1^{(2)}£ : B est augmenté de 10.
Si panne entre 3] et 4] :
- £T_1£ est annulée : A réinitialisé à son état avant transaction ;
- mais £T_2£ a été exécutée et validée ...
£\Rightarrow£ Échec d'atomicité et de cohérence.
Garantie ACID : cela n'arrive jamais.
NoSQL
Catégorie de SGBD qui n'est plus fondée sur l'architecture classique des bases relationnelles.
L'unité logique n'y est plus la table, et les données ne sont en général pas manipulées avec SQL.
But : améliorer les performances des SGBD classiques sur de très grandes bases de données.
Il faut cependant accepter un compromis :
CAP theorem
Une base de données distribuée ne peut garantir simultanément
- Consistency : all nodes see the same data at the same time ;
- Availability : every request receives a response quickly ;
- Partition tolerance : local failures don't stop the system.
...mais ACID possible : FoundationDB, Spanner, Marklogic ?!
Bilan
PostGreSQL permet de créer/modifier/supprimer ...
- un utilisateur ;
- une base de données ;
- une table (dans une base de données) ;
Les attributs peuvent avoir divers types (voir la documentation).
On peut contrôler la façon dont les transactions s'exécutent.
/