Un serveur de production génère des alertes critiques depuis 3 heures. Vous voulez savoir lesquelles, sur quel serveur, depuis quand. L’information est dans la base de données — il suffit d’un SELECT pour l’extraire.
SQL (Structured Query Language) est un langage déclaratif : vous décrivez ce que vous voulez obtenir, pas comment le calculer. Le moteur de la base de données se charge de trouver le chemin optimal pour y arriver. Cette approche le rend beaucoup plus accessible qu’un langage de programmation classique.
Ce guide vous apprend à écrire vos premières requêtes de lecture : sélectionner des colonnes, filtrer avec des conditions, trier les résultats et limiter le nombre de lignes renvoyées. Vous pratiquerez sur une base SQLite fil rouge qui simule la gestion d’une plateforme d’infrastructure — la même base sera utilisée tout au long des 5 guides de cette section.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Préparer votre environnement SQLite avec la base fil rouge
- Sélectionner des colonnes spécifiques d’une table avec
SELECT - Filtrer les résultats avec
WHEREet les opérateurs de comparaison - Combiner des conditions avec
AND,OR,NOT,IN,BETWEENetLIKE - Trier les résultats avec
ORDER BY(ascendant et descendant) - Limiter le nombre de lignes renvoyées avec
LIMITetOFFSET - Éliminer les doublons avec
DISTINCTet renommer les colonnes avecAS
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »SQL est le langage que vous utilisez chaque fois que vous interagissez avec une base relationnelle. En administration système et DevOps, les situations sont fréquentes :
- Vérifier qu’un utilisateur applicatif existe dans la table des comptes après un déploiement
- Compter le nombre de connexions actives dans
pg_stat_activity(PostgreSQL) ouinformation_schema.processlist(MySQL) - Chercher les alertes critiques non acquittées
- Extraire la liste des serveurs d’un environnement donné
- Valider qu’une migration de données s’est bien déroulée en comparant des comptages
Ce guide ne couvre pas…
Section intitulée « Ce guide ne couvre pas… »- La modification des données (
INSERT,UPDATE,DELETE) → voir le guide Manipuler les données - Les jointures entre tables → voir le guide Jointures et sous-requêtes
- La création de tables (
CREATE TABLE) → voir le guide SQL avancé
Prérequis
Section intitulée « Prérequis »- SQLite installé (disponible par défaut sur la plupart des distributions Linux :
sqlite3 --version) - Un terminal
Préparez votre environnement de travail
Section intitulée « Préparez votre environnement de travail »Tous les guides SQL de cette section utilisent la même base fil rouge : une plateforme de gestion d’infrastructure avec 6 tables.
-
Créez votre répertoire de travail.
Fenêtre de terminal mkdir -p ~/Projets/cours-sql && cd ~/Projets/cours-sql -
Créez le fichier
init-db.sqlavec le contenu suivant (copiez-collez l’intégralité) :-- init-db.sql — Base fil rouge pour les guides SQL-- Plateforme d'infrastructure DevSecOps---- Utilisation :-- cd ~/Projets/cours-sql-- sqlite3 infra.db < init-db.sqlPRAGMA foreign_keys = ON;-- ── Serveurs gérés par l'équipe ──────────────────────CREATE TABLE IF NOT EXISTS servers (id INTEGER PRIMARY KEY AUTOINCREMENT,hostname TEXT NOT NULL UNIQUE,ip_address TEXT NOT NULL,os TEXT NOT NULL,os_version TEXT,environment TEXT NOT NULL CHECK (environment IN ('production', 'staging', 'development')),datacenter TEXT NOT NULL,cpu_cores INTEGER NOT NULL,ram_gb INTEGER NOT NULL,disk_gb INTEGER NOT NULL,active INTEGER NOT NULL DEFAULT 1,created_at TEXT NOT NULL DEFAULT (datetime('now')),notes TEXT);-- ── Membres de l'équipe ─────────────────────────────CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT,username TEXT NOT NULL UNIQUE,email TEXT,full_name TEXT NOT NULL,role TEXT NOT NULL CHECK (role IN ('admin', 'operator', 'viewer')),team TEXT NOT NULL,active INTEGER NOT NULL DEFAULT 1,created_at TEXT NOT NULL DEFAULT (datetime('now')),last_login TEXT);-- ── Services applicatifs ────────────────────────────CREATE TABLE IF NOT EXISTS services (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT NOT NULL,server_id INTEGER NOT NULL REFERENCES servers(id),port INTEGER,protocol TEXT CHECK (protocol IN ('http', 'https', 'tcp', 'udp')),status TEXT NOT NULL CHECK (status IN ('running', 'stopped', 'failed', 'maintenance')),started_at TEXT,config_path TEXT);-- ── Historique des déploiements ─────────────────────CREATE TABLE IF NOT EXISTS deployments (id INTEGER PRIMARY KEY AUTOINCREMENT,service_id INTEGER NOT NULL REFERENCES services(id),deployed_by INTEGER NOT NULL REFERENCES users(id),version TEXT NOT NULL,environment TEXT NOT NULL,status TEXT NOT NULL CHECK (status IN ('success', 'failed', 'rollback', 'in_progress')),duration_seconds INTEGER,deployed_at TEXT NOT NULL DEFAULT (datetime('now')),notes TEXT);-- ── Alertes de supervision ──────────────────────────CREATE TABLE IF NOT EXISTS alerts (id INTEGER PRIMARY KEY AUTOINCREMENT,server_id INTEGER NOT NULL REFERENCES servers(id),severity TEXT NOT NULL CHECK (severity IN ('critical', 'warning', 'info')),message TEXT NOT NULL,acknowledged INTEGER NOT NULL DEFAULT 0,created_at TEXT NOT NULL DEFAULT (datetime('now')),acknowledged_at TEXT,acknowledged_by INTEGER REFERENCES users(id));-- ── Catégories d'infrastructure (pour CTE récursifs) ──CREATE TABLE IF NOT EXISTS categories (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT NOT NULL,parent_id INTEGER REFERENCES categories(id));-- ════════════════════════════════════════════════════════-- DONNÉES-- ════════════════════════════════════════════════════════INSERT INTO servers (hostname, ip_address, os, os_version, environment, datacenter, cpu_cores, ram_gb, disk_gb, active, created_at, notes) VALUES('web-prod-01', '10.1.1.10', 'Debian', '12', 'production', 'paris-dc1', 4, 8, 100, 1, '2025-03-15 09:00:00', NULL),('web-prod-02', '10.1.1.11', 'Debian', '12', 'production', 'paris-dc1', 4, 8, 100, 1, '2025-03-15 09:30:00', NULL),('db-prod-01', '10.1.2.10', 'Ubuntu', '24.04', 'production', 'paris-dc1', 8, 32, 500, 1, '2025-01-10 14:00:00', 'PostgreSQL principal'),('db-prod-02', '10.1.2.11', 'Ubuntu', '24.04', 'production', 'paris-dc1', 8, 32, 500, 1, '2025-01-10 14:30:00', 'PostgreSQL replica'),('app-staging-01', '10.2.1.10', 'Debian', '12', 'staging', 'paris-dc2', 2, 4, 50, 1, '2025-06-01 10:00:00', NULL),('db-staging-01', '10.2.2.10', 'Ubuntu', '24.04', 'staging', 'paris-dc2', 4, 16, 200, 1, '2025-06-01 10:30:00', NULL),('monitoring-01', '10.1.3.10', 'Rocky Linux', '9', 'production', 'paris-dc1', 4, 16, 200, 1, '2025-02-20 11:00:00', 'Prometheus + Grafana'),('ci-runner-01', '10.3.1.10', 'Ubuntu', '24.04', 'development', 'lyon-dc1', 8, 16, 200, 1, '2025-04-10 08:00:00', 'GitLab Runner'),('ci-runner-02', '10.3.1.11', 'Ubuntu', '24.04', 'development', 'lyon-dc1', 8, 16, 200, 1, '2025-04-10 08:30:00', 'GitLab Runner'),('backup-01', '10.1.4.10', 'Debian', '11', 'production', 'paris-dc1', 2, 8, 2000, 1, '2024-11-05 16:00:00', 'Serveur de sauvegarde'),('legacy-web-01', '10.1.1.50', 'CentOS', '7', 'production', 'paris-dc1', 2, 4, 50, 0, '2023-06-15 09:00:00', 'Décommissionné'),('dev-sandbox-01', '10.3.2.10', 'Fedora', '40', 'development', 'lyon-dc1', 4, 8, 100, 1, '2026-01-15 13:00:00', NULL);INSERT INTO users (username, email, full_name, role, team, active, created_at, last_login) VALUES('jdupont', 'jean.dupont@infra.local', 'Jean Dupont', 'admin', 'infrastructure', 1, '2024-06-15 09:00:00', '2026-04-12 17:45:00'),('mmartin', 'marie.martin@infra.local', 'Marie Martin', 'admin', 'infrastructure', 1, '2024-06-15 09:30:00', '2026-04-13 08:30:00'),('pbernard', 'paul.bernard@infra.local', 'Paul Bernard', 'operator', 'deploiement', 1, '2024-09-01 10:00:00', '2026-04-11 16:00:00'),('agarcia', 'alice.garcia@infra.local', 'Alice Garcia', 'operator', 'deploiement', 1, '2025-01-10 08:00:00', '2026-04-13 09:15:00'),('lmoreau', NULL, 'Lucas Moreau', 'operator', 'securite', 1, '2025-03-20 14:00:00', '2026-04-10 12:00:00'),('cbenoit', 'claire.benoit@infra.local', 'Claire Benoit', 'viewer', 'direction', 1, '2025-06-01 11:00:00', '2026-03-28 10:00:00'),('trousseau', 'thomas.rousseau@infra.local','Thomas Rousseau', 'operator', 'infrastructure', 1, '2025-09-15 09:00:00', NULL),('nleroy', 'nadia.leroy@infra.local', 'Nadia Leroy', 'viewer', 'deploiement', 1, '2025-11-01 10:00:00', '2026-04-05 14:00:00'),('fduval', 'fabien.duval@infra.local', 'Fabien Duval', 'admin', 'securite', 1, '2024-03-01 08:00:00', '2026-04-13 07:00:00'),('sperrin', 'sylvie.perrin@infra.local', 'Sylvie Perrin', 'operator', 'infrastructure', 0, '2024-01-15 09:00:00', '2025-08-30 17:00:00');INSERT INTO services (name, server_id, port, protocol, status, started_at, config_path) VALUES('nginx', 1, 443, 'https', 'running', '2026-04-01 06:00:00', '/etc/nginx/nginx.conf'),('nginx', 2, 443, 'https', 'running', '2026-04-01 06:00:00', '/etc/nginx/nginx.conf'),('api-backend', 1, 8080, 'http', 'running', '2026-04-01 06:05:00', '/opt/api/config.yaml'),('api-backend', 2, 8080, 'http', 'running', '2026-04-01 06:05:00', '/opt/api/config.yaml'),('postgresql', 3, 5432, 'tcp', 'running', '2025-01-10 15:00:00', '/etc/postgresql/16/main/postgresql.conf'),('postgresql', 4, 5432, 'tcp', 'running', '2025-01-10 15:30:00', '/etc/postgresql/16/main/postgresql.conf'),('postgresql', 6, 5432, 'tcp', 'running', '2025-06-01 11:00:00', '/etc/postgresql/16/main/postgresql.conf'),('prometheus', 7, 9090, 'http', 'running', '2025-02-20 12:00:00', '/etc/prometheus/prometheus.yml'),('grafana', 7, 3000, 'http', 'running', '2025-02-20 12:30:00', '/etc/grafana/grafana.ini'),('alertmanager', 7, 9093, 'http', 'running', '2025-02-20 12:15:00', '/etc/alertmanager/alertmanager.yml'),('gitlab-runner', 8, NULL, NULL, 'running', '2025-04-10 09:00:00', '/etc/gitlab-runner/config.toml'),('gitlab-runner', 9, NULL, NULL, 'stopped', '2025-04-10 09:00:00', '/etc/gitlab-runner/config.toml'),('nginx', 5, 443, 'https', 'running', '2025-06-01 10:15:00', '/etc/nginx/nginx.conf'),('api-backend', 5, 8080, 'http', 'failed', '2026-04-13 02:00:00', '/opt/api/config.yaml'),('restic', 10, NULL, NULL, 'running', '2024-11-05 17:00:00', '/etc/restic/config.json');INSERT INTO deployments (service_id, deployed_by, version, environment, status, duration_seconds, deployed_at, notes) VALUES(3, 4, '2.1.0', 'production', 'success', 45, '2026-04-12 14:00:00', NULL),(4, 4, '2.1.0', 'production', 'success', 42, '2026-04-12 14:02:00', NULL),(14, 4, '2.1.0', 'staging', 'success', 38, '2026-04-11 10:00:00', 'Déploiement staging avant prod'),(3, 3, '2.0.9', 'production', 'success', 50, '2026-04-05 11:00:00', NULL),(4, 3, '2.0.9', 'production', 'success', 48, '2026-04-05 11:03:00', NULL),(14, 3, '2.0.9', 'staging', 'failed', 120, '2026-04-04 16:00:00', 'Erreur de migration BDD'),(14, 3, '2.0.9', 'staging', 'success', 55, '2026-04-04 17:00:00', 'Retry après correction'),(1, 1, '1.24.0', 'production', 'success', 15, '2026-04-01 06:00:00', 'Mise à jour nginx'),(2, 1, '1.24.0', 'production', 'success', 14, '2026-04-01 06:01:00', NULL),(13, 1, '1.24.0', 'staging', 'success', 12, '2026-03-30 09:00:00', NULL),(5, 2, '16.3', 'production', 'success', 300, '2026-03-15 22:00:00', 'Mise à jour PostgreSQL'),(6, 2, '16.3', 'production', 'success', 280, '2026-03-15 22:10:00', NULL),(7, 2, '16.3', 'staging', 'success', 250, '2026-03-14 14:00:00', NULL),(8, 9, '2.51.0', 'production', 'success', 30, '2026-03-01 10:00:00', NULL),(9, 9, '10.4.0', 'production', 'success', 25, '2026-03-01 10:30:00', NULL),(10, 9, '0.27.0', 'production', 'success', 20, '2026-03-01 10:15:00', NULL),(3, 4, '2.0.8', 'production', 'rollback', 90, '2026-02-20 15:00:00', 'Rollback après erreur 500'),(4, 4, '2.0.8', 'production', 'rollback', 88, '2026-02-20 15:05:00', NULL),(3, 3, '2.0.7', 'production', 'success', 52, '2026-02-10 11:00:00', NULL),(4, 3, '2.0.7', 'production', 'success', 50, '2026-02-10 11:03:00', NULL),(14, 4, '2.1.1', 'staging', 'failed', 200, '2026-04-13 01:30:00', 'Service API en erreur'),(14, 4, '2.1.0', 'staging', 'success', 40, '2026-04-13 02:30:00', 'Rollback vers version stable'),(11, 1, '17.1.0', 'development', 'success', 10, '2026-04-10 08:00:00', NULL),(15, 9, '0.16.0', 'production', 'success', 5, '2026-01-15 09:00:00', NULL),(3, 4, '2.0.6', 'production', 'success', 55, '2026-01-20 14:00:00', NULL);INSERT INTO alerts (server_id, severity, message, acknowledged, created_at, acknowledged_at, acknowledged_by) VALUES(3, 'critical', 'Espace disque > 90% sur /var/lib/postgresql', 1, '2026-04-13 03:00:00', '2026-04-13 03:15:00', 2),(1, 'warning', 'Charge CPU > 80% pendant 10 min', 1, '2026-04-12 14:30:00', '2026-04-12 14:45:00', 1),(2, 'warning', 'Charge CPU > 80% pendant 10 min', 1, '2026-04-12 14:31:00', '2026-04-12 14:45:00', 1),(5, 'critical', 'Service api-backend en état failed', 0, '2026-04-13 02:05:00', NULL, NULL),(7, 'info', 'Redémarrage planifié de Prometheus effectué', 1, '2026-04-10 06:00:00', '2026-04-10 06:05:00', 9),(10, 'warning', 'Backup quotidien : durée > 4h (seuil : 2h)', 0, '2026-04-12 04:30:00', NULL, NULL),(8, 'info', 'GitLab Runner : nouveau runner enregistré', 1, '2026-04-10 08:10:00', '2026-04-10 08:15:00', 1),(4, 'warning', 'Réplication PostgreSQL : lag > 30s', 1, '2026-04-11 22:00:00', '2026-04-11 22:30:00', 2),(11, 'critical', 'Serveur injoignable depuis 5 min', 1, '2025-09-01 14:00:00', '2025-09-01 14:05:00', 1),(3, 'warning', 'Connexions PostgreSQL > 80% du max', 0, '2026-04-13 08:00:00', NULL, NULL),(1, 'info', 'Certificat TLS expire dans 30 jours', 0, '2026-04-10 09:00:00', NULL, NULL),(2, 'info', 'Certificat TLS expire dans 30 jours', 0, '2026-04-10 09:01:00', NULL, NULL),(12, 'warning', 'Espace disque > 85% sur /home', 1, '2026-04-08 11:00:00', '2026-04-08 11:30:00', 1),(6, 'info', 'Mise à jour de sécurité disponible pour postgresql', 0, '2026-04-12 07:00:00', NULL, NULL),(9, 'info', 'GitLab Runner 02 : job échoué 3 fois', 0, '2026-04-11 15:00:00', NULL, NULL),(7, 'warning', 'Espace disque > 75% sur /var/lib/prometheus', 1, '2026-04-06 08:00:00', '2026-04-06 09:00:00', 9),(3, 'critical', 'PostgreSQL : requête bloquée depuis 30 min', 1, '2026-04-09 16:00:00', '2026-04-09 16:10:00', 2),(5, 'warning', 'Latence réseau > 100ms vers paris-dc1', 0, '2026-04-13 07:00:00', NULL, NULL),(1, 'critical', 'OOM Killer activé — processus nginx tué', 1, '2026-03-25 03:00:00', '2026-03-25 03:20:00', 1),(10, 'info', 'Backup hebdomadaire terminé avec succès', 1, '2026-04-07 06:00:00', '2026-04-07 06:01:00', 9);INSERT INTO categories (id, name, parent_id) VALUES(1, 'Infrastructure', NULL),(2, 'Compute', 1),(3, 'Réseau', 1),(4, 'Stockage', 1),(5, 'Machines virtuelles', 2),(6, 'Conteneurs', 2),(7, 'Load Balancers', 3),(8, 'Firewalls', 3),(9, 'Bloc', 4),(10, 'Objet', 4),(11, 'Sauvegarde', 4); -
Chargez la base.
Fenêtre de terminal sqlite3 infra.db < init-db.sql -
Vérifiez que tout est en place en ouvrant la base et en lançant quelques requêtes :
Fenêtre de terminal sqlite3 infra.db.tables-- Résultat : alerts categories deployments servers services usersSELECT count(*) FROM servers; -- 12SELECT count(*) FROM users; -- 10SELECT count(*) FROM services; -- 15SELECT count(*) FROM deployments;-- 25SELECT count(*) FROM alerts; -- 20
Votre base est prête. Gardez le terminal sqlite3 infra.db ouvert pour pratiquer les requêtes de ce guide.
Les 6 tables du fil rouge
Section intitulée « Les 6 tables du fil rouge »| Table | Lignes | Contenu |
|---|---|---|
servers | 12 | Serveurs gérés (hostname, OS, environnement, datacenter, RAM…) |
users | 10 | Membres de l’équipe (rôle, équipe, dernier login) |
services | 15 | Applications déployées sur les serveurs (port, protocole, statut) |
deployments | 25 | Historique des déploiements (version, durée, statut, qui) |
alerts | 20 | Alertes de supervision (sévérité, message, acquittement) |
categories | 11 | Arborescence d’infrastructure (pour les CTE récursifs, guide 5) |
Les données sont réalistes : serveurs décommissionnés (active = 0), utilisateur sans email (NULL), services en erreur, déploiements échoués, alertes non acquittées.
La structure d’une requête SELECT
Section intitulée « La structure d’une requête SELECT »Toute requête de lecture en SQL suit le même schéma fondamental :
SELECT colonnesFROM tableWHERE conditionsORDER BY colonnesLIMIT nombre;Seuls SELECT et FROM sont obligatoires. Les autres clauses sont optionnelles et s’ajoutent dans cet ordre précis.
SELECT : choisir les colonnes
Section intitulée « SELECT : choisir les colonnes »SELECT définit quelles colonnes vous voulez voir dans le résultat.
-- Sélectionner deux colonnes spécifiquesSELECT hostname, ip_addressFROM servers;Pour récupérer toutes les colonnes, utilisez * :
SELECT *FROM servers;FROM : préciser la table
Section intitulée « FROM : préciser la table »FROM identifie la table (ou les tables) à interroger :
SELECT username, full_name, roleFROM users;La requête minimale
Section intitulée « La requête minimale »La plus simple requête SQL possible :
SELECT 1;Elle ne lit aucune table — elle renvoie simplement la valeur 1. C’est un test rapide pour vérifier que votre connexion fonctionne.
Filtrer avec WHERE
Section intitulée « Filtrer avec WHERE »WHERE restreint les lignes renvoyées à celles qui respectent une condition.
SELECT hostname, environmentFROM serversWHERE active = 1;Sans WHERE, la requête renvoie toutes les lignes de la table.
Opérateurs de comparaison
Section intitulée « Opérateurs de comparaison »| Opérateur | Signification | Exemple |
|---|---|---|
= | Égal | WHERE environment = 'production' |
<> ou != | Différent | WHERE role <> 'viewer' |
< | Inférieur | WHERE cpu_cores < 4 |
> | Supérieur | WHERE created_at > '2026-01-01' |
<= | Inférieur ou égal | WHERE ram_gb <= 8 |
>= | Supérieur ou égal | WHERE disk_gb >= 200 |
Opérateurs logiques (AND, OR, NOT)
Section intitulée « Opérateurs logiques (AND, OR, NOT) »Combinez plusieurs conditions :
-- Les deux conditions doivent être vraiesSELECT hostname, osFROM serversWHERE active = 1 AND environment = 'production';
-- Au moins une condition doit être vraieSELECT username, roleFROM usersWHERE role = 'admin' OR role = 'operator';
-- Inverser une conditionSELECT hostnameFROM serversWHERE NOT active;IN, BETWEEN et LIKE
Section intitulée « IN, BETWEEN et LIKE »Ces opérateurs simplifient les conditions courantes :
-- IN : appartenance à une liste (remplace plusieurs OR)SELECT hostname, environmentFROM serversWHERE environment IN ('production', 'staging');
-- BETWEEN : intervalle inclusifSELECT hostname, created_atFROM serversWHERE created_at BETWEEN '2025-01-01' AND '2025-06-30';
-- LIKE : correspondance de motif (% = n'importe quoi, _ = un caractère)SELECT hostnameFROM serversWHERE hostname LIKE 'web-%';LIKE est insensible à la casse pour les caractères ASCII en SQLite (et en MySQL avec les collations par défaut). En PostgreSQL, utilisez ILIKE pour ce comportement.
Filtrer les valeurs NULL
Section intitulée « Filtrer les valeurs NULL »NULL signifie « absence de valeur ». Ce n’est ni zéro, ni une chaîne vide — c’est inconnu. On ne peut pas comparer avec =, il faut utiliser IS NULL ou IS NOT NULL :
-- Trouver le membre de l'équipe sans emailSELECT username, full_nameFROM usersWHERE email IS NULL;
-- Trouver les serveurs qui ont une noteSELECT hostname, notesFROM serversWHERE notes IS NOT NULL;Trier avec ORDER BY
Section intitulée « Trier avec ORDER BY »ORDER BY contrôle l’ordre des lignes dans le résultat.
-- Tri ascendant (par défaut)SELECT hostname, created_atFROM serversORDER BY created_at ASC;
-- Tri descendant (les plus récents d'abord)SELECT hostname, created_atFROM serversORDER BY created_at DESC;Tri sur plusieurs colonnes
Section intitulée « Tri sur plusieurs colonnes »Les colonnes de tri sont évaluées de gauche à droite. Le deuxième critère ne départage que les lignes à égalité sur le premier :
SELECT environment, hostnameFROM serversORDER BY environment ASC, hostname ASC;Limiter les résultats
Section intitulée « Limiter les résultats »Sur une table de millions de lignes, vous ne voulez pas tout récupérer. LIMIT restreint le nombre de lignes renvoyées.
LIMIT et OFFSET
Section intitulée « LIMIT et OFFSET »-- Les 5 serveurs les plus récentsSELECT hostname, os, created_atFROM serversORDER BY created_at DESCLIMIT 5;
-- Serveurs 6 à 10 (pagination)SELECT hostname, os, created_atFROM serversORDER BY created_at DESCLIMIT 5 OFFSET 5;LIMIT et OFFSET fonctionnent avec SQLite, PostgreSQL et MySQL. C’est la syntaxe la plus courante.
FETCH FIRST (SQL standard)
Section intitulée « FETCH FIRST (SQL standard) »Le standard SQL utilise une syntaxe différente, supportée par PostgreSQL et SQLite (mais pas MySQL) :
SELECT hostname, osFROM serversORDER BY created_at DESCFETCH FIRST 5 ROWS ONLY;En pratique, LIMIT est quasi universel et plus simple — préférez-le.
Alias de colonnes et de tables (AS)
Section intitulée « Alias de colonnes et de tables (AS) »Les alias renomment une colonne ou une table dans le résultat. C’est utile pour la lisibilité, surtout avec des expressions calculées.
-- Alias de colonneSELECT hostname AS serveur, ip_address AS adresse_ipFROM servers;
-- Alias de table (utile avec les jointures — guide 3)SELECT s.hostname, s.osFROM servers AS sWHERE s.active = 1;Le mot-clé AS est optionnel (on peut écrire FROM servers s), mais le garder rend le code plus explicite.
Éliminer les doublons (DISTINCT)
Section intitulée « Éliminer les doublons (DISTINCT) »DISTINCT supprime les lignes identiques du résultat :
-- Liste des systèmes d'exploitation distinctsSELECT DISTINCT osFROM servers;
-- Combinaison de colonnes distinctesSELECT DISTINCT os, environmentFROM servers;Les commentaires en SQL
Section intitulée « Les commentaires en SQL »Les commentaires documentent vos requêtes — très utile dans les scripts :
-- Commentaire sur une ligne
/* Commentaire sur plusieurs lignes */
SELECT hostname -- on sélectionne le nom d'hôteFROM servers;Exercice pratique
Section intitulée « Exercice pratique »Vous avez maintenant toutes les bases. Pratiquez sur la base infra.db :
-
Listez tous les serveurs de production actifs, triés par date de création (les plus récents d’abord).
SELECT hostname, os, datacenter, created_atFROM serversWHERE environment = 'production' AND active = 1ORDER BY created_at DESC; -
Trouvez le membre de l’équipe qui ne s’est jamais connecté (colonne
last_loginà NULL).SELECT username, full_name, created_atFROM usersWHERE last_login IS NULL; -
Listez les 3 derniers serveurs ajoutés à l’inventaire.
SELECT hostname, os, environment, created_atFROM serversORDER BY created_at DESCLIMIT 3; -
Cherchez les serveurs dont le hostname commence par
db-parmi les serveurs actifs.SELECT hostname, ip_address, environmentFROM serversWHERE active = 1 AND hostname LIKE 'db-%'ORDER BY hostname; -
Listez les systèmes d’exploitation distincts utilisés dans l’infrastructure.
SELECT DISTINCT os, os_versionFROM serversWHERE active = 1ORDER BY os; -
Trouvez les serveurs avec plus de 16 Go de RAM dans le datacenter
paris-dc1.SELECT hostname, ram_gb, disk_gbFROM serversWHERE ram_gb > 16 AND datacenter = 'paris-dc1'ORDER BY ram_gb DESC;
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
| Résultat vide inattendu | Condition WHERE trop restrictive | Retirez les filtres un par un pour identifier le coupable |
WHERE email = NULL ne renvoie rien | NULL ne se compare pas avec = | Utilisez IS NULL |
| Résultat dans un ordre différent à chaque exécution | Pas de ORDER BY | Ajoutez un ORDER BY explicite |
SELECT * très lent sur une grosse table | Toutes les colonnes + toutes les lignes | Listez les colonnes et ajoutez LIMIT ou WHERE |
Erreur no such column | Nom de colonne mal orthographié | Vérifiez avec .schema table dans sqlite3, \d table dans psql |
À retenir
Section intitulée « À retenir »SELECT+FROMforment la requête minimale — tout le reste est optionnelWHEREfiltre les lignes avant qu’elles ne soient renvoyées — c’est votre outil principalNULLn’est pas une valeur — on le teste toujours avecIS NULL/IS NOT NULLANDest évalué avantOR— utilisez des parenthèses pour clarifierORDER BYest le seul moyen de garantir l’ordre du résultatLIMITempêche de ramener des millions de lignes par accident- Préférez les colonnes explicites à
SELECT *dans les scripts et en production - La base fil rouge
infra.dbvous accompagnera tout au long des 5 guides