ToolBox Hub
Gros plan de code de programmation colore affiche sur un ecran de moniteur.

Aide-memoire SQL pour les developpeurs : Des bases aux requetes avancees en 2026

đź“· Myburgh Roux / Pexels

Aide-memoire SQL pour les developpeurs : Des bases aux requetes avancees en 2026

Un guide de reference SQL complet couvrant SELECT, les JOINs, les sous-requetes, les window functions, les CTEs, l'indexation et les conseils d'optimisation pour les developpeurs.

19 mars 202615 min de lecture

SQL reste l'une des competences les plus importantes qu'un developpeur puisse avoir en 2026. Que vous construisiez des REST APIs, travailliez avec des pipelines d'analytics ou debutiez des donnees de production, vous aurez inevitablement besoin d'ecrire et de comprendre du SQL. Cet aide-memoire va bien au-dela des bases, vous offrant une reference pratique qui couvre tout, des simples instructions SELECT aux window functions avancees et aux strategies d'optimisation de requetes.

Requetes SELECT de base

Tout parcours SQL commence avec l'instruction SELECT. C'est le fondement de toutes les operations de recuperation de donnees.

Selectionner des colonnes

-- Selectionner des colonnes specifiques
SELECT first_name, last_name, email
FROM users;

-- Selectionner toutes les colonnes (a eviter en code de production)
SELECT *
FROM users;

-- Alias de colonnes pour plus de clarte
SELECT
  first_name AS "First Name",
  last_name AS "Last Name",
  created_at AS "Registration Date"
FROM users;

-- Selectionner des valeurs distinctes
SELECT DISTINCT department
FROM employees;

Filtrer avec WHERE

La clause WHERE vous permet de filtrer les lignes selon des conditions. Comprendre la precedence des operateurs et le traitement des NULL est essentiel pour ecrire des requetes correctes.

-- Comparaison de base
SELECT * FROM products
WHERE price > 50.00;

-- Conditions multiples avec AND / OR
SELECT * FROM orders
WHERE status = 'shipped'
  AND total_amount > 100
  AND created_at >= '2026-01-01';

-- Operateur IN pour valeurs multiples
SELECT * FROM users
WHERE country IN ('US', 'CA', 'GB', 'DE');

-- BETWEEN pour les plages (inclusif)
SELECT * FROM events
WHERE event_date BETWEEN '2026-01-01' AND '2026-12-31';

-- Correspondance de motifs avec LIKE
SELECT * FROM products
WHERE name LIKE '%wireless%';  -- contient 'wireless'

-- Gestion des NULLs (= ne fonctionne PAS avec NULL)
SELECT * FROM users
WHERE phone IS NULL;

SELECT * FROM users
WHERE phone IS NOT NULL;

Une erreur courante est d'ecrire WHERE column = NULL au lieu de WHERE column IS NULL. La premiere forme evalue toujours a UNKNOWN et ne retourne aucune ligne.

Trier avec ORDER BY

-- Tri sur une seule colonne
SELECT * FROM products
ORDER BY price DESC;

-- Tri sur plusieurs colonnes
SELECT * FROM employees
ORDER BY department ASC, salary DESC;

-- Tri par position de colonne (moins lisible, mais valide)
SELECT first_name, last_name, hire_date
FROM employees
ORDER BY 3 DESC;

-- NULLS FIRST / NULLS LAST (PostgreSQL, Oracle)
SELECT * FROM tasks
ORDER BY due_date ASC NULLS LAST;

Limiter les resultats

-- PostgreSQL / MySQL
SELECT * FROM logs
ORDER BY created_at DESC
LIMIT 100;

-- Avec offset pour la pagination
SELECT * FROM products
ORDER BY id
LIMIT 20 OFFSET 40;  -- Page 3 (20 elements par page)

-- SQL Server
SELECT TOP 100 * FROM logs
ORDER BY created_at DESC;

-- SQL standard (FETCH FIRST)
SELECT * FROM logs
ORDER BY created_at DESC
FETCH FIRST 100 ROWS ONLY;

Fonctions d'agregation et GROUP BY

Les fonctions d'agregation regroupent plusieurs lignes en une seule valeur de synthese. Elles sont presque toujours utilisees conjointement avec GROUP BY.

