
Vous avez installé PostgreSQL, le service tourne, pg_isready répond accepting connections. Maintenant, il faut entrer dedans. Comment lister les bases existantes ? Voir la structure d’une table ? Exécuter une requête, exporter un résultat en CSV, ou lancer un script SQL automatisé ?
psql est le client en ligne de commande officiel de PostgreSQL. C’est un outil beaucoup plus puissant qu’un simple prompt SQL : il intègre des méta-commandes pour explorer la structure, du formatage avancé pour lire les résultats, de l’import/export CSV, et un mode scripting complet avec variables et fichiers. Ce guide couvre tout ce qu’un administrateur ou un développeur utilise au quotidien.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Se connecter à PostgreSQL en local (socket) et en réseau (TCP, URI,
.pgpass) - Explorer la structure d’un cluster avec les méta-commandes :
\l,\dn,\dt,\d+,\du - Créer et manipuler des objets : bases, schémas, tables, avec transactions
- Formater la sortie : mode étendu, chronométrage, export vers fichier
- Importer et exporter des données en CSV avec
\copy - Scripter des tâches avec
psql -f,-v,\setet\gset
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »psql est l’outil que vous utiliserez dans toutes ces situations :
- Vous venez d’installer PostgreSQL et voulez vérifier ce qui existe sur le cluster
- Vous devez créer une base, un schéma et des tables pour une nouvelle application
- Vous administrez un serveur et avez besoin de connaître la taille des bases, les connexions actives ou les verrous
- Vous devez importer un fichier CSV ou exporter des résultats pour un collègue
- Vous automatisez des tâches d’administration dans un script shell (migration, rapport, nettoyage)
Ce guide ne couvre pas…
Section intitulée « Ce guide ne couvre pas… »Prérequis
Section intitulée « Prérequis »- PostgreSQL installé et le service actif (
pg_isreadyretourneaccepting connections) - Accès au rôle
postgresou à un rôle avecLOGIN(voir le guide Installation)
Vérification rapide :
psql --versionpsql (PostgreSQL) 18.3 (Debian 18.3-1.pgdg12+1)Se connecter à PostgreSQL
Section intitulée « Se connecter à PostgreSQL »Connexion locale (socket Unix)
Section intitulée « Connexion locale (socket Unix) »La méthode la plus courante sur le serveur lui-même. Pas besoin de mot de passe — l’authentification peer vérifie que l’utilisateur système correspond au rôle PostgreSQL :
sudo -u postgres psqlVous obtenez le prompt postgres=#. Le # indique un superuser ; un utilisateur normal verrait =>.
Pour se connecter directement à une base spécifique :
sudo -u postgres psql -d lab_adminConnexion réseau (host, port, user, dbname)
Section intitulée « Connexion réseau (host, port, user, dbname) »Pour une connexion TCP (depuis un autre serveur, ou en local avec mot de passe) :
psql -h 127.0.0.1 -p 5432 -U admin_lab -d lab_adminPostgreSQL demandera le mot de passe (méthode scram-sha-256 via pg_hba.conf).
URI de connexion
Section intitulée « URI de connexion »psql accepte aussi une URI complète — pratique pour les scripts et les variables d’environnement :
psql "postgresql://admin_lab@127.0.0.1:5432/lab_admin"Format : postgresql://[user[:password]@][host][:port][/dbname][?param=value]
Variables d’environnement et .pgpass
Section intitulée « Variables d’environnement et .pgpass »Plutôt que de répéter les options de connexion, configurez des variables d’environnement :
export PGHOST=127.0.0.1export PGPORT=5432export PGUSER=admin_labexport PGDATABASE=lab_adminpsql # se connecte automatiquement avec ces paramètresPour éviter de taper le mot de passe, créez un fichier .pgpass :
# Format : hostname:port:database:username:passwordecho "127.0.0.1:5432:lab_admin:admin_lab:<change-me>" > ~/.pgpasschmod 600 ~/.pgpass\conninfo : vérifier sa connexion
Section intitulée « \conninfo : vérifier sa connexion »Une fois connecté, vérifiez toujours où vous êtes :
\conninfo Parameter | Value----------------------+--------------------- Database | lab_admin Client User | postgres Socket Directory | /var/run/postgresql Server Port | 5432 Protocol Version | 3.0 Password Used | false SSL Connection | false Superuser | onLes informations clés : Database (la base active), Password Used (false = socket peer), SSL Connection (false = pas de chiffrement réseau).
Les méta-commandes essentielles
Section intitulée « Les méta-commandes essentielles »Les méta-commandes commencent par \ et sont interprétées par psql lui-même, pas par le serveur SQL. Elles permettent d’explorer la structure sans écrire de requêtes sur les catalogues système.
Explorer : \l, \dn, \dt, \d+, \di, \du
Section intitulée « Explorer : \l, \dn, \dt, \d+, \di, \du »Lister les bases de données :
\l Name | Owner | Encoding | Locale Provider | Collate | Ctype | Access privileges-----------+-----------+----------+-----------------+---------+---------+----------------------- lab_admin | admin_lab | UTF8 | libc | C.UTF-8 | C.UTF-8 | postgres | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | template0 | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | =c/postgres + | | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | =c/postgres + | | | | | | postgres=CTc/postgresLister les schémas (avec détails) :
\dn+ Name | Owner | Access privileges | Description--------+-------------------+----------------------------------------+------------------------ app | postgres | | public | pg_database_owner | pg_database_owner=UC/pg_database_owner+| standard public schema | | =U/pg_database_owner |Lister les tables d’un schéma :
\dt app.* Schema | Name | Type | Owner--------+-----------+-------+---------- app | clients | table | postgres app | commandes | table | postgresVoir la structure d’une table :
\d app.clients Table "app.clients" Column | Type | Collation | Nullable | Default-------------+---------+-----------+----------+----------------------------------------- id | integer | | not null | nextval('app.clients_id_seq'::regclass) nom | text | | not null | email | text | | not null | ville | text | | | inscription | date | | | CURRENT_DATEIndexes: "clients_pkey" PRIMARY KEY, btree (id) "clients_email_key" UNIQUE CONSTRAINT, btree (email)Referenced by: TABLE "app.commandes" CONSTRAINT "commandes_client_id_fkey" FOREIGN KEY (client_id) REFERENCES app.clients(id)Ajoutez + pour plus de détails (stockage, compression, contraintes NOT NULL explicites) :
\d+ app.clientsLister les index :
\di app.* Schema | Name | Type | Owner | Table--------+-------------------+-------+----------+----------- app | clients_email_key | index | postgres | clients app | clients_pkey | index | postgres | clients app | commandes_pkey | index | postgres | commandesLister les rôles :
\du Role name | Attributes-----------+------------------------------------------------------------ admin_lab | Create DB postgres | Superuser, Create role, Create DB, Replication, Bypass RLSNaviguer : \c, SET search_path
Section intitulée « Naviguer : \c, SET search_path »Pour changer de base de données sans quitter psql :
\c lab_adminPour accéder aux tables du schéma app sans préfixer :
SET search_path TO app, public;SELECT * FROM clients LIMIT 2; id | nom | email | ville | inscription----+--------------+-------------------+-------+------------- 1 | Alice Martin | alice@example.com | Paris | 2026-01-15 2 | Bob Dupont | bob@example.com | Lyon | 2026-02-20Aide : \h (SQL), \? (méta-commandes)
Section intitulée « Aide : \h (SQL), \? (méta-commandes) »Deux systèmes d’aide intégrés :
\h CREATE TABLE— affiche la syntaxe SQL complète d’une commande\?— liste toutes les méta-commandes disponibles dans psql
\h CREATE TABLECommand: CREATE TABLEDescription: define a new tableSyntax:CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name ( [ { column_name data_type [...] | table_constraint | LIKE source_table [...] } [, ... ]] )...URL: https://www.postgresql.org/docs/18/sql-createtable.htmlL’URL en fin de sortie pointe directement vers la documentation officielle de la version installée.
Tableau récapitulatif des méta-commandes courantes
Section intitulée « Tableau récapitulatif des méta-commandes courantes »| Commande | Description |
|---|---|
\l | Lister les bases de données |
\c <base> | Se connecter à une autre base |
\dn+ | Lister les schémas (avec permissions) |
\dt [schema.*] | Lister les tables |
\d <table> | Structure d’une table (colonnes, types, index) |
\d+ <table> | Structure détaillée (stockage, compression, contraintes) |
\di [schema.*] | Lister les index |
\du | Lister les rôles |
\conninfo | Informations de connexion courante |
\x | Basculer en mode étendu (une ligne = un enregistrement vertical) |
\timing | Activer/désactiver le chronométrage des requêtes |
\o <fichier> | Rediriger la sortie vers un fichier |
\i <fichier> | Exécuter un script SQL |
\copy | Import/export CSV (côté client) |
\! <cmd> | Exécuter une commande shell |
\h <cmd> | Aide sur une commande SQL |
\? | Liste de toutes les méta-commandes |
\q | Quitter psql |
Créer et gérer des objets
Section intitulée « Créer et gérer des objets »Créer une base et un schéma
Section intitulée « Créer une base et un schéma »CREATE DATABASE mon_projet;\c mon_projet
CREATE SCHEMA backend;Toute table créée dans le schéma backend sera isolée du schéma public :
CREATE TABLE backend.utilisateurs ( id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, login TEXT UNIQUE NOT NULL, created_at TIMESTAMP DEFAULT NOW());Créer une table, insérer, interroger
Section intitulée « Créer une table, insérer, interroger »Voici une table réelle créée sur notre lab :
CREATE TABLE app.clients ( id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, nom TEXT NOT NULL, email TEXT UNIQUE NOT NULL, ville TEXT, inscription DATE DEFAULT CURRENT_DATE);
INSERT INTO app.clients (nom, email, ville, inscription) VALUES ('Alice Martin', 'alice@example.com', 'Paris', '2026-01-15'), ('Bob Dupont', 'bob@example.com', 'Lyon', '2026-02-20'), ('Claire Durand', 'claire@example.com', 'Marseille', '2026-03-10'), ('David Moreau', 'david@example.com', 'Toulouse', '2026-03-28'), ('Eva Bernard', 'eva@example.com', 'Bordeaux', '2026-04-01');SELECT * FROM app.clients; id | nom | email | ville | inscription----+---------------+--------------------+-----------+------------- 1 | Alice Martin | alice@example.com | Paris | 2026-01-15 2 | Bob Dupont | bob@example.com | Lyon | 2026-02-20 3 | Claire Durand | claire@example.com | Marseille | 2026-03-10 4 | David Moreau | david@example.com | Toulouse | 2026-03-28 5 | Eva Bernard | eva@example.com | Bordeaux | 2026-04-01Transactions : BEGIN, COMMIT, ROLLBACK
Section intitulée « Transactions : BEGIN, COMMIT, ROLLBACK »Toute modification peut être testée dans une transaction avant d’être validée :
BEGIN;INSERT INTO app.clients (nom, email, ville) VALUES ('Test Rollback', 'test@example.com', 'Nantes');SELECT count(*) FROM app.clients; count------- 6L’insertion est visible dans la transaction. Mais si vous annulez :
ROLLBACK;SELECT count(*) FROM app.clients; count------- 5La ligne a disparu. C’est le mécanisme fondamental des transactions : rien n’est définitif tant que vous n’avez pas exécuté COMMIT.
Supprimer proprement
Section intitulée « Supprimer proprement »DROP supprime un objet. Si d’autres objets en dépendent, PostgreSQL refuse par défaut :
DROP TABLE app.clients;-- ERROR: cannot drop table app.clients because other objects depend on itPour forcer la suppression avec les dépendances :
DROP TABLE app.clients CASCADE;-- supprime aussi la contrainte FK dans app.commandesFormater et filtrer la sortie
Section intitulée « Formater et filtrer la sortie »\x : mode étendu (affichage vertical)
Section intitulée « \x : mode étendu (affichage vertical) »Pour les tables larges (beaucoup de colonnes), le mode étendu affiche chaque ligne comme un bloc clé/valeur :
\xSELECT * FROM app.clients WHERE id = 1;-[ RECORD 1 ]------------------id | 1nom | Alice Martinemail | alice@example.comville | Parisinscription | 2026-01-15Tapez \x à nouveau pour revenir au mode tableau. Vous pouvez aussi utiliser \x auto pour laisser psql choisir le mode le plus lisible selon la largeur du résultat.
\timing : chronométrer les requêtes
Section intitulée « \timing : chronométrer les requêtes »\timingSELECT count(*) FROM app.clients; count------- 5(1 row)
Time: 0.782 msIndispensable pour repérer les requêtes lentes. \timing à nouveau pour désactiver.
\o : rediriger la sortie vers un fichier
Section intitulée « \o : rediriger la sortie vers un fichier »Pour capturer le résultat d’une requête dans un fichier :
\o /tmp/clients_par_ville.txtSELECT nom, ville FROM app.clients ORDER BY ville;\oLe fichier /tmp/clients_par_ville.txt contient :
nom | ville---------------+----------- Eva Bernard | Bordeaux Bob Dupont | Lyon Claire Durand | Marseille Alice Martin | Paris David Moreau | Toulouse\o sans argument rétablit la sortie sur le terminal.
Requêtes utiles d’administration
Section intitulée « Requêtes utiles d’administration »Taille des bases :
SELECT datname, pg_size_pretty(pg_database_size(datname)) AS tailleFROM pg_database WHERE datistemplate = falseORDER BY pg_database_size(datname) DESC; datname | taille-----------+--------- lab_admin | 7894 kB postgres | 7654 kBConnexions actives :
SELECT pid, usename, datname, state, LEFT(query, 60) AS queryFROM pg_stat_activity WHERE backend_type = 'client backend'; pid | usename | datname | state | query------+----------+-----------+--------+-------------------------------------------------------------- 5504 | postgres | lab_admin | active | SELECT pid, usename, datname, state, LEFT(query ...Taille des tables :
SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS tailleFROM pg_tables WHERE schemaname = 'app'ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC; schemaname | tablename | taille------------+-----------+-------- app | clients | 48 kB app | commandes | 32 kBVerrous actifs :
SELECT pid, mode, granted, relation::regclassFROM pg_locks WHERE relation IS NOT NULL;Importer et exporter des données
Section intitulée « Importer et exporter des données »\copy : import/export CSV côté client
Section intitulée « \copy : import/export CSV côté client »\copy est une méta-commande psql qui transfère les données entre le client et le serveur. Contrairement à la commande SQL COPY, elle ne nécessite pas que le fichier soit accessible depuis le serveur.
Exporter en CSV :
\copy app.clients TO '/tmp/clients.csv' WITH (FORMAT csv, HEADER)COPY 5Le fichier contient :
id,nom,email,ville,inscription1,Alice Martin,alice@example.com,Paris,2026-01-152,Bob Dupont,bob@example.com,Lyon,2026-02-203,Claire Durand,claire@example.com,Marseille,2026-03-10...Importer depuis un CSV :
\copy app.clients (nom, email, ville) FROM '/tmp/nouveaux_clients.csv' WITH (FORMAT csv, HEADER)Exécuter un script SQL : \i et psql -f
Section intitulée « Exécuter un script SQL : \i et psql -f »Depuis psql avec \i :
\i /tmp/rapport.sqlDepuis le shell avec psql -f :
sudo -u postgres psql -d lab_admin -f /tmp/rapport.sqlVoici un exemple de script SQL (rapport.sql) :
\echo === Rapport clients ===SELECT ville, count(*) AS nb_clientsFROM app.clients GROUP BY ville ORDER BY nb_clients DESC;
\echo === Dernières commandes ===SELECT c.nom, o.montant, o.statutFROM app.commandes o JOIN app.clients c ON o.client_id = c.idORDER BY o.created_at DESC LIMIT 3;Sortie :
=== Rapport clients === ville | nb_clients-----------+------------ Lyon | 1 Marseille | 1 Toulouse | 1 Paris | 1 Bordeaux | 1
=== Dernières commandes === nom | montant | statut--------------+---------+---------- Alice Martin | 149.99 | livree Alice Martin | 89.50 | en_cours Bob Dupont | 250.00 | livreeScripter avec psql
Section intitulée « Scripter avec psql »One-liner : psql -c
Section intitulée « One-liner : psql -c »Pour exécuter une seule requête depuis le shell (dans un script Bash, un cron, etc.) :
sudo -u postgres psql -d lab_admin -c "SELECT nom, ville FROM app.clients WHERE ville = 'Paris';" nom | ville--------------+------- Alice Martin | ParisAjoutez -t (tuples only) et -A (unaligned) pour un résultat exploitable par un script :
sudo -u postgres psql -d lab_admin -tA -c "SELECT count(*) FROM app.clients;"# Sortie : 5Variables avec -v et \set
Section intitulée « Variables avec -v et \set »psql supporte des variables substituées dans les requêtes. Elles se définissent avec -v au lancement ou \set en interactif.
Avec -v (les variables sont substituées dans les scripts, pas dans -c) :
sudo -u postgres psql -d lab_admin -v ville="'Lyon'" <<'SQL'SELECT * FROM app.clients WHERE ville = :ville;SQL id | nom | email | ville | inscription----+------------+-----------------+-------+------------- 2 | Bob Dupont | bob@example.com | Lyon | 2026-02-20En interactif avec \set :
\set ville 'Marseille'SELECT * FROM app.clients WHERE ville = :'ville';La syntaxe :'ville' entoure la valeur de guillemets simples — indispensable pour les chaînes de caractères.
\gset : capturer le résultat dans une variable
Section intitulée « \gset : capturer le résultat dans une variable »SELECT count(*) AS total FROM app.clients \gset\echo Il y a :total clientsIl y a 5 clients\gset exécute la requête et stocke chaque colonne du résultat comme variable psql. C’est très utile dans les scripts pour enchaîner des requêtes conditionnelles.
\! : exécuter une commande shell depuis psql
Section intitulée « \! : exécuter une commande shell depuis psql »Pas besoin de quitter psql pour lancer une commande système :
\! hostnamepg-lab\! dateMon Apr 13 09:50:55 UTC 2026Dépannage
Section intitulée « Dépannage »FATAL: role "bob" does not exist
Section intitulée « FATAL: role "bob" does not exist »Vous essayez de vous connecter avec un rôle qui n’existe pas. Vérifiez :
\duCréez le rôle si nécessaire :
CREATE ROLE bob LOGIN PASSWORD '<change-me>';FATAL: Peer authentication failed
Section intitulée « FATAL: Peer authentication failed »Vous tentez une connexion socket avec un utilisateur système qui ne correspond à aucun rôle PostgreSQL. Solutions :
- Utiliser
sudo -u postgres psql(connexion en tant que rôlepostgres) - Passer en TCP :
psql -h 127.0.0.1 -U admin_lab(utilisescram-sha-256au lieu depeer)
psql: error: connection to server on socket ... failed: No such file or directory
Section intitulée « psql: error: connection to server on socket ... failed: No such file or directory »Le socket n’existe pas — le service PostgreSQL n’est probablement pas démarré :
pg_isreadysudo systemctl status postgresql@18-mainLa sortie est tronquée ou illisible
Section intitulée « La sortie est tronquée ou illisible »- Colonnes trop larges →
\x autopour basculer en mode étendu automatique - Résultat paginé →
\pset pager offpour désactiver le pager - Résultat sans bordures →
\pset format unalignedpuis\pset fieldsep ','pour un pseudo-CSV
À retenir
Section intitulée « À retenir »psqlest bien plus qu’un prompt SQL — les méta-commandes (\l,\dt,\d+,\du) évitent d’écrire des requêtes catalogue.- Vérifiez toujours votre connexion avec
\conninfoavant de travailler. - Utilisez
\x autopour les tables larges et\timingpour repérer les requêtes lentes. \copy(côté client) est plus pratique et plus sûr queCOPY(côté serveur) pour l’import/export CSV.- Les variables psql (
-v,\set,\gset) fonctionnent dans les scripts et heredocs, mais pas avec-c. Chaque SQL passé à-cest une transaction unique. BEGIN/ROLLBACKavant la plupart des DDL risqués — saufCREATE/DROP DATABASEetCREATE TABLESPACEqui refusent un bloc transactionnel.- Les requêtes d’administration (
pg_stat_activity,pg_database_size,pg_locks) sont vos outils de diagnostic quotidiens.