NULL dans les agrégats, les jointures et DISTINCT
Comprendre les différences de comportement de NULL lors du regroupement, des jointures et de la recherche d’unicité
NULL dans les agrégats, les jointures et DISTINCT est une leçon SQL Interview Prep gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage SQL Interview Prep, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours SQL Interview Prep comprend 4 leçons au total.
NULL à trois endroits surprenants
NULL ne se comporte pas de la même manière partout. Cette dernière leçon couvre les trois contextes qui surprennent le plus souvent les candidats : les agrégats, les jointures et DISTINCT / GROUP BY.
Le point récurrent est le suivant : les agrégats et le filtrage traitent NULL comme une valeur à « ignorer », tandis que le regroupement et DISTINCT le traitent comme « une valeur égale aux autres NULL ». Cette incohérence est précisément ce que les recruteurs cherchent à vérifier.
Maîtrisez ces points et vous aurez couvert les questions les plus fréquentes sur NULL lors des entretiens SQL.
Les agrégats ignorent NULL
La règle essentielle est la suivante : les fonctions d’agrégation ignorent les NULL. SUM, AVG, MIN, MAX et COUNT(column) ignorent entièrement les entrées NULL au lieu de les traiter comme des zéros.
C’est pourquoi AVG peut renvoyer un nombre différent de celui auquel vous vous attendez. La fonction divise la somme des valeurs non-NULL par le nombre de valeurs non-NULL, et non par le nombre total de lignes.
-- bonus values: 100, 200, NULL
SELECT
SUM(bonus) AS total, -- 300 (NULL ignored)
AVG(bonus) AS average, -- 150 = 300 / 2, not / 3
COUNT(bonus) AS cnt -- 2 (NULL not counted)
FROM employees;COUNT(*) et COUNT(column)
C’est la question la plus fréquente sur les agrégats et NULL. COUNT(*) compte les lignes, y compris celles qui contiennent des NULL. COUNT(column) compte uniquement les lignes où cette colonne n’est pas NULL.
La différence entre les deux correspond donc exactement au nombre de NULL présents dans cette colonne. COUNT(DISTINCT column) va plus loin : il ignore également les NULL tout en supprimant les doublons.
SELECT
COUNT(*) AS rows_total, -- all rows
COUNT(bonus) AS non_null_bonus, -- excludes NULLs
COUNT(DISTINCT bonus) AS distinct_bonus, -- excludes NULLs + dups
COUNT(*) - COUNT(bonus) AS null_bonus
FROM employees;AVG contre SUM/COUNT(*) : un piège classique
Les recruteurs demandent parfois : « AVG(x) est-elle équivalente à SUM(x) / COUNT(*) ? » La réponse est non lorsque des NULL sont présents.
AVG(x) est égale à SUM(x) / COUNT(x) : la division se fait par le nombre de valeurs non-NULL. Utiliser COUNT(*) à la place revient à traiter les NULL comme des zéros et réduit la moyenne.
Si vous voulez réellement compter les NULL comme des zéros, vous devez l’indiquer explicitement avec COALESCE.
-- These differ when bonus has NULLs:
SELECT
AVG(bonus) AS avg_ignoring_nulls,
SUM(bonus) * 1.0 / COUNT(*) AS avg_nulls_as_zero,
AVG(COALESCE(bonus, 0)) AS explicit_nulls_as_zero
FROM employees;Le cas limite d’un agrégat entièrement NULL
Que renvoie un agrégat lorsque chaque entrée est NULL ou lorsqu’il n’y a aucune ligne ? Voici une distinction précise que les recruteurs apprécient :
SUM,AVG,MINetMAXappliqués à des lignes entièrement NULL (ou à zéro ligne) renvoient NULL.COUNTrenvoie toujours 0, jamais NULL.
Si un rapport affiche des totaux vides, un SUM entièrement NULL en est probablement la cause. Entourez-le de COALESCE pour afficher 0.
-- No matching rows or all bonuses NULL:
SELECT SUM(bonus) FROM employees WHERE 1 = 0; -- NULL
SELECT COUNT(bonus) FROM employees WHERE 1 = 0; -- 0
-- Present a clean zero:
SELECT COALESCE(SUM(bonus), 0) FROM employees;NULL dans les conditions de JOIN
Dans la clause ON d’une jointure, NULL = NULL vaut toujours UNKNOWN : les clés NULL ne correspondent jamais dans une équijointure. Deux lignes dont la clé de jointure vaut NULL ne seront pas associées.
Ce cas surprend souvent lorsqu’on effectue une jointure sur des clés étrangères facultatives. Si la correspondance entre NULL et NULL est le comportement souhaité, vous avez besoin d’un opérateur compatible avec NULL (IS NOT DISTINCT FROM ou <=>), présenté dans la leçon précédente.
-- Rows with region IS NULL on both sides do NOT match
SELECT *
FROM a JOIN b ON a.region = b.region;
-- To match NULL-to-NULL (ANSI):
SELECT *
FROM a JOIN b ON a.region IS NOT DISTINCT FROM b.region;Les NULL produits par les jointures externes
Les jointures externes produisent des NULL pour les lignes sans correspondance. Après un LEFT JOIN, chaque colonne du côté droit vaut NULL pour les lignes du côté gauche qui n’ont trouvé aucune correspondance.
C’est le fondement du modèle d’anti-jointure : utilisez le filtre WHERE right_table.key IS NULL pour trouver les lignes sans correspondance, par exemple les clients sans commande.
Soyez toutefois prudent : filtrer une colonne issue d’une jointure externe dans WHERE peut la transformer accidentellement en jointure interne. C’est le sujet de la scène suivante.
-- Find customers who have never ordered (anti-join)
SELECT c.id, c.name
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.id IS NULL;Le piège de NULL avec WHERE après une jointure externe
Voici un piège très courant. Vous effectuez un LEFT JOIN sur les commandes, puis vous ajoutez WHERE o.status = 'shipped'. Les clients sans commande disparaissent soudainement, ce qui transforme votre jointure externe en jointure interne effective.
Pourquoi ? Pour les lignes sans correspondance, o.status vaut NULL et NULL = 'shipped' vaut UNKNOWN : WHERE les élimine donc. Pour conserver les lignes sans correspondance, déplacez plutôt la condition dans la clause ON.
-- Accidental inner join: drops customers with no orders
SELECT c.name, o.status
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.status = 'shipped';
-- Correct: keep unmatched customers
SELECT c.name, o.status
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.id AND o.status = 'shipped';DISTINCT traite tous les NULL comme égaux
Voici l'incohérence qui surprend tout le monde. Les agrégats ignorent NULL, mais DISTINCT conserve exactement un NULL, en considérant tous les NULL comme des doublons les uns des autres.
Ainsi, SELECT DISTINCT bonus appliqué aux valeurs 100, 100, NULL, NULL renvoie trois lignes : 100, NULL, et c'est tout. Les deux NULL sont regroupés en un seul, même si NULL = NULL vaut UNKNOWN ailleurs.
-- bonus: 100, 100, NULL, NULL, 200
SELECT DISTINCT bonus FROM employees;
-- Returns: 100, 200, NULL (the two NULLs become one row)GROUP BY regroupe les NULL dans un seul groupe
GROUP BY suit la même règle que DISTINCT : toutes les clés NULL sont rassemblées dans un seul groupe. C'est l'opposé de la logique de comparaison, dans laquelle les NULL ne sont jamais égaux entre eux.
Regrouper selon une colonne pouvant contenir NULL vous donne donc une ligne représentant tous les enregistrements dont la clé est NULL, ce qui correspond généralement à ce que vous souhaitez pour les rapports. Mentionnez ce contraste (regroupement contre comparaison) pour montrer votre maîtrise du sujet.
-- All employees with NULL department form ONE group
SELECT department, COUNT(*) AS headcount
FROM employees
GROUP BY department;
-- A single row where department is NULL totals all of themPoints clés à aborder en entretien
Le résumé unificateur qui impressionne les recruteurs :
- Les agrégats ignorent NULL ; AVG divise par COUNT(colonne), et non par COUNT(*).
- COUNT(*) compte les lignes ; COUNT(colonne) et COUNT(DISTINCT colonne) ignorent NULL.
- SUM/AVG/MIN/MAX appliqués à aucune ligne renvoient NULL ; COUNT renvoie 0.
- Dans les jointures, les clés NULL ne correspondent jamais ; filtrer dans WHERE une colonne issue d'une jointure externe la transforme silencieusement en jointure interne.
- DISTINCT et GROUP BY considèrent tous les NULL comme égaux, à l'opposé de la logique de comparaison.
La phrase à retenir : « NULL est ignoré lors des agrégations et des comparaisons, mais regroupé lors de la déduplication. »
Vérification rapide
Vérifiez le contraste entre regroupement et agrégation.
Récapitulatif
Vous avez terminé l'étude de la gestion de NULL pour les entretiens :
- Les agrégats ignorent NULL ; AVG divise par le nombre de valeurs non NULL, et une somme entièrement composée de NULL vaut NULL, tandis que COUNT vaut 0.
COUNT(*)inclut les lignes contenant NULL ;COUNT(col)ne les inclut pas, et l'écart correspond au nombre de NULL.- Les clés de jointure qui valent NULL ne correspondent jamais ; filtrer dans WHERE les colonnes issues d'une jointure externe peut la transformer en jointure interne.
- DISTINCT et GROUP BY regroupent tous les NULL en un seul, à l'inverse de la logique de comparaison.
Retenez cette formule : NULL est ignoré lors des agrégations et des comparaisons, mais regroupé lors de la déduplication. Cette seule idée répond à la plupart des questions d'entretien sur NULL.
Questions Fréquemment Posées
La leçon « NULL dans les agrégats, les jointures et DISTINCT » est-elle gratuite ?
Oui — le texte complet de « NULL dans les agrégats, les jointures et DISTINCT » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours SQL Interview Prep, passe à CoddyKit PRO. Le cours SQL Interview Prep comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « NULL dans les agrégats, les jointures et DISTINCT » ?
Comprendre les différences de comportement de NULL lors du regroupement, des jointures et de la recherche d’unicité Tu pratiques SQL Interview Prep avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer SQL Interview Prep ?
Aucune expérience préalable n'est requise. SQL Interview Prep sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.
Combien de temps prend la leçon « NULL dans les agrégats, les jointures et DISTINCT » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon SQL Interview Prep ?
Oui. Chaque leçon SQL Interview Prep inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Logique à trois valeurs et UNKNOWN
- IS NULL, IS NOT NULL et égalité compatible avec NULL
- COALESCE, NULLIF et ISNULL
- NULL dans les agrégats, les jointures et DISTINCT