£ \newcommand{\cC}{{\cal C}} \newcommand{\cT}{{\cal T}} \newcommand{\cE}{{\cal E}} \newcommand{\cP}{{\cal P}} \newcommand{\cB}{{\cal B}} \newcommand{\cU}{{\cal U}} \newcommand{\cA}{{\cal A}} \newcommand{\cL}{{\cal L}} \newcommand{\cG}{{\cal G}} \newcommand{\cH}{{\cal H}} \newcommand{\cS}{{\cal S}} \newcommand{\cN}{{\cal N}} \newcommand{\cD}{{\cal D}} \newcommand{\C}{\mathbb{C}} \newcommand{\N}{\mathbb{N}} \newcommand{\E}{\mathrm{E}} \newcommand{\R}{\mathbb{R}} \newcommand{\P}{\mathrm{P}} \newcommand{\Q}{\mathbb{Q}} \newcommand{\U}{\mathbb{U}} \newcommand{\Z}{\mathbb{Z}} \newcommand{\L}{\mathbb{L}} \newcommand{\1}{\mathbb{1}} \newcommand{\puiss}{e\thinspace \mbox{\small -}} \newcommand{\esp}{\thinspace} \newcommand{\tr}{{}^t \negthinspace} £

Requêtes SQL
via PostgreSQL

Définition intuitive
Base de Données (B[d]D)

"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."

Réseau d'ordinateurs connectés à une ou plusieurs BdD

DQL : interroger la base

Choix du SGBD
Système de gestion de bases de données (SGBD)

Ensemble de logiciels permettant aux utilisateurs

  • d'insérer, de modifier, de rechercher efficacement
  • des données spécifiques
  • dans une grande masse d’informations
  • partagées par de multiples utilisateurs.
PostGreSQL :
  • logiciel libre, open source ;
  • un des plus respectueux des normes SQL ;
  • robuste (peu de bugs), extensible ;
  • très bonne documentation.

Requêtes sur une seule table

SELECT ... FROM ...

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;
SELECT ... FROM ... WHERE ...

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%';
Exercices

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';
UNION

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';
  • "UNION" élimine les doublons (comme "SELECT DISTINCT").
  • Si on veut les doublons, il faut utiliser "UNION ALL".
COUNT

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;
MAX, MIN, SUM, ...

£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.

Exercices

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;
SELECT ... FROM ... WHERE ... GROUP BY ...

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;
ORDER BY ...
-- 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;
Exercices

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;
CASE WHEN ... THEN ...

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.

Requêtes sur plusieurs tables

Clés
Clé primaire

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 :
  • Unicité ?
  • Performance ?
Clé étrangère

Attribut qui est une clé primaire dans une autre table.

Exemple : Client(ID, Nom, Prenom, DateDeNaissance)
Exemple : Commande(ID,ClientID,Quantite,Prix,Produit,Date)

JOIN : concaténation de tables

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);
Inconvénients :
  • la requête devient vite peu lisible avec des filtres sur les rangées ;
  • possibles soucis de performance (syntaxe "produit cartésien").
[INNER] JOIN

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.

OUTER JOIN

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.
Autres comportements possibles :
  • LEFT OUTER JOIN : n'ajoute que les rangées manquantes
    LEFT OUTER JOIN : de la première table ;
  • RIGHT OUTER JOIN : n'ajoute que les rangées manquantes
    RIGHT OUTER JOIN : de la seconde table.
Exercice

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';

Requêtes en plusieurs temps

Requêtes imbriquées

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);
Exemple : groupes ayant donné au moins un concert.
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.

Requêtes imbriquées - suite
Exemple : salariés dont le salaire dépasse 45k.
SELECT *
FROM Company
WHERE ID IN 
  (SELECT ID
   FROM Company
   WHERE Salary > 45000) ;
("NOT IN" afficherait le complémentaire.)
Exemple : groupes de métal dont le nom commence par un E.
SELECT *
FROM
  (SELECT Nom,Style
   FROM Groupe
   WHERE Nom LIKE 'E%') AS tableTemp
WHERE tableTemp.Style = 'Symphonic Metal';
Requêtes imbriquées - "fin"
Exemple : commandes dont l'ID client est plus grand qu'au moins un des ID de clients ayant commandé 5 exemplaires d'un article.
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);
Exemple : groupes jouant dans des lieux ne se terminant pas par O.
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');
Exercice

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'))
WITH ...

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;
  1. Régions dont les sommes des ventes dépassent le 1/10 du total.
  2. Afficher ces régions en les regroupants par produits.
Les vues

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
Exemple :
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;
WITH RECURSIVE

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.

  1. Une rangée est ajoutée avec la valeur 1 (initialisation).
  2. À chaque itération, la dernière rangée calculée est incrémentée et ajoutée au résultat.

Exemple d'utilisation plus réaliste

WITH RECURSIVE (détails)
  1. Initialisation d'une table résultat (R) avec une requête SQL simple. Recopie de ce premier résultat dans une table de travail (T)
  2. Tant que la table de travail n'est pas vide :
    1. Évaluer la requête récursive en utilisant T. Appelons le résultat "tmp"
    2. Ajouter le contenu de tmp dans R.
    3. Remplacer le contenu de T par celui de tmp

Note : avec le mot-clé UNION on élimine les doublons en 1, 2.b et 2.c.
Note : Avec UNION ALL, on les garde.

Requête SQL "factorielle"
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

nfact

tmp

nfact

R

nfact
11
11
11
22
22
11
22
36
36
11
22
36
WITH RECURSIVE : parcours de graphe
-- 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 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 :
'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.

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 ...

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
NameSizeRange
smallint2 bytes£-2^{15}£ to £+2^{15}-1£
integer4 bytes£-2^{31}£ to £+2^{31}-1£
bigint8 bytes£-2^{63}£ to £+2^{63}-1£
decimalvariableup to £10^{2 ^{\wedge} 17}£, +16k fractional digits
numericvariableup to £10^{2 ^{\wedge} 17}£, +16k fractional digits
real4 bytes6 decimal digits precision
double precision8 bytes15 decimal digits precision
smallserial2 bytes1 to £2^{15}-1£, autoincrementing
serial4 bytes1 to £2^{31}-1£, autoincrementing
bigserial8 bytes1 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 :
  1. Écrire un programme C définissant le type (typedef ...) ;
  2. Toujours en C, écrire les fonctions d'entrée/sortie ;
  3. Compiler la librairie (utilisable par PostGreSQL) ;
  4. Écrire les fonctions d'entrée/sortie dans PostGreSQL ;
  5. 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
NameDescription
character varying(n), varchar(n)variable-length with limit
character(n), char(n)fixed-length, blank padded
textvariable 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) :
NameSizeDescription
timestamp [ (p) ]8 bytesboth date and time
date4 bytesdate (no time of day)
time [ (p) ]8 bytestime of day (no date)
interval [ fields ] [ (p) ]12 bytestime 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.

Explications

    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 :
  1. £T_1^{(1)}£ : A est réduit de 10 ;
  2. £T_2^{(1)}£ : B est réduit de 5 ;
  3. £T_2^{(2)}£ : A est augmenté de 5 ;
  4. £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 ...

Les attributs peuvent avoir divers types (voir la documentation).

On peut contrôler la façon dont les transactions s'exécutent.

/