-- Fonctions d'agregation courantes
SELECT
  department,
  COUNT(*) AS employee_count,
  AVG(salary) AS avg_salary,
  MIN(salary) AS min_salary,
  MAX(salary) AS max_salary,
  SUM(salary) AS total_payroll
FROM employees
GROUP BY department;

-- Filtrer les groupes avec HAVING
SELECT
  category,
  COUNT(*) AS product_count,
  AVG(price) AS avg_price
FROM products
GROUP BY category
HAVING COUNT(*) > 10
ORDER BY avg_price DESC;

-- Variantes de COUNT
SELECT
  COUNT(*) AS total_rows,         -- compte toutes les lignes, y compris les NULLs
  COUNT(email) AS has_email,      -- compte les emails non NULL
  COUNT(DISTINCT city) AS cities  -- compte les villes uniques
FROM users;

La distinction cle : WHERE filtre les lignes individuelles avant l'agregation, tandis que HAVING filtre les groupes apres l'agregation. Vous ne pouvez pas referencer de fonctions d'agregation dans une clause WHERE.

JOINs : Combiner les tables

Les JOINs sont la ou SQL devient vraiment puissant. Comprendre les differents types est essentiel pour travailler avec des donnees relationnelles.

INNER JOIN

Ne retourne que les lignes ayant des valeurs correspondantes dans les deux tables.

SELECT
  o.id AS order_id,
  o.order_date,
  c.name AS customer_name,
  c.email
FROM orders o
INNER JOIN customers c ON o.customer_id = c.id;

LEFT JOIN (LEFT OUTER JOIN)

Retourne toutes les lignes de la table de gauche et les lignes correspondantes de la table de droite. Les colonnes sans correspondance du cote droit retournent NULL.

-- Trouver tous les clients et leurs commandes (y compris les clients sans commandes)
SELECT
  c.name,
  c.email,
  o.id AS order_id,
  o.total_amount
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id;

-- Trouver les clients qui n'ont jamais passe de commande
SELECT c.name, c.email
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
WHERE o.id IS NULL;

RIGHT JOIN et FULL OUTER JOIN

-- RIGHT JOIN : toutes les lignes de la table de droite
SELECT
  e.name AS employee,
  d.name AS department
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.id;

-- FULL OUTER JOIN : toutes les lignes des deux tables
SELECT
  s.name AS student,
  c.title AS course
FROM students s
FULL OUTER JOIN enrollments e ON s.id = e.student_id
FULL OUTER JOIN courses c ON e.course_id = c.id;

CROSS JOIN

Produit le produit cartesien de deux tables. Chaque ligne de la premiere table est combinee avec chaque ligne de la deuxieme table.

-- Generer toutes les combinaisons taille-couleur possibles
SELECT
  s.size_name,
  c.color_name
FROM sizes s
CROSS JOIN colors c;

Self JOIN

Une table jointe a elle-meme, utile pour les donnees hierarchiques ou comparatives.

-- Trouver les employes et leurs responsables
SELECT
  e.name AS employee,
  m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;

Sous-requetes

Les sous-requetes sont des requetes imbriquees dans d'autres requetes. Elles peuvent apparaitre dans les clauses SELECT, FROM, WHERE et HAVING.

Sous-requete dans WHERE

-- Trouver les produits dont le prix est superieur a la moyenne
SELECT name, price
FROM products
WHERE price > (SELECT AVG(price) FROM products);

-- Trouver les clients qui ont passe des commandes dans les 30 derniers jours
SELECT name, email
FROM customers
WHERE id IN (
  SELECT DISTINCT customer_id
  FROM orders
  WHERE order_date >= CURRENT_DATE - INTERVAL '30 days'
);

Sous-requetes correlees

Une sous-requete correlee reference des colonnes de la requete externe et s'execute une fois par ligne externe. Elles sont puissantes mais peuvent etre plus lentes que les JOINs sur de grands ensembles de donnees.

-- Trouver les employes qui gagnent plus que la moyenne de leur departement
SELECT e.name, e.salary, e.department_id
FROM employees e
WHERE e.salary > (
  SELECT AVG(e2.salary)
  FROM employees e2
  WHERE e2.department_id = e.department_id
);

