Project Oxygen & Ideo-LabIDEO LAB Dashboard 2026

🐘 PostgreSQL – Installation, SQL & Administration

Guide complet IDEO‑Lab pour maĂźtriser le SGBD objet-relationnel, de l'installation Ă  la rĂ©plication.

1.1

Vue d'ensemble

SGBD Objet-Relationnel, ACID, extensible.

SGBD-OR ACID Extensible
1.2

Architecture

Processus (Postmaster), MVCC, WAL.

MVCC WAL Processus
2.1

Installation Linux

Ubuntu/Debian & RHEL/CentOS (DépÎts PG).

apt dnf postgresql.org
2.2

Install (Win/Docker)

Installeur EDB, conteneur Docker (reco).

Windows Docker EDB
2.3

Client psql

CLI, méta-commandes (\l, \dt, \c).

psql \l \dt
3.1

RĂŽles & Authentification

CREATE ROLE, pg_hba.conf.

ROLE GRANT pg_hba.conf
3.2

Gestion des BDD

CREATE DATABASE, \l, \c.

DATABASE \l \c
3.3

Gestion des Schemas

Schema 'public', search_path, isolation.

SCHEMA public search_path
4.1

Types de Données Av.

JSONB, ARRAY, UUID, HSTORE.

JSONB ARRAY UUID
4.2

DDL : CREATE TABLE

SERIAL, IDENTITY, Contraintes.

CREATE TABLE SERIAL IDENTITY
4.3

DML : INSERT (RETURNING)

INSERT...RETURNING, UPDATE, DELETE.

INSERT UPDATE RETURNING
5.1

DQL : SELECT & JOIN

SELECT, INNER/LEFT JOIN.

SELECT JOIN Relationnel
5.2

DQL : Agrégats

GROUP BY, COUNT, FILTER, HAVING.

GROUP BY COUNT FILTER
5.3

Transactions (ACID)

BEGIN, COMMIT, ROLLBACK.

BEGIN COMMIT ROLLBACK
6.1

Index (EXPLAIN)

EXPLAIN ANALYZE, B-Tree, GIN, GiST.

EXPLAIN GIN GiST
6.2

Fonctions & Triggers

pl/pgsql, CREATE FUNCTION, TRIGGER.

pl/pgsql FUNCTION TRIGGER
6.3

Vues & Vues Mat.

CREATE VIEW, Vues Matérialisées.

VIEW MATERIALIZED
7.1

Backup (pg_dump)

Dump logique (SQL) vs Binaire (Custom).

pg_dump pg_dumpall
7.2

Restauration

psql (SQL) ou pg_restore (Custom).

pg_restore psql
7.3

Réplication (Streaming)

Hot Standby, Read Replica, WAL.

Replication Hot Standby
8.1

Cheat-sheet psql

Commandes psql et SQL fréquentes.

cheat psql
1.1 Vue d'ensemble PostgreSQL
Le SGBD Open-Source le plus avancé

PostgreSQL (souvent "Postgres") est un **SGBD Objet-Relationnel (SGBD-OR)**. Il est réputé pour sa robustesse, sa conformité stricte aux standards SQL, et son ensemble de fonctionnalités avancées.

Il est 100% open-source (licence PostgreSQL, similaire à BSD/MIT) et n'est contrÎlé par aucune entreprise (contrairement à MySQL/MariaDB), mais par une communauté mondiale.

Caractéristiques Clés
  • ConformitĂ© ACID : Garantie totale des propriĂ©tĂ©s (AtomicitĂ©, CohĂ©rence, Isolation, DurabilitĂ©).
  • ExtensibilitĂ© : Vous pouvez crĂ©er vos propres types de donnĂ©es, fonctions, opĂ©rateurs et index.
  • Types de donnĂ©es riches : Support natif pour JSONB (binaire), ARRAY (tableaux), UUID, types gĂ©ospatiaux (PostGIS).
  • Concurrence : GĂ©rĂ©e via **MVCC** (Multi-Version Concurrency Control), permettant aux lecteurs de ne pas bloquer les scripteurs (et vice-versa).
Postgres vs. MySQL/MariaDB

Choix trÚs courant, voici les différences philosophiques :

CritĂšrePostgreSQLMySQL / MariaDB
ModĂšleObjet-Relationnel (plus strict)Relationnel (plus permissif)
ACIDStricte. Ne sacrifie jamais l'intĂ©gritĂ©.Strict (avec InnoDB), mais peut ĂȘtre plus laxiste.
Cas d'usageDonnées complexes, applications "sérieuses", GIS, data warehousing.Applications Web, CMS (WordPress), "backend" simple.
TypesJSONB, ARRAY, UUID natifs et performants.JSON (texte, moins performant), pas d'ARRAY.
ExtensibilitĂ©ExtrĂȘme (PostGIS, TimescaleDB).LimitĂ©e (Moteurs de stockage).

Analogie : MySQL/MariaDB est une voiture de sport rapide et simple. PostgreSQL est un "char d'assaut" : plus complexe, mais incroyablement robuste, fiable et capable de tout faire.

1.2 Architecture PostgreSQL (MVCC & WAL)
Architecture Processus (Pas de Threads)

Contrairement Ă  MySQL, Postgres utilise un modĂšle "processus par connexion".

+------------------------------------------------------+
| SERVEUR POSTGRESQL                                   |
|                                                      |
|  +------------------+     Mémoire Partagée (Cache)   |
|  | Postmaster       | <--> [Shared Buffers]          |
|  | (Processus PĂšre) | <--> [WAL Buffers]             |
|  +------------------+                                |
|       | (fork) | (fork)                              |
|       ▌        ▌                                     |
|  +---------+ +---------+ (Processus "Backend")       |
|  | postgres| | postgres| ... 1 par client            |
|  | (Client 1)| (Client 2)|                           |
|  +---------+ +---------+                           |
|       |        |                                     |
|       ▌        ▌                                     |
|  +---------+ +---------+ (Processus "Workers")       |
|  | Autovacuum| | WAL Writer| (BGWriter, Checkpointer)|
|  +---------+ +---------+                           |
|                                                      |
+------------------------------------------------------+
  • Postmaster : Le "pĂšre" de tous les processus. Il Ă©coute les connexions.
  • Backend (postgres) : Quand un client se connecte, le Postmaster "fork" un nouveau processus postgres dĂ©diĂ© Ă  ce client.
  • MĂ©moire PartagĂ©e : (shared_buffers) Le cache principal (RAM) partagĂ© par tous les processus.
  • Workers : Processus d'arriĂšre-plan (autovacuum, wal_writer...).
