Fiche de référence PostgreSQL : Un guide rapide pour les développeurs

Référence rapide à PostgreSQL

Sommaire

Un guide de référence rapide pour les tâches quotidiennes
PostgreSQL
de travail : connexions, syntaxe SQL, commandes métas psql, performance, JSON, fonctions fenêtres, et bien plus.

Un cheatsheet SQL concis couvrant les commandes et opérations les plus essentielles est également disponible.

logo postgresql

Se connecter & Principes de base

# Se connecter
psql -h Hôte -p 5432 -U Utilisateur -d BaseDeDonnées
psql $DATABASE_URL

# À l'intérieur de psql
\conninfo             -- afficher la connexion
\l[+]                 -- lister les bases de données
\c BaseDeDonnées      -- se connecter à la base de données
\dt[+] [schéma.]pat   -- lister les tables
\dv[+]                -- lister les vues
\ds[+]                -- lister les séquences
\df[+] [pat]          -- lister les fonctions
\d[S+] nom            -- décrire une table/vue/séquence
\dn[+]                -- lister les schémas
\du[+]                -- lister les rôles
\timing               -- activer/désactiver le chronométrage des requêtes
\x                    -- affichage étendu
\e                    -- éditer le buffer dans $EDITOR
\i fichier.sql        -- exécuter un fichier
\copy ...             -- COPY côté client
\! commande_shell     -- exécuter une commande shell

Types de données (communs)

  • Numériques : smallint, integer, bigint, decimal(p,s), numeric, real, double precision, serial, bigserial
  • Texte : text, varchar(n), char(n)
  • Booléen : boolean
  • Temporels : timestamp [avec/sans fuseau horaire], date, time, interval
  • UUID : uuid
  • JSON : json, jsonb (préféré)
  • Tableaux : type[] par exemple text[]
  • Réseau : inet, cidr, macaddr
  • Géométriques : point, ligne, polygone, etc.

Vérifier la version de PostgreSQL

SELECT version();

Version du serveur PostgreSQL :

pg_config --version

Version du client PostgreSQL :

psql --version

DDL (Créer / Modifier)

-- Créer un schéma & une table
CREATE SCHEMA IF NOT EXISTS app;
CREATE TABLE app.users (
  id           bigserial PRIMARY KEY,
  email        text NOT NULL UNIQUE,
  name         text,
  active       boolean NOT NULL DEFAULT true,
  created_at   timestamptz NOT NULL DEFAULT now(),
  profile      jsonb,
  tags         text[]
);

-- Modifier
ALTER TABLE app.users ADD COLUMN last_login timestamptz;
ALTER TABLE app.users ALTER COLUMN name SET NOT NULL;
ALTER TABLE app.users DROP COLUMN tags;

-- Contraintes
ALTER TABLE app.users ADD CONSTRAINT email_lower_uk UNIQUE (lower(email));

-- Indexes
CREATE INDEX ON app.users (email);
CREATE UNIQUE INDEX CONCURRENTLY users_email_uidx ON app.users (lower(email));
CREATE INDEX users_profile_gin ON app.users USING gin (profile);
CREATE INDEX users_created_at_idx ON app.users (created_at DESC);

DML (Insérer / Mettre à jour / Upsert / Supprimer)

INSERT INTO app.users (email, name) VALUES
  ('a@x.com','A'), ('b@x.com','B')
RETURNING id;

-- Upsert (ON CONFLICT)
INSERT INTO app.users (email, name)
VALUES ('a@x.com','Alice')
ON CONFLICT (email)
DO UPDATE SET name = EXCLUDED.name, updated_at = now();

UPDATE app.users SET active = false WHERE last_login < now() - interval '1 year';
DELETE FROM app.users WHERE active = false AND last_login IS NULL;

Essentiels de requêtes

SELECT * FROM app.users ORDER BY created_at DESC LIMIT 20 OFFSET 40; -- pagination

-- Filtrage
SELECT * FROM app.users WHERE email ILIKE '%@example.%' AND active;

-- Agrégats & GROUP BY
SELECT active, count(*) AS n
FROM app.users
GROUP BY active
HAVING count(*) > 10;

-- Joins
SELECT o.id, u.email, o.total
FROM app.orders o
JOIN app.users u ON u.id = o.user_id
LEFT JOIN app.discounts d ON d.id = o.discount_id;

-- DISTINCT ON (spécifique à Postgres)
SELECT DISTINCT ON (user_id) user_id, status, created_at
FROM app.events
ORDER BY user_id, created_at DESC;

-- CTEs
WITH recent AS (
  SELECT * FROM app.orders WHERE created_at > now() - interval '30 days'
)
SELECT count(*) FROM recent;

-- CTE récursive
WITH RECURSIVE t(n) AS (
  SELECT 1
  UNION ALL
  SELECT n+1 FROM t WHERE n < 10
)
SELECT sum(n) FROM t;

Fonctions fenêtres

SELECT
  user_id,
  created_at,
  sum(total)      OVER (PARTITION BY user_id ORDER BY created_at) AS running_total,
  row_number()    OVER (PARTITION BY user_id ORDER BY created_at) AS rn,
  lag(total, 1)   OVER (PARTITION BY user_id ORDER BY created_at) AS prev_total
FROM app.orders;

JSON / JSONB