-- Verification avec EXISTS
SELECT c.name
FROM customers c
WHERE EXISTS (
  SELECT 1
  FROM orders o
  WHERE o.customer_id = c.id
    AND o.total_amount > 1000
);

Sous-requete dans FROM (Tables derivees)

SELECT
  dept_stats.department,
  dept_stats.avg_salary,
  dept_stats.employee_count
FROM (
  SELECT
    department,
    AVG(salary) AS avg_salary,
    COUNT(*) AS employee_count
  FROM employees
  GROUP BY department
) AS dept_stats
WHERE dept_stats.employee_count > 5
ORDER BY dept_stats.avg_salary DESC;

Common Table Expressions (CTEs)

Les CTEs offrent un moyen d'ecrire du SQL modulaire et lisible. Ils definissent des ensembles de resultats temporaires nommes qui existent le temps d'une seule requete.

CTE de base

WITH active_customers AS (
  SELECT
    c.id,
    c.name,
    c.email,
    COUNT(o.id) AS order_count,
    SUM(o.total_amount) AS total_spent
  FROM customers c
  JOIN orders o ON c.id = o.customer_id
  WHERE o.order_date >= '2026-01-01'
  GROUP BY c.id, c.name, c.email
)
SELECT *
FROM active_customers
WHERE total_spent > 500
ORDER BY total_spent DESC;

CTEs multiples

WITH
monthly_revenue AS (
  SELECT
    DATE_TRUNC('month', order_date) AS month,
    SUM(total_amount) AS revenue
  FROM orders
  WHERE order_date >= '2025-01-01'
  GROUP BY DATE_TRUNC('month', order_date)
),
revenue_with_growth AS (
  SELECT
    month,
    revenue,
    LAG(revenue) OVER (ORDER BY month) AS prev_month_revenue,
    ROUND(
      (revenue - LAG(revenue) OVER (ORDER BY month))
      / LAG(revenue) OVER (ORDER BY month) * 100, 2
    ) AS growth_pct
  FROM monthly_revenue
)
SELECT *
FROM revenue_with_growth
ORDER BY month;

CTEs recursifs

Les CTEs recursifs sont parfaits pour les donnees hierarchiques telles que les organigrammes, les arborescences de categories ou le parcours de graphes.

WITH RECURSIVE org_chart AS (
  -- Cas de base : responsables de niveau superieur (sans responsable)
  SELECT id, name, manager_id, 1 AS level
  FROM employees
  WHERE manager_id IS NULL

  UNION ALL

  -- Cas recursif : employes avec un responsable deja dans le resultat
  SELECT e.id, e.name, e.manager_id, oc.level + 1
  FROM employees e
  JOIN org_chart oc ON e.manager_id = oc.id
)
SELECT
  REPEAT('  ', level - 1) || name AS org_tree,
  level
FROM org_chart
ORDER BY level, name;

Window Functions

Les window functions effectuent des calculs sur un ensemble de lignes liees a la ligne courante sans les regrouper en une seule ligne de sortie. Elles sont l'une des fonctionnalites les plus puissantes du SQL moderne.

ROW_NUMBER, RANK et DENSE_RANK

SELECT
  name,
  department,
  salary,
  ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num,
  RANK() OVER (ORDER BY salary DESC) AS rank,
  DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank
FROM employees;

La difference : si deux employes sont a egalite au rang 2, ROW_NUMBER leur attribue arbitrairement 2 et 3, RANK attribue 2 aux deux et saute a 4, et DENSE_RANK attribue 2 aux deux et continue a 3.

Window Functions partitionnees

-- Top 3 des salaires les plus eleves par departement
WITH ranked AS (
  SELECT
    name,
    department,
    salary,
    ROW_NUMBER() OVER (
      PARTITION BY department
      ORDER BY salary DESC
    ) AS dept_rank
  FROM employees
)
SELECT * FROM ranked
WHERE dept_rank <= 3;

LAG et LEAD

Ces fonctions vous permettent d'acceder aux valeurs des lignes precedentes ou suivantes.