MVCC (Multi-Version Concurrency Control)

C'est le concept fondamental de Postgres pour gérer la concurrence. Au lieu de verrouiller les lignes, Postgres crée de nouvelles "versions" des lignes.

Quand vous faites un UPDATE :

  1. Postgres ne modifie pas la ligne originale.
  2. Il copie la ligne, effectue la modification sur la copie, et l'insĂšre comme une nouvelle version de la ligne.
  3. Il marque l'ancienne version comme "morte" (visible uniquement par les transactions qui ont commencé *avant* l'UPDATE).
Table "users" (État T1)
ID | nom   | xmin | xmax
---|-------|------|------
1  | Alice | 10   | inf

-- Client 1 (Transaction 15): UPDATE users SET nom = 'Alicia' WHERE id = 1;

Table "users" (État T2)
ID | nom   | xmin | xmax  <-- (Transaction 12 voit encore ça)
---|-------|------|------
1  | Alice | 10   | 15    (Morte, tuée par TX 15)
1  | Alicia| 15   | inf   (Nouvelle version, créée par TX 15)
  • Avantage : Les SELECT (lecteurs) ne bloquent **jamais** les UPDATE (scripteurs), et vice-versa.
  • InconvĂ©nient : Les "lignes mortes" s'accumulent (gonflement/bloat).
  • Solution : Le processus AUTOVACUUM tourne en permanence pour nettoyer ces lignes mortes.
WAL (Write-Ahead Logging)

C'est le mécanisme qui garantit la **Durabilité (le D de ACID)** et permet la réplication.

Avant d'écrire *quoi que ce soit* dans les fichiers de données (lents), Postgres écrit *d'abord* la modification dans un journal de transactions (le WAL).

[Client: COMMIT;]
    |
    ▌
1. Écriture dans [WAL Buffer] (RAM)
    |
    ▌
2. 'fsync' (écrit sur disque) -> [Fichiers WAL (/pg_wal/)]
    |
    ▌
3. [Réponse "OK" au Client]
    |
    | (Plus tard, en arriĂšre-plan...)
    ▌
4. Écriture dans [Shared Buffers] (RAM)
    |
    ▌
5. (Checkpoint) Écriture dans [Fichiers de donnĂ©es] (Disque)
  • Garantie : Si le serveur crash Ă  l'Ă©tape 4, au redĂ©marrage, Postgres lit le WAL (Ă©tape 2) et "rejoue" les transactions qui n'Ă©taient pas encore dans les fichiers de donnĂ©es.
  • RĂ©plication : Le serveur "Slave" reçoit le flux du WAL (Ă©tape 2) et le rejoue sur ses propres donnĂ©es.
2.1 Installation Linux (DépÎts officiels)
Ubuntu 22.04+ / Debian 11+

Fortement recommandé : N'utilisez pas les dépÎts de votre OS (souvent obsolÚtes). Utilisez les dépÎts officiels de PostgreSQL.

# 1. Créer le fichier de dépÎt
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'

# 2. Ajouter la clé GPG
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -

# 3. Installer (Exemple: Postgres 16)
sudo apt-get update
sudo apt-get -y install postgresql-16 postgresql-client-16
RHEL / CentOS 8+ / Fedora
# 1. Installer le RPM du dépÎt (Ex: PG 16)
sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm

# 2. Désactiver le module PG natif (si nécessaire)
sudo dnf -qy module disable postgresql

# 3. Installer
sudo dnf install -y postgresql16-server postgresql16
Initialisation du Cluster (initdb)

Sur Debian/Ubuntu, l'installation exécute initdb automatiquement et démarre le service.

Sur RHEL/CentOS, vous devez le faire manuellement :

(RHEL/CentOS seulement)
# 1. Initialiser la BDD (crée le 'datadir')
sudo /usr/pgsql-16/bin/postgresql-16-setup initdb

# 2. Démarrer et activer
sudo systemctl enable --now postgresql-16
Authentification peer (Linux)

Par défaut, l'installation crée un super-utilisateur de BDD nommé postgres.

L'authentification est réglée sur peer, ce qui signifie que seul l'utilisateur systÚme postgres peut se connecter.

# Devenez l'utilisateur systĂšme 'postgres'
sudo -i -u postgres

# Lancez psql (connexion réussie)
psql

postgres=# \q
exit

Vous ne pouvez pas vous connecter avec psql -u postgres depuis votre compte normal. Voir la section 3.1 (pg_hba.conf) pour changer cela.

2.2 Installation (Windows & Docker)
Option 1 : Installeur Windows (EDB)

L'installeur officiel pour Windows est fourni par EDB (EnterpriseDB).

  1. Allez sur postgresql.org/download/windows/
  2. Téléchargez l'installeur EDB.
  3. Exécutez l'installeur.
  4. Étape clĂ© : DĂ©finissez un mot de passe pour le super-utilisateur postgres.
  5. Laissez le port (5432).
  6. Choisissez la "Locale" (ex: fr_FR).

L'installeur installe Postgres en tant que service Windows et ajoute psql Ă  votre PATH.

Il installe aussi pgAdmin (un client GUI populaire).

(Dans cmd ou PowerShell)
psql -U postgres
Password for user postgres: [votre-mot-de-passe]
Option 2 : Docker (Recommandé pour le Dev)

La méthode la plus propre, la plus rapide et la plus isolée pour le développement.