-- Extraction
SELECT profile->>'company' AS company FROM app.users;
SELECT profile->'address'->>'city' FROM app.users;

-- Index pour les requêtes jsonb
CREATE INDEX users_profile_company_gin ON app.users USING gin ((profile->>'company'));

-- Existence / contenance
SELECT * FROM app.users WHERE profile ? 'company';              -- clé existe
SELECT * FROM app.users WHERE profile @> '{"role":"admin"}'; -- contient

-- Mise à jour jsonb
UPDATE app.users
SET profile = jsonb_set(COALESCE(profile,'{}'::jsonb), '{prefs,theme}', '"dark"', true);

Tableaux

-- appartenance & contenance
SELECT * FROM app.users WHERE 'vip' = ANY(tags);
SELECT * FROM app.users WHERE tags @> ARRAY['beta'];

-- ajouter
UPDATE app.users SET tags = array_distinct(tags || ARRAY['vip']);

Temps & Dates

SELECT now() AT TIME ZONE 'Australia/Melbourne';
SELECT date_trunc('day', created_at) AS d, count(*)
FROM app.users GROUP BY d ORDER BY d;

-- Intervalle
SELECT now() - interval '7 days';

Transactions & Verrous

BEGIN;
UPDATE app.accounts SET balance = balance - 100 WHERE id = 1;
UPDATE app.accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- ou ROLLBACK

-- Niveau d'isolation
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- Vérifier les verrous
SELECT * FROM pg_locks l JOIN pg_stat_activity a USING (pid);

Rôles & Permissions

-- Créer un rôle/utilisateur
CREATE ROLE app_user LOGIN PASSWORD 'secret';
GRANT USAGE ON SCHEMA app TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA app GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;

Import / Export

-- Serveur (nécessite un superutilisateur ou des autorisations appropriées)
COPY app.users TO '/tmp/users.csv' CSV HEADER;
COPY app.users(email,name) FROM '/tmp/users.csv' CSV HEADER;

-- Client (psql)
\copy app.users TO 'users.csv' CSV HEADER
\copy app.users(email,name) FROM 'users.csv' CSV HEADER

Performance & Observabilité

EXPLAIN (ANALYZE, BUFFERS, VERBOSE) SELECT ...;  -- temps d'exécution réel

-- Vues de statistiques
SELECT * FROM pg_stat_user_tables;
SELECT * FROM pg_stat_statements ORDER BY total_time DESC LIMIT 20; -- nécessite une extension

-- Maintenance
VACUUM [FULL] [VERBOSE] table_name;
ANALYZE table_name;
REINDEX TABLE table_name;

Activer les extensions nécessaires :

CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS btree_gin;
CREATE EXTENSION IF NOT EXISTS pg_trgm; -- recherche trigramme

Recherche plein texte (rapide)

ALTER TABLE app.docs ADD COLUMN tsv tsvector;
UPDATE app.docs SET tsv = to_tsvector('english', coalesce(title,'') || ' ' || coalesce(body,''));
CREATE INDEX docs_tsv_idx ON app.docs USING gin(tsv);
SELECT id FROM app.docs WHERE tsv @@ plainto_tsquery('english', 'quick brown fox');

Paramètres utiles de psql

\pset pager off       -- désactiver le pager
\pset null '∅'
\pset format aligned  -- autres : unaligned, csv
\set ON_ERROR_STOP on
\timing on

Requêtes utiles du catalogue

-- Taille de la table
SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) AS size
FROM pg_catalog.pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC;

-- Bloat d'index (approximatif)
SELECT schemaname, relname, indexrelname, idx_scan
FROM pg_stat_user_indexes ORDER BY idx_scan ASC NULLS FIRST LIMIT 20;

Sauvegarde / Restauration

# Sauvegarde logique
pg_dump -h Hôte -U Utilisateur -d BaseDeDonnées -F c -f db.dump        # format personnalisé
pg_restore -h Hôte -U Utilisateur -d NouvelleBaseDeDonnées -j 4 db.dump

# SQL simple
pg_dump -h Hôte -U Utilisateur -d BaseDeDonnées > dump.sql
psql -h Hôte -U Utilisateur -d BaseDeDonnées -f dump.sql

Réplication (niveau haut)

  • Les fichiers WAL sont transmis depuis principalsecondaire
  • Paramètres clés : wal_level, max_wal_senders, hot_standby, primary_conninfo
  • Outils : pg_basebackup, standby.signal (PG ≥12)

Pièges & Conseils

  • Utilisez toujours jsonb, et non json, pour l’indexation et les opérateurs.
  • Privilégiez timestamptz (timezone-aware).
  • DISTINCT ON est une fonction précieuse de Postgres pour “top-N par groupe”.
  • Utilisez GENERATED ALWAYS AS IDENTITY au lieu de serial.
  • Évitez SELECT * dans les requêtes en production.
  • Créez des index pour les filtres sélectifs et les clés de jointure.
  • Mesurez avec EXPLAIN (ANALYZE) avant d’optimiser.

Petits avantages spécifiques aux versions (≥v12+)

-- Identité générée
CREATE TABLE t (id bigINT GENERATED ALWAYS AS IDENTITY, ...);

-- Upsert avec index partiel
CREATE UNIQUE INDEX ON t (clé) WHERE is_active;

Liens utiles