Le problème : « les clients actifs de l'Est qui n'ont pas d'email »
Le marketing débarque avec une demande tordue : « Je veux les clients de Besançon ou Dijon, inscrits entre janvier et mars, dont le nom commence par un M, et qui n'ont pas renseigné de téléphone. »
Une seule condition WHERE prix > 20 ne suffit plus. Il faut combiner plusieurs conditions avec précision. C'est tout l'art de la clause WHERE avancée. On travaille sur la table clients :
id | nom | ville | telephone | inscrit_le
---+--------------+-----------+-------------+-----------
1 | Marie Dupont | Besançon | 0601020304 | 2026-01-15
2 | Karim Benali | Dijon | NULL | 2026-02-03
3 | Léa Martin | Lyon | 0708091011 | 2026-03-21
Comparer et combiner : =, <, >, AND, OR, NOT
Les opérateurs de comparaison de base : = (égal), <> ou != (différent), <, >, <=, >=.
Pour combiner plusieurs conditions, on utilise AND (toutes vraies), OR (au moins une vraie) et NOT (l'inverse) :
SELECT nom, ville
FROM clients
WHERE ville = 'Besançon' AND telephone IS NOT NULL;
Attention aux parenthèses quand vous mélangez AND et OR. WHERE a = 1 OR a = 2 AND b = 3 ne fait pas ce que vous croyez : AND est prioritaire sur OR. Mettez des parenthèses explicites : WHERE (a = 1 OR a = 2) AND b = 3.
LIKE, IN, BETWEEN, IS NULL
LIKE cherche un motif dans du texte. Le caractère % remplace « n'importe quelle suite de caractères », _ remplace exactement un caractère :
SELECT nom
FROM clients
WHERE nom LIKE 'M%'; -- les noms commençant par M
IN teste l'appartenance à une liste, bien plus lisible qu'une cascade de OR :
SELECT nom, ville
FROM clients
WHERE ville IN ('Besançon', 'Dijon');
BETWEEN teste un intervalle (bornes incluses), idéal pour les dates et les nombres :
SELECT nom, inscrit_le
FROM clients
WHERE inscrit_le BETWEEN '2026-01-01' AND '2026-03-31';
NULL n'est pas une valeur ordinaire. telephone = NULL ne marche jamais, même si la valeur est vide. NULL veut dire « inconnu », et rien n'est égal à l'inconnu. Utilisez IS NULL ou IS NOT NULL :
SELECT nom FROM clients WHERE telephone IS NULL;
Trier sur plusieurs colonnes
ORDER BY accepte plusieurs colonnes, séparées par des virgules. Le tri se fait de gauche à droite : d'abord par la première colonne, puis la deuxième départage les égalités.
SELECT nom, ville, inscrit_le
FROM clients
ORDER BY ville ASC, inscrit_le DESC;
Ici, les clients sont d'abord groupés par ville (de A à Z), et à l'intérieur de chaque ville, du plus récemment inscrit au plus ancien.
La requête finale du marketing combine tout :
SELECT nom, ville
FROM clients
WHERE ville IN ('Besançon', 'Dijon')
AND inscrit_le BETWEEN '2026-01-01' AND '2026-03-31'
AND nom LIKE 'M%'
AND telephone IS NULL
ORDER BY ville, nom;
The problem: "active eastern customers with no email"
Marketing shows up with a tricky request: "I want customers from Besancon or Dijon, registered between January and March, whose name starts with an M, and who have no phone number."
A single WHERE prix > 20 condition is no longer enough. You need to combine several conditions precisely. This is the whole art of the advanced WHERE clause. We work on the clients table:
id | nom | ville | telephone | inscrit_le
---+--------------+-----------+-------------+-----------
1 | Marie Dupont | Besancon | 0601020304 | 2026-01-15
2 | Karim Benali | Dijon | NULL | 2026-02-03
3 | Lea Martin | Lyon | 0708091011 | 2026-03-21
Compare and combine: =, <, >, AND, OR, NOT
The basic comparison operators: = (equal), <> or != (not equal), <, >, <=, >=.
To combine conditions, use AND (all true), OR (at least one true) and NOT (the opposite):
SELECT nom, ville
FROM clients
WHERE ville = 'Besancon' AND telephone IS NOT NULL;
Watch out for parentheses when mixing AND and OR. WHERE a = 1 OR a = 2 AND b = 3 does not do what you think: AND has priority over OR. Use explicit parentheses: WHERE (a = 1 OR a = 2) AND b = 3.
LIKE, IN, BETWEEN, IS NULL
LIKE searches for a pattern in text. The % character replaces "any sequence of characters", _ replaces exactly one character:
SELECT nom
FROM clients
WHERE nom LIKE 'M%'; -- names starting with M
IN tests membership in a list, far more readable than a cascade of OR:
SELECT nom, ville
FROM clients
WHERE ville IN ('Besancon', 'Dijon');
BETWEEN tests a range (bounds included), ideal for dates and numbers:
SELECT nom, inscrit_le
FROM clients
WHERE inscrit_le BETWEEN '2026-01-01' AND '2026-03-31';
NULL is not an ordinary value. telephone = NULL never works, even if the value is empty. NULL means "unknown", and nothing equals the unknown. Use IS NULL or IS NOT NULL:
SELECT nom FROM clients WHERE telephone IS NULL;
Sorting on several columns
ORDER BY accepts several columns, separated by commas. Sorting goes left to right: first by the first column, then the second breaks ties.
SELECT nom, ville, inscrit_le
FROM clients
ORDER BY ville ASC, inscrit_le DESC;
Here, customers are first grouped by city (A to Z), and within each city, from most recently registered to oldest.
The final marketing query combines everything:
SELECT nom, ville
FROM clients
WHERE ville IN ('Besancon', 'Dijon')
AND inscrit_le BETWEEN '2026-01-01' AND '2026-03-31'
AND nom LIKE 'M%'
AND telephone IS NULL
ORDER BY ville, nom;
À vous d'essayer — la base est déjà remplie :
CREATE TABLE commandes (id INTEGER, client TEXT, ville TEXT, montant REAL);
INSERT INTO commandes VALUES (1, 'Alice Durand', 'Dijon', 120.50);
INSERT INTO commandes VALUES (2, 'Marc Petit', 'Lyon', 45.00);
INSERT INTO commandes VALUES (3, 'Léa Martin', 'Dijon', 230.90);
INSERT INTO commandes VALUES (4, 'Paul Roux', 'Lyon', 89.00);
SELECT client, montant
FROM commandes
WHERE ville = 'Dijon' AND montant > 100
ORDER BY montant DESC;
Faites traduire une demande métier en SQL par l'IA, puis vérifiez les pièges (NULL, parenthèses AND/OR) :
Table clients (id, nom, ville, telephone, inscrit_le). Écris une requête SQL pour les clients de Besançon ou Dijon, inscrits entre le 1er janvier et le 31 mars 2026, dont le nom commence par M et qui n'ont pas de téléphone. Trie par ville puis par nom. Attention : gère correctement le test du téléphone absent (NULL) et mets les parenthèses nécessaires entre les AND et OR. Explique pourquoi.
Sans relire la réponse de l'IA : pourquoi WHERE telephone = NULL ne renvoie-t-il jamais la moindre ligne, et qu'écris-tu à la place ?
NULL signifie « valeur inconnue », pas « vide ». En SQL, toute comparaison avec NULL via = ou <> renvoie UNKNOWN, jamais TRUE : la ligne est donc exclue. On teste l'absence de valeur avec IS NULL (et la présence avec IS NOT NULL), les seuls opérateurs conçus pour ça.Écrivez une requête qui renvoie les clients de Dijon OU Lyon (utilisez IN) dont le téléphone est renseigné (utilisez IS NOT NULL), triés par nom. Pensez à WHERE, IN, IS NOT NULL et ORDER BY.
Tu as demandé « les clients de Dijon ou Lyon dont le montant dépasse 100 ». L'IA propose ce code. Ton rôle de relecteur : l'accepter tel quel ou le rejeter, et dire pourquoi.
SELECT client, ville, montant
FROM commandes
WHERE ville = 'Dijon' OR ville = 'Lyon' AND montant > 100;
AND est prioritaire sur OR, donc la base lit ceci comme ville = 'Dijon' OR (ville = 'Lyon' AND montant > 100) : tous les clients de Dijon ressortent, même ceux à 10 €, alors que le filtre montant ne s'applique qu'à Lyon. Le réflexe pro : parenthéser l'alternative de villes, WHERE (ville = 'Dijon' OR ville = 'Lyon') AND montant > 100 (ou mieux, ville IN ('Dijon','Lyon')).Sans remonter dans la leçon : que filtre WHERE nom LIKE 'M%', et dans ORDER BY ville ASC, inscrit_le DESC que fait la deuxième colonne ?
LIKE 'M%' garde les noms qui commencent par M : le % remplace n'importe quelle suite de caractères (alors que '%M' filtrerait ceux qui finissent par M). Dans ORDER BY ville ASC, inscrit_le DESC, le tri se fait d'abord par ville de A à Z ; la deuxième colonne, inscrit_le DESC, ne sert qu'à départager les lignes d'une même ville, du plus récent au plus ancien.Filtrer et trier, c'est répondre ligne par ligne. La prochaine étape change d'échelle : compter, totaliser et faire des moyennes par groupe avec COUNT, SUM, AVG, GROUP BY et HAVING.
Leçon 4 : Agréger et regrouper →