SELECT
  order_date,
  total_amount,
  LAG(total_amount, 1) OVER (ORDER BY order_date) AS prev_day_amount,
  LEAD(total_amount, 1) OVER (ORDER BY order_date) AS next_day_amount,
  total_amount - LAG(total_amount, 1) OVER (ORDER BY order_date) AS daily_change
FROM daily_sales;

Totaux cumulatifs et moyennes mobiles

SELECT
  order_date,
  total_amount,
  SUM(total_amount) OVER (
    ORDER BY order_date
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
  ) AS running_total,
  AVG(total_amount) OVER (
    ORDER BY order_date
    ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
  ) AS seven_day_avg
FROM daily_sales;

NTILE pour le regroupement

-- Diviser les clients en 4 quartiles par depenses
SELECT
  name,
  total_spent,
  NTILE(4) OVER (ORDER BY total_spent DESC) AS spending_quartile
FROM customer_summary;

Strategies d'indexation

Ecrire du SQL efficace ne se limite pas a la syntaxe des requetes ; il s'agit egalement de s'assurer que la base de donnees puisse executer vos requetes rapidement. Les index sont le mecanisme principal pour cela.

Types d'index

-- Index B-tree (par defaut, adapte aux requetes d'egalite et de plage)
CREATE INDEX idx_users_email ON users (email);

-- Index composite (l'ordre des colonnes est important)
CREATE INDEX idx_orders_customer_date
ON orders (customer_id, order_date DESC);

-- Index unique (impose egalement l'unicite)
CREATE UNIQUE INDEX idx_users_email_unique ON users (email);

-- Index partiel (PostgreSQL - indexe uniquement un sous-ensemble de lignes)
CREATE INDEX idx_orders_pending
ON orders (created_at)
WHERE status = 'pending';

-- Index GIN pour la recherche plein texte (PostgreSQL)
CREATE INDEX idx_articles_search
ON articles USING GIN (to_tsvector('english', title || ' ' || body));

Quand creer des index

Les index accelerent les lectures mais ralentissent les ecritures. Creez des index sur les colonnes qui apparaissent frequemment dans les clauses WHERE, les conditions de JOIN et les clauses ORDER BY. Evitez d'indexer les colonnes a tres faible cardinalite (comme une colonne booleenne) sauf si vous utilisez un index partiel.

Un index composite sur (a, b, c) peut satisfaire les requetes filtrant sur a, sur a AND b, ou sur a AND b AND c, mais pas sur b seul ou c seul. L'ordre des colonnes est determinant.

Verifier les performances des requetes avec EXPLAIN

-- PostgreSQL
EXPLAIN ANALYZE
SELECT * FROM orders
WHERE customer_id = 42
  AND order_date >= '2026-01-01';

-- MySQL
EXPLAIN
SELECT * FROM orders
WHERE customer_id = 42
  AND order_date >= '2026-01-01';

Recherchez les parcours sequentiels (Seq Scan) sur les grandes tables, qui indiquent un index manquant. Un Index Scan ou Index Only Scan est ce que vous souhaitez voir.

Conseils d'optimisation des requetes

1. Evitez SELECT *

Specifiez toujours les colonnes dont vous avez besoin. Cela reduit les E/S, l'utilisation memoire et peut permettre des index-only scans.

-- Mauvais
SELECT * FROM users WHERE id = 42;

-- Bon
SELECT id, name, email FROM users WHERE id = 42;

2. Utilisez EXISTS au lieu de IN pour les grandes sous-requetes

-- Plus lent avec de grands resultats de sous-requete
SELECT * FROM customers
WHERE id IN (SELECT customer_id FROM orders);

-- Plus rapide : s'arrete a la premiere correspondance
SELECT * FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id);

3. Evitez les fonctions sur les colonnes indexees

-- Mauvais : l'index sur created_at n'est pas utilise
SELECT * FROM orders
WHERE YEAR(created_at) = 2026;

-- Bon : balayage de plage compatible avec les index
SELECT * FROM orders
WHERE created_at >= '2026-01-01'
  AND created_at < '2027-01-01';

4. Utilisez UNION ALL au lieu de UNION quand c'est possible

UNION supprime les doublons (necessitant un tri), tandis que UNION ALL ne le fait pas. Si vous savez que les ensembles de resultats sont deja distincts, preferez toujours UNION ALL.

-- Plus lent (deduplique)
SELECT name FROM employees
UNION
SELECT name FROM contractors;

-- Plus rapide (pas de deduplication)
SELECT name FROM employees
UNION ALL
SELECT name FROM contractors;

5. Operations par lots pour les grands volumes de donnees

-- Au lieu de supprimer des millions de lignes d'un coup
-- Supprimer par lots pour eviter de verrouiller la table
DELETE FROM logs
WHERE created_at < '2025-01-01'
LIMIT 10000;
-- Repeter jusqu'a ce que 0 lignes soient affectees

6. Utilisez les types de donnees appropries

Des types de donnees plus petits signifient que plus de lignes tiennent en memoire et dans les pages d'index. Utilisez INTEGER au lieu de BIGINT lorsque la plage le permet. Utilisez VARCHAR avec des longueurs appropriees plutot que TEXT pour les colonnes indexees.

Modeles SQL utiles

UPSERT (INSERT ON CONFLICT)

-- PostgreSQL
INSERT INTO user_settings (user_id, theme, language)
VALUES (42, 'dark', 'en')
ON CONFLICT (user_id)
DO UPDATE SET
  theme = EXCLUDED.theme,
  language = EXCLUDED.language;

-- MySQL
INSERT INTO user_settings (user_id, theme, language)
VALUES (42, 'dark', 'en')
ON DUPLICATE KEY UPDATE
  theme = VALUES(theme),
  language = VALUES(language);

Pivoter les donnees avec CASE

SELECT
  product_id,
  SUM(CASE WHEN month = 1 THEN revenue ELSE 0 END) AS jan,
  SUM(CASE WHEN month = 2 THEN revenue ELSE 0 END) AS feb,
  SUM(CASE WHEN month = 3 THEN revenue ELSE 0 END) AS mar
FROM monthly_revenue
GROUP BY product_id;

Generer des series de dates

-- PostgreSQL : generer une serie de dates
SELECT generate_series(
  '2026-01-01'::date,
  '2026-12-31'::date,
  '1 day'::interval
)::date AS date;

-- Utiliser avec LEFT JOIN pour combler les lacunes dans les donnees de series temporelles
SELECT
  d.date,
  COALESCE(s.total, 0) AS daily_total
FROM generate_series('2026-01-01'::date, '2026-03-31'::date, '1 day') AS d(date)
LEFT JOIN daily_sales s ON s.sale_date = d.date
ORDER BY d.date;

Tableau de reference rapide

OperationSyntaxe
Filtrer les lignesWHERE condition
Trier les resultatsORDER BY column ASC/DESC
Limiter les lignesLIMIT n ou FETCH FIRST n ROWS ONLY
Supprimer les doublonsSELECT DISTINCT
Grouper et agregerGROUP BY ... HAVING
Combiner les tablesJOIN ... ON
Ensemble de resultats temporaireWITH cte AS (...)
Numerotation des lignesROW_NUMBER() OVER (...)
Ligne precedente/suivanteLAG() / LEAD() OVER (...)
Total cumulatifSUM() OVER (ORDER BY ... ROWS ...)
Inserer ou mettre a jourINSERT ... ON CONFLICT DO UPDATE

Conclusion

SQL est un langage vaste, mais maitriser les modeles de cet aide-memoire couvrira la grande majorite des scenarios reels que vous rencontrerez en tant que developpeur. Commencez par des bases solides en SELECT et JOIN, puis ajoutez progressivement les CTEs et les window functions a votre boite a outils. Gardez toujours a l'esprit l'indexation et les performances des requetes, surtout a mesure que vos donnees augmentent.

Essayez notre SQL Formatter pour embellir vos requetes.

Ajoutez cette page a vos favoris et revenez-y chaque fois que vous avez besoin d'une reference rapide. La meilleure facon d'interioriser le SQL est de pratiquer regulierement, que ce soit en ecrivant des requetes sur vos propres bases de donnees, en utilisant des plateformes comme les sandboxes en memoire SQLite, ou en explorant des jeux de donnees ouverts. Les modeles que vous construisez aujourd'hui vous serviront tout au long de votre carriere.

Articles associés