# Lancer un conteneur Postgres
docker run -d \
  --name pg-local \
  -e POSTGRES_PASSWORD=mon_pass_secret \
  -p 5432:5432 \
  -v pg-data:/var/lib/postgresql/data \
  postgres:16-alpine
  • -d : DĂ©tachĂ©.
  • -e POSTGRES_PASSWORD : DĂ©finit le mot de passe pour l'utilisateur postgres.
  • -p 5432:5432 : Lie le port 5432 de votre PC au port 5432 du conteneur.
  • -v pg-data... : CrĂ©e un volume Docker "pg-data" pour la persistance des donnĂ©es.
  • postgres:16-alpine : Image officielle, version 16 (lĂ©gĂšre).
# Se connecter (depuis votre PC)
psql -h localhost -U postgres
2.3 Client CLI : psql (Méta-commandes)
Connexion psql

psql est le client interactif. Il est plus puissant que mysql car il possÚde des "méta-commandes" (commençant par \) qui ne sont pas du SQL.

# Syntaxe
psql -h [HOST] -U [USER] -p [PORT] -d [DATABASE]

# Connexion locale (compte 'postgres')
sudo -u postgres psql

# Connexion locale (votre compte) Ă  une BDD
psql -U mon_user -d ideo_lab_crm

# Connexion distante
psql -h db.ideolab.com -U mon_user -d ideo_lab_prod
Le Prompt

Le prompt psql vous donne des informations :

ideo_lab_prod=#

(Connecté à 'ideo_lab_prod' en tant que super-utilisateur #)

ideo_lab_prod=>

(Connecté à 'ideo_lab_prod' en tant qu'utilisateur normal >)

ideo_lab_prod-(

(Dans une transaction BEGIN; non terminée)

Méta-commandes (Cheat-sheet)

C'est ce qui rend psql génial. (Pas besoin de ;)

CommandeDescription
\qQuitter psql.
\l ou \listLister toutes les bases de données.
\c [nom_db]Se Connecter Ă  une autre base.
\dtDécrire les Tables (du schema actuel).
\d [nom_table]Décrire une table (colonnes, index...).
\dnDécrire les Schemas.
\dfDécrire les Fonctions.
\duDécrire les RÎles (utilisateurs).
\conninfoInfos sur la Connexion actuelle.
\timingActive/DĂ©sactive l'affichage du temps d'exĂ©cution des requĂȘtes.
\eOuvre la requĂȘte actuelle dans l'Ă©diteur ($EDITOR).
\?Aide sur les méta-commandes.
3.1 RĂŽles & Authentification (pg_hba.conf)
Concept : RĂŽles (Utilisateurs et Groupes)

Postgres a un concept unifiĂ© : les RĂŽles. Un RĂŽle peut ĂȘtre un utilisateur, un groupe, ou les deux.

  • Un RĂŽle avec l'attribut LOGIN est un "Utilisateur" (peut se connecter).
  • Un RĂŽle sans l'attribut LOGIN est un "Groupe".

On peut ensuite "GRANTer" un RÎle-Groupe à un RÎle-Utilisateur (héritage).

[ROLE "groupe_lecture"] (NOLOGIN)
  (GRANT SELECT ON ALL TABLES...)
    ^
    | (GRANT)
    |
[ROLE "alice"] (LOGIN)
[ROLE "bob"] (LOGIN)
Création de RÎles (Utilisateurs)
-- Crée un rÎle qui peut se connecter (un utilisateur)
CREATE ROLE ideo_app WITH
    LOGIN
    PASSWORD 'pass_tres_fort';

-- Crée un super-utilisateur (NE PAS FAIRE en prod app)
CREATE ROLE admin_dev WITH
    LOGIN
    SUPERUSER
    PASSWORD 'admin_pass';

-- Crée un rÎle 'groupe'
CREATE ROLE readonly_group NOLOGIN;
Gestion des PrivilĂšges (GRANT)

Les privilÚges sont gérés par objet (BDD, Schema, Table...).

-- 1. Donner le droit de se connecter Ă  une BDD
GRANT CONNECT ON DATABASE ideo_lab_crm TO ideo_app;

-- 2. Donner le droit d'utiliser un schema
GRANT USAGE ON SCHEMA public TO ideo_app;

-- 3. Donner des droits sur les tables
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO ideo_app;

-- Gérer l'héritage (groupes)
GRANT readonly_group TO ideo_app;
Fichier pg_hba.conf (Host-Based Authentication)

C'est l'étape la plus importante (et la plus confuse pour les débutants).

Ce fichier (situé dans le datadir) est le **portier** de Postgres. Il décide QUI (IP, User) peut se connecter et QUELLE méthode d'authentification utiliser.

Il est lu de haut en bas. La premiÚre rÚgle qui correspond est utilisée.

# Fichier: /etc/postgresql/16/main/pg_hba.conf

# TYPE  DATABASE    USER        ADDRESS           METHOD
#---------------------------------------------------------
# Connexions locales UNIX (ex: sudo -u postgres psql)
local   all         postgres                      peer
local   all         all                           peer

# Connexions locales TCP/IP (ex: localhost, 127.0.0.1)
host    all         all         127.0.0.1/32      scram-sha-256

# Connexions réseau (Ex: DBeaver, App Web)
host    all         all         192.168.1.0/24    scram-sha-256

# Connexion pour la réplication
host    replication repl_user   192.168.1.10/32   scram-sha-256
MéthodeDescription
peer(Socket local) Fait confiance si le nom d'utilisateur systĂšme correspond au nom d'utilisateur BDD. (sudo -u postgres psql).
scram-sha-256Recommandé. Mot de passe chiffré.
md5Ancien standard de mot de passe. OK, mais scram est mieux.
trustDangereux. Connexion sans mot de passe. (À n'utiliser *jamais* sauf en dev local).

Toute modification de pg_hba.conf nécessite un rechargement (sudo systemctl reload postgresql) ou redémarrage.

3.2 Gestion des Bases de Données
Isolation

Dans Postgres, les BDD sont des conteneurs totalement isolés. Une connexion est liée à UNE seule BDD. Vous ne pouvez pas faire de JOIN entre BDD (contrairement à MySQL).

(Pour cela, on utilise les Schemas, voir 3.3).

Créer une BDD (SQL)
-- Syntaxe
CREATE DATABASE nom_db
    WITH
    OWNER = role_proprietaire
    ENCODING = 'UTF8'
    LC_COLLATE = 'fr_FR.UTF-8'
    LC_CTYPE = 'fr_FR.UTF-8'
    TEMPLATE = template0;

-- Exemple simple (suffisant 99% du temps)
CREATE DATABASE ideo_lab_crm OWNER ideo_app;
Créer une BDD (CLI)

(Exécuté depuis le shell bash, pas psql)

sudo -u postgres createdb -O ideo_app ideo_lab_crm
Lister & Gérer (psql)
postgres=# \l
-- (ou \list) Liste toutes les BDD

                               List of databases
       Name       |  Owner   | Encoding |   Collate   |    Ctype    | ...
------------------+----------+----------+-------------+-------------+-----
 ideo_lab_crm   | ideo_app | UTF8     | fr_FR.UTF-8 | fr_FR.UTF-8 |
 postgres         | postgres | UTF8     | fr_FR.UTF-8 | fr_FR.UTF-8 |
 template0        | postgres | UTF8     | fr_FR.UTF-8 | fr_FR.UTF-8 |
 template1        | postgres | UTF8     | fr_FR.UTF-8 | fr_FR.UTF-8 |

template0 et template1 sont les modÚles utilisés par CREATE DATABASE. Ne pas y toucher.

Se connecter (psql)
postgres=# \c ideo_lab_crm
You are now connected to database "ideo_lab_crm" as user "postgres".
ideo_lab_crm=#
Supprimer une BDD
-- SQL
DROP DATABASE ideo_lab_crm;

-- CLI (bash)
sudo -u postgres dropdb ideo_lab_crm
3.3 Gestion des Schemas (Concept Clé)
Schema : Un "dossier" dans votre BDD

Si une BDD est un "Projet", un Schema est un "dossier" dans ce projet. Il permet d'organiser les tables logiquement.

C'est ce qui permet à Postgres de gérer des applications multi-tenant ou complexes dans une seule BDD.

BDD "ideo_lab_main"
├── Schema: public (DĂ©faut)
│   ├── Table: utilisateurs
│   └── Table: entreprises
│
├── Schema: crm
│   ├── Table: clients (public.entreprises)
│   └── Table: contacts
│
└── Schema: compta
    ├── Table: factures
    └── Table: lignes_facture
Le schema public

Toute nouvelle BDD contient un schema public. Si vous faites CREATE TABLE users, Postgres crée en fait public.users.

search_path (Le "PATH" de Postgres)

Comment Postgres sait-il oĂč chercher (public, crm...) quand vous Ă©crivez SELECT * FROM clients ?

Il utilise le search_path.

-- Voir le search_path actuel
SHOW search_path;
-- Résultat: "$user", public

-- (Il cherche d'abord un schema Ă  votre nom, puis 'public')

-- Changer le search_path pour la session
SET search_path TO crm, public;

-- Maintenant, 'SELECT * FROM clients' va chercher 'crm.clients'
-- 'SELECT * FROM utilisateurs' va chercher 'public.utilisateurs'
Utilisation
-- Créer un schema
CREATE SCHEMA crm;

-- Créer une table dans un schema
CREATE TABLE crm.contacts (
    id INT PRIMARY KEY,
    nom VARCHAR(100)
);

-- Lister les schemas (psql)
\dn
4.1 Types de Données Avancés
Types Standard

(Similaires Ă  MariaDB, mais avec des noms plus stricts)

TypeAliasDescription
TEXTTexte de longueur illimitée. (Il n'y a pas de VARCHAR vs TEXT en performance dans Postgres. Utilisez TEXT).
VARCHAR(n)Limite la longueur Ă  N caractĂšres (contrainte).
INTEGERINTEntier 4 octets.
BIGINTEntier 8 octets.
SMALLINTEntier 2 octets.
NUMERIC(p, s)DECIMALNombre à précision exacte (finance).
DOUBLE PRECISIONFLOAT8Virgule flottante 8 octets.
BOOLEANBOOLtrue ou false.
TIMESTAMPDate et heure (sans fuseau).
TIMESTAMPTZRecommandé. Timestamp avec fuseau horaire (stocké en UTC).
DATEDate seule.
Types Auto-Incrément (Clés Primaires)

Il y a deux façons de créer un ID auto-incrémenté :

1. SERIAL (Ancienne méthode)

SERIAL est un pseudo-type. C'est un alias qui crée un INTEGER et une SEQUENCE.

CREATE TABLE old_way (
    id SERIAL PRIMARY KEY,
    nom TEXT
);
2. IDENTITY (Méthode SQL standard, recommandée)

Depuis SQL:2003, la méthode standard est GENERATED AS IDENTITY.

CREATE TABLE new_way (
    id INT PRIMARY KEY
        GENERATED ALWAYS AS IDENTITY,
    -- (ou GENERATED BY DEFAULT AS IDENTITY)
    nom TEXT
);
  • GENERATED ALWAYS : Refuse un INSERT manuel de l'ID (INSERT INTO new_way(id, nom) VALUES(100, 'Test') -> Erreur). C'est plus strict et plus sĂ»r.
  • GENERATED BY DEFAULT : Autorise l'insertion manuelle de l'ID.
Types Avancés (La force de Postgres)
TypeDescriptionExemple d'utilisation
UUIDIdentifiant unique universel (128 bits).Clés primaires (évite les conflits en distribué). DEFAULT gen_random_uuid() (nécessite CREATE EXTENSION "pgcrypto").
JSONBStockage Binaire de JSON. C'est la version "intelligente" de JSON (qui est textuel).Stockage de mĂ©tadonnĂ©es, documents NoSQL. Peut ĂȘtre indexĂ© (GIN).
ARRAYTableau d'un autre type. Ex: TEXT[] ou INT[].Tags (TEXT[]), listes de permissions.
HSTOREStockage Clé-Valeur simple (texte). (Nécessite CREATE EXTENSION "hstore").Moins utilisé depuis JSONB, mais trÚs rapide pour des paires clé-valeur simples.
(PostGIS)Types géospatiaux (Points, Lignes, Polygones). (Nécessite CREATE EXTENSION "postgis").Cartographie, GIS.
CREATE TABLE produits (
    id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    ref UUID DEFAULT gen_random_uuid(),
    nom TEXT NOT NULL,
    tags TEXT[],
    specs JSONB
);
4.2 DDL : CREATE TABLE
Contraintes de Colonne
CREATE TABLE utilisateurs (
    id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    
    email TEXT NOT NULL
        CONSTRAINT email_unique UNIQUE, -- Contrainte nommée
        
    nom TEXT NOT NULL,
    
    date_naissance DATE
        CHECK (date_naissance > '1900-01-01'), -- Contrainte CHECK
        
    statut TEXT DEFAULT 'actif'
        CHECK (statut IN ('actif', 'inactif', 'archive')),
        
    cree_le TIMESTAMPTZ DEFAULT NOW()
);
Contraintes de Table (ClĂ©s ÉtrangĂšres)

Définit la relation entre commandes et utilisateurs.

CREATE TABLE commandes (
    id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    utilisateur_id INT NOT NULL,
    montant NUMERIC(10, 2) NOT NULL CHECK (montant > 0),
    
    -- Contrainte de ClĂ© ÉtrangĂšre
    CONSTRAINT fk_utilisateur
        FOREIGN KEY(utilisateur_id) 
        REFERENCES utilisateurs(id)
        ON DELETE RESTRICT -- (Quoi faire si l'utilisateur est supprimé)
);
Comportement ON DELETE
  • ON DELETE RESTRICT (DĂ©faut) : Refuse de supprimer un utilisateur s'il a encore des commandes. (SĂ»r).
  • ON DELETE CASCADE : Si on supprime un utilisateur, supprime automatiquement toutes ses commandes. (Dangereux, mais utile).
  • ON DELETE SET NULL : Si on supprime un utilisateur, met utilisateur_id Ă  NULL dans ses commandes.
4.3 DML : INSERT, UPDATE, DELETE
INSERT ... RETURNING

La clause RETURNING est une fonctionnalité trÚs puissante de Postgres. Elle permet de retourner des valeurs (comme l'ID) juste aprÚs l'insertion.

-- (Table 'utilisateurs' de l'exemple 4.2)

INSERT INTO utilisateurs (nom, email)
VALUES ('Jean Dupont', 'jean.dupont@mail.com')
RETURNING id, cree_le;

-- Résultat (trÚs utile pour les ORMs):
-- id | cree_le
-- ---|-------------------------------
-- 5  | 2025-11-01 22:30:00.12345+01
INSERT (JSONB & ARRAY)
INSERT INTO produits (nom, tags, specs)
VALUES (
    'Clavier Pro',
    ARRAY['mecanique', 'rgb', 'usb-c'],
    '{"poids": 500, "switch": "blue"}'::jsonb
);
UPDATE

(WHERE est obligatoire !)

UPDATE utilisateurs
SET
    nom = 'Jean Martin',
    statut = 'inactif'
WHERE email = 'jean.dupont@mail.com'
RETURNING id; -- (RETURNING marche aussi ici)
DELETE

(WHERE est obligatoire !)

DELETE FROM utilisateurs
WHERE statut = 'archive'
RETURNING email; -- (Retourne les emails supprimés)
TRUNCATE
-- Vider une table (rapide)
TRUNCATE TABLE logs;

-- Vider plusieurs tables (ex: dépendantes)
TRUNCATE TABLE utilisateurs, commandes RESTART IDENTITY CASCADE;
-- RESTART IDENTITY: Réinitialise les 'SERIAL'
-- CASCADE: Vide aussi les tables liées (commandes)
5.1 DQL : SELECT & JOIN

Le SQL DQL (SELECT) de Postgres est standard et trĂšs similaire Ă  celui de MariaDB (cf. sections 5.2, 6.1, 6.2 du guide MariaDB). Les concepts de SELECT, WHERE, ORDER BY, LIMIT, INNER JOIN, LEFT JOIN sont identiques.

RequĂȘte sur JSONB

La vraie différence réside dans l'interrogation des types avancés.

-- (Table 'produits' de l'exemple 4.3)

-- Sélectionner un champ JSONB (opérateur '->>')
SELECT nom, specs->>'switch' AS type_switch
FROM produits
WHERE specs->>'poids' = '500';

-- Filtrer si une clé JSONB existe (opérateur '?')
SELECT nom FROM produits
WHERE specs ? 'switch';

-- Filtrer sur un ARRAY (opérateur '@>')
-- (Trouve les produits qui ont (au moins) 'rgb' ET 'usb-c')
SELECT nom FROM produits
WHERE tags @> ARRAY['rgb', 'usb-c'];
5.2 DQL : Agrégats (GROUP BY)

Les agrégats (COUNT, SUM, AVG, MAX, MIN) et GROUP BY / HAVING sont standards.

Agrégats avancés (FILTER)

Postgres permet de filtrer un agrĂ©gat (sans filtrer toute la requĂȘte).

-- Compter le total, ET compter juste les actifs, en 1 passe
SELECT
    COUNT(id) AS total_utilisateurs,
    COUNT(id) FILTER (WHERE statut = 'actif') AS nb_actifs,
    COUNT(id) FILTER (WHERE statut = 'inactif') AS nb_inactifs
FROM utilisateurs;
Agrégats de chaßne/JSON
-- Regrouper tous les noms dans un seul texte
SELECT STRING_AGG(nom, ', ') FROM utilisateurs;
-- 'Jean Dupont, Marie Martin, ...'

-- Regrouper tous les noms dans un tableau
SELECT ARRAY_AGG(nom) FROM utilisateurs;
-- '{ "Jean Dupont", "Marie Martin", ... }'

-- Regrouper les résultats en un JSON
SELECT JSONB_AGG(
    JSONB_BUILD_OBJECT('id', id, 'nom', nom)
)
FROM utilisateurs;
5.3 Transactions (ACID)
Garantie ACID

Postgres est transactionnel par dĂ©faut. Chaque requĂȘte SQL est exĂ©cutĂ©e dans sa propre transaction (auto-commit).

Pour des opérations complexes (ex: virement bancaire), vous devez les envelopper dans une transaction manuelle (BEGIN) pour garantir l'Atomicité (Tout ou Rien).

Scénario : Virement bancaire
-- Client 1 (Alice) a 100€
-- Client 2 (Bob) a 50€

-- Démarrer la transaction
BEGIN;

-- 1. DĂ©biter Alice de 30€
UPDATE comptes SET solde = solde - 30 WHERE client_id = 1;

-- 2. CrĂ©diter Bob de 30€
UPDATE comptes SET solde = solde + 30 WHERE client_id = 2;

-- (Si le serveur crash ici, rien n'est sauvegardé
-- au redémarrage, car le COMMIT n'a pas eu lieu)

-- 3. Valider (Appliquer les 2 changements d'un coup)
COMMIT;
ROLLBACK (Annulation)

Si une erreur survient (ex: le solde d'Alice devient négatif), l'application doit annuler toute la transaction.

BEGIN;

-- 1. DĂ©biter Alice (Solde: 100€) de 500€
UPDATE comptes SET solde = solde - 500 WHERE client_id = 1;
-- (Le solde est maintenant -400€)

-- (L'application (ou un CHECK) détecte l'erreur)

-- 2. Annuler toute la transaction
ROLLBACK;

-- (Le solde d'Alice revient à 100€, comme si
-- rien ne s'était passé.)
SAVEPOINT (Points de sauvegarde)
BEGIN;
INSERT INTO users (nom) VALUES ('A');
SAVEPOINT point_a;
INSERT INTO users (nom) VALUES ('B');
-- Annule juste B, garde A
ROLLBACK TO SAVEPOINT point_a;
COMMIT; -- (Valide A)
6.1 Index & EXPLAIN ANALYZE
EXPLAIN (ANALYZE, BUFFERS)

EXPLAIN (comme MariaDB) montre le plan. EXPLAIN ANALYZE **exĂ©cute** la requĂȘte et montre le plan + le temps rĂ©el et les lignes rĂ©elles. C'est l'outil N°1 d'optimisation Postgres.

EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM utilisateurs WHERE id = 123;
Résultat (Bon : Index Scan)
Index Scan using utilisateurs_pkey on utilisateurs (cost=0.43..8.45 rows=1) (actual time=0.020..0.021 rows=1)
  Index Cond: (id = 123)
  Buffers: shared hit=3
Planning Time: 0.100 ms
Execution Time: 0.030 ms

Index Scan, actual time=0.021ms. Instantané.

EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM utilisateurs WHERE nom = 'Durand';
-- (Sans index sur 'nom')
Résultat (Mauvais : Seq Scan)
Seq Scan on utilisateurs (cost=0.00..4333.00 rows=15) (actual time=0.015..35.400 rows=15)
  Filter: (nom = 'Durand'::text)
  Rows Removed by Filter: 149985
  Buffers: shared hit=1833
Planning Time: 0.080 ms
Execution Time: 35.420 ms

Seq Scan (Full Table Scan), actual time=35.4ms. Lent.

Index B-Tree (Défaut)

L'index standard, utilisé pour 99% des cas. Idéal pour les opérateurs =, >, <, BETWEEN, LIKE 'prefixe%', et ORDER BY.

-- Index simple (pour WHERE nom = ...)
CREATE INDEX idx_utilisateurs_nom ON utilisateurs(nom);

-- Index composite (pour WHERE nom = ... AND prenom = ...)
CREATE INDEX idx_utilisateurs_nom_prenom ON utilisateurs(nom, prenom);

-- Index unique
CREATE UNIQUE INDEX idx_utilisateurs_email_unique ON utilisateurs(LOWER(email));
-- (Index sur l'email en minuscule pour éviter les doublons)
Index Avancés (GIN & GiST)

C'est là que Postgres brille : des index pour les types de données complexes.

GIN (Generalized Inverted Index)

Idéal pour les types "composites" (qui contiennent plusieurs choses). Pensez "recherche inversée".

  • JSONB : (Pour ?, @>).
  • ARRAY : (Pour @>).
  • HSTORE
  • Texte plein (FTS)
-- (Table 'produits' de l'exemple 4.3)

-- Indexer les clés/valeurs du JSONB
CREATE INDEX idx_produits_specs_gin ON produits USING GIN (specs);
-- (AccélÚre: WHERE specs @> '{"switch": "blue"}')

-- Indexer les éléments du tableau
CREATE INDEX idx_produits_tags_gin ON produits USING GIN (tags);
-- (AccélÚre: WHERE tags @> ARRAY['rgb'])
GiST (Generalized Search Tree)

Idéal pour les données "spatiales" ou "complexes" (chevauchement).

  • PostGIS : (GĂ©omĂ©trie, "trouver les points dans ce polygone").
  • Types natifs (point, box).
6.2 Fonctions (pl/pgsql) & Triggers
Fonctions (pl/pgsql)

Postgres permet d'écrire une logique complexe (procédures stockées) directement dans la BDD. Le langage par défaut est pl/pgsql (similaire au PL/SQL d'Oracle).

CREATE FUNCTION get_total_ca(date_debut DATE, date_fin DATE)
RETURNS NUMERIC AS $$
DECLARE
    total_ca NUMERIC := 0;
BEGIN
    SELECT SUM(montant)
    INTO total_ca
    FROM commandes
    WHERE date_commande BETWEEN date_debut AND date_fin;
    
    RETURN total_ca;
END;
$$ LANGUAGE plpgsql;

-- Utilisation:
SELECT get_total_ca('2025-01-01', '2025-01-31');
Triggers

Un Trigger exécute une Fonction automatiquement avant ou aprÚs un événement (INSERT, UPDATE, DELETE).

Ex: Mettre Ă  jour modifie_le Ă  chaque UPDATE.

-- 1. La Fonction (qui retourne un TRIGGER)
CREATE FUNCTION update_modifie_le_col()
RETURNS TRIGGER AS $$
BEGIN
    NEW.modifie_le = NOW(); -- 'NEW' est la ligne en cours d'insertion/maj
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- 2. Le Trigger (lié à la table)
CREATE TRIGGER trg_update_modifie_le
BEFORE UPDATE ON utilisateurs -- 'BEFORE' ou 'AFTER'
FOR EACH ROW -- (Pour chaque ligne modifiée)
EXECUTE FUNCTION update_modifie_le_col();
6.3 Vues & Vues Matérialisées
Vues (VIEW)

Une Vue est une "requĂȘte SELECT stockĂ©e" qui agit comme une table virtuelle. Elle n'existe pas physiquement et est rĂ©-exĂ©cutĂ©e Ă  chaque appel.

Utile pour simplifier des requĂȘtes complexes ou restreindre l'accĂšs aux colonnes.

-- Vue qui joint utilisateurs et commandes
CREATE VIEW vue_commandes_clients AS
    SELECT
        u.email,
        u.nom,
        c.id AS commande_id,
        c.montant
    FROM utilisateurs u
    JOIN commandes c ON u.id = c.utilisateur_id;

-- Utilisation (comme une table)
SELECT * FROM vue_commandes_clients
WHERE montant > 100;
Vues Matérialisées (MATERIALIZED VIEW)

Une Vue MatĂ©rialisĂ©e **exĂ©cute** la requĂȘte et stocke le rĂ©sultat **physiquement** sur le disque (un "snapshot").

Avantage : La lecture est instantanée (c'est une table).
InconvĂ©nient : Les donnĂ©es ne sont pas Ă  jour. Elles doivent ĂȘtre rafraĂźchies manuellement.

IdĂ©al pour les rapports (BI) lourds qui n'ont pas besoin d'ĂȘtre en temps rĂ©el.

-- Créer la vue (coûteux)
CREATE MATERIALIZED VIEW mv_rapport_ca_par_jour AS
    SELECT
        date_commande::DATE AS jour,
        SUM(montant) AS ca_total,
        COUNT(id) AS nb_commandes
    FROM commandes
    GROUP BY jour;
    
-- Lire la vue (instantané)
SELECT * FROM mv_rapport_ca_par_jour;

-- Rafraßchir les données (coûteux)
REFRESH MATERIALIZED VIEW mv_rapport_ca_par_jour;
7.1 Backup (pg_dump)
pg_dump (Backup Logique)

pg_dump est l'outil CLI pour sauvegarder **UNE** base de données. Il se connecte à la BDD (comme psql) et génÚre un fichier de dump. Il ne bloque pas les lectures/écritures (grùce à MVCC).

Option 1: Format SQL (Texte)

GénÚre un gros fichier .sql lisible. Simple, portable, mais lent à restaurer.

pg_dump -U [USER] -h [HOST] -W [NOM_DB] > backup.sql
# -W force la demande de mot de passe
Option 2: Format "Custom" (Binaire)

RecommandĂ©. GĂ©nĂšre un fichier binaire .dump, compressĂ©, qui peut ĂȘtre restaurĂ© en parallĂšle (multi-thread) avec pg_restore.

pg_dump -U [USER] -h [HOST] -W -F c [NOM_DB] > backup.dump
# -F c = Format Custom
pg_dumpall (Roles & Global)

pg_dump ne sauvegarde **PAS** les RĂŽles (utilisateurs) ni les Tablespaces (objets globaux).

pg_dumpall sauvegarde **toutes** les BDD *ET* les objets globaux (roles, etc.) dans un seul fichier SQL.

# Sauvegarde globale (structure + rĂŽles)
pg_dumpall -U postgres --globals-only > roles_et_globaux.sql

# Sauvegarde globale (tout)
pg_dumpall -U postgres > full_cluster_dump.sql
Stratégie de Backup
  1. Faire un pg_dumpall --globals-only (une fois par jour).
  2. Faire un pg_dump -F c sur chaque BDD applicative (toutes les heures).
7.2 Restauration (psql & pg_restore)
Restaurer un Dump SQL (Texte)

On utilise psql pour "exécuter" le fichier SQL.

# 1. Créer la BDD vide
# (Le dump SQL ne contient pas CREATE DATABASE)
createdb -U postgres -O ideo_app ideo_lab_crm_restored

# 2. Importer le dump
psql -U postgres -d ideo_lab_crm_restored < backup.sql
Restaurer un Dump "Custom" (Binaire)

On doit utiliser pg_restore. C'est plus puissant et permet la restauration parallĂšle (-j).

# 1. Créer la BDD vide
createdb -U postgres ideo_lab_crm_restored

# 2. Importer avec pg_restore
# -d (database), -j (jobs/threads)
pg_restore -U postgres -d ideo_lab_crm_restored -j 4 backup.dump
Restauration (pg_dumpall)

Un dump de pg_dumpall (SQL) se restaure simplement avec psql. (Il contient les CREATE DATABASE et CREATE ROLE).

psql -U postgres -f full_cluster_dump.sql
7.3 Réplication (Streaming & Hot Standby)
Réplication "Streaming"

Postgres utilise son systÚme de WAL (cf 1.2) pour la réplication. Le serveur Secondaire ("Slave" ou "Replica") se connecte au Primaire ("Master") et "stream" les fichiers WAL dÚs qu'ils sont écrits.

Le Secondaire "rejoue" ce journal en continu, ce qui en fait une copie (presque) temps réel.

Hot Standby (Read Replica)

Si hot_standby = on, le serveur Secondaire autorise les connexions en lecture seule (SELECT) pendant qu'il réplique. C'est ce qu'on appelle une "Read Replica".

[Applications (Écriture)]
       |
       | INSERT, UPDATE
       ▌
+-----------------------+
| MASTER (Primaire)     |
| (wal_level = replica) |
+-----------------------+
       |
       | Streaming des fichiers WAL (via réseau)
       ▌
+-----------------------+
| SLAVE (Hot Standby)   |
| (read_only = on)      |
+-----------------------+
       |
       | SELECT (Reporting, BI)
       ▌
[Applications (Lecture)]
1. Configuration Primaire (Master)

Fichiers Ă  modifier : postgresql.conf et pg_hba.conf.

postgresql.conf
# /etc/postgresql/16/main/postgresql.conf

# Active le streaming
wal_level = replica

# Garde X segments de WAL pour le slave (ex: 64)
max_wal_senders = 10 # (Combien de slaves)
min_wal_size = 1G
max_wal_size = 2G
pg_hba.conf
# /etc/postgresql/16/main/pg_hba.conf

# Autoriser l'utilisateur 'repl_user' Ă  se connecter
# depuis l'IP du Slave (192.168.1.51)
# 'replication' est un type de BDD spécial
host    replication   repl_user    192.168.1.51/32   scram-sha-256
-- SQL: Créer l'utilisateur de réplication
CREATE ROLE repl_user WITH REPLICATION LOGIN PASSWORD 'pass_repl';

-- Redémarrer Postgres
sudo systemctl restart postgresql
2. Configuration Secondaire (Slave / Replica)

Le Slave doit ĂȘtre une **copie binaire exacte** du Master. On utilise pg_basebackup pour cloner.

# 1. ArrĂȘter Postgres sur le Slave
sudo systemctl stop postgresql

# 2. Vider le datadir
sudo -u postgres rm -rf /var/lib/postgresql/16/main/*

# 3. Cloner le Master (en tant que user 'postgres')
sudo -u postgres pg_basebackup \
    -h [IP_DU_MASTER] \
    -U repl_user \
    -p 5432 \
    -D /var/lib/postgresql/16/main/ \
    -Fp -Xs -R
# -R : Crée le fichier 'standby.signal' et la config de réplication

# 4. Modifier postgresql.conf sur le Slave
# (Assurez-vous que hot_standby est activé)
# hot_standby = on

# 5. Démarrer le Slave
sudo systemctl start postgresql

Le Slave va démarrer, se connecter au Master, et commencer à "streamer" les WALs. Vous pouvez le vérifier avec \l sur le Master (il sera en lecture seule).

8.1 Cheat-sheet psql & SQL
psql (Dans le client interactif)
-- CONNEXION / SORTIE
\q                      -- Quitter psql
\l                      -- Lister les BDD
\c [nom_db] [user]      -- Se connecter Ă  une autre BDD/user

-- INSPECTION (OBJETS)
\dt [schema.*]          -- Lister les tables (du schema)
\ds                     -- Lister les sequences
\dv                     -- Lister les vues
\df                     -- Lister les fonctions
\dn                     -- Lister les schemas
\d [nom_objet]          -- Décrire un objet (table, vue, index...)
\d+ [nom_table]         -- Décrire (plus de détails)

-- INSPECTION (RÔLES)
\du                     -- Lister les rĂŽles (utilisateurs)
\dp [nom_table]         -- Lister les permissions de la table (ACL)

-- AIDE & CONFIG
\?                      -- Aide psql
\h [COMMANDE_SQL]       -- Aide SQL (ex: \h CREATE TABLE)
\timing                 -- Activer le timer
\conninfo               -- Infos connexion (host, port, user)
CLI (Commandes Shell Bash)
# Connexion
psql -h [host] -U [user] -W -d [db]

# Connexion locale (compte 'postgres')
sudo -u postgres psql

# Créer/Supprimer une BDD
sudo -u postgres createdb [nom_db]
sudo -u postgres dropdb [nom_db]

# Exécuter une commande rapide
psql -U [user] -d [db] -c "SELECT COUNT(*) FROM users"

# Exécuter un fichier SQL
psql -U [user] -d [db] -f script.sql

# Backup (Custom format)
pg_dump -U [user] -W -F c [db] > backup.dump

# Backup (Global / Roles)
pg_dumpall -U postgres --globals-only > roles.sql

# Restore (Custom format)
pg_restore -U [user] -W -d [db_vide] backup.dump

# Restore (SQL format)
psql -U [user] -W -d [db_vide] < backup.sql
SQL (Gestion & RĂŽles)
-- GESTION DES RÔLES
CREATE ROLE nom_role LOGIN PASSWORD 'pass';
ALTER ROLE nom_role WITH SUPERUSER;
DROP ROLE nom_role;
ALTER ROLE nom_role RENAME TO nouveau_nom;

-- GESTION DES PRIVILÈGES
GRANT CONNECT ON DATABASE nom_db TO nom_role;
GRANT USAGE ON SCHEMA nom_schema TO nom_role;
GRANT SELECT, INSERT, UPDATE, DELETE ON table TO nom_role;
GRANT ALL PRIVILEGES ON DATABASE nom_db TO nom_role;

REVOKE SELECT ON table FROM nom_role;

-- GESTION DES SCHEMAS
CREATE SCHEMA nom_schema;
DROP SCHEMA nom_schema;
ALTER SCHEMA nom_schema RENAME TO nouveau_nom;
SHOW search_path;
SET search_path TO nom_schema, public;

-- GESTION DES INDEX
CREATE INDEX idx_nom ON table (colonne);
CREATE INDEX idx_jsonb ON table USING GIN (jsonb_col);
DROP INDEX idx_nom;

-- ANALYSE
EXPLAIN (ANALYZE, BUFFERS) SELECT ...;