NULL signifie « inconnu »
Dans SQLite, toutes les autres valeurs représentent quelque chose de précis : un nombre, une chaîne, un blob. NULL, lui, joue dans une autre catégorie. C'est un marqueur qui indique une valeur manquante ou inconnue. Cette simple idée explique à elle seule tout le comportement bizarre de NULL dans vos requêtes.
Créons une petite table pour expérimenter :
Deux colonnes acceptent les valeurs NULL. Boris n'a pas d'email. Cleo n'a pas d'âge. Dan n'a ni l'un ni l'autre. Le reste de la page explique comment interroger ce genre de lignes sans se faire piéger.
= et <> ne fonctionnent pas avec NULL
Le réflexe naturel, c'est d'écrire WHERE email = NULL. Ça paraît logique. Et pourtant, ça ne renvoie rien :
Zéro ligne — alors que Boris et Dan ont visiblement un email à NULL. La raison ? Toute comparaison avec NULL renvoie NULL, et non vrai ou faux. La clause WHERE de SQLite ne conserve que les lignes pour lesquelles la condition est vraie, or NULL n'est pas vrai. Du coup, la ligne passe à la trappe.
Même piège avec <> :
Vous vous attendez sans doute à récupérer tout le monde sauf Ada. Eh bien non, seul Cleo ressort. Boris et Dan, dont l'email vaut null, passent à la trappe — parce que NULL <> 'ada@example.com' renvoie lui aussi NULL, et pas vrai.
C'est LE piège classique de SQL. Dès qu'une requête « perd » des lignes que vous attendiez, le coupable est presque toujours une colonne qui contient des null.
Utiliser IS NULL et IS NOT NULL
Pour tester un null correctement, il faut passer par l'opérateur IS. Contrairement à =, il sait gérer les null et renvoie toujours vrai ou faux — jamais null :
La première requête renvoie Boris et Dan, la seconde renvoie Ada et Cleo. IS NULL et IS NOT NULL sont les deux opérateurs conçus précisément pour répondre à la question « cette valeur est-elle absente ? ». Utilisez-les partout où vous seriez tenté d'écrire = NULL ou <> NULL.
Si vous voulez « tout sauf Ada, y compris les valeurs inconnues », il faut combiner les deux conditions explicitement :
Et là, Boris, Cleo et Dan apparaissent tous les trois.
NULL se propage dans les calculs et la concaténation
La règle de l'« inconnu » ne se limite pas aux comparaisons. Toute opération qui touche un NULL renvoie un NULL :
next_year et doubled valent NULL pour Cleo et Dan. labelled_age aussi : concaténer une chaîne avec NULL donne NULL, pas 'Âge : '. Si une colonne peut être nulle et que vous voulez récupérer une valeur exploitable derrière, il faut gérer le cas. C'est précisément le rôle des deux fonctions qui suivent.
IFNULL : la valeur de repli à deux arguments
IFNULL(a, b) renvoie a, sauf si c'est NULL — auquel cas elle renvoie b. C'est la façon la plus directe de remplacer un NULL par une valeur par défaut :
Boris et Dan reçoivent (aucun e-mail). Cleo et Dan obtiennent 0. Les données d'origine ne bougent pas — IFNULL se contente de réécrire ce qui sort de la requête.
IFNULL prend toujours exactement deux arguments. S'il vous faut plusieurs valeurs de repli, c'est COALESCE qu'il faut utiliser.
COALESCE : la première valeur non NULL l'emporte
COALESCE(a, b, c, ...) parcourt ses arguments dans l'ordre et renvoie le premier qui n'est pas null. C'est en quelque sorte la version généralisée d'IFNULL, capable d'enchaîner autant de valeurs de repli qu'on veut :
Pour Ada et Cleo, c'est l'email qui sort. Pour Boris et Dan, l'email est NULL, donc SQLite passe au deuxième argument : une adresse fabriquée à partir du nom. Et si celui-ci était également NULL, on retomberait sur 'anonyme'.
COALESCE reste le choix portable : toutes les grandes bases SQL le gèrent de la même façon. IFNULL, lui, est un raccourci propre à SQLite et MySQL, limité à deux arguments. Par défaut, partez sur COALESCE ; ne sortez IFNULL que si vous avez vraiment juste deux arguments et que vous tenez au nom plus court.
NULL n'est pas une chaîne vide en SQLite
Une confusion classique : croire que NULL et '' sont interchangeables. Ce n'est pas le cas.
'' est une vraie chaîne de caractères, elle a juste zéro caractère. NULL, lui, représente l'absence de valeur. length('') renvoie 0 ; length(NULL) renvoie... NULL. Et NULL = NULL vaut NULL, pas 1 — c'est précisément pour ça que IS NULL existe.
Si une colonne peut contenir à la fois '' et NULL, choisissez lequel des deux signifie « valeur manquante » et tenez-vous à cette règle. Mélanger les deux oblige chaque requête à gérer deux cas, et vous allez forcément en oublier un.
NULL avec IN, NOT IN et DISTINCT
Encore quelques endroits où le NULL te tombe dessus sans prévenir.
IN avec une liste qui contient un NULL peut donner des résultats déroutants, surtout combiné avec NOT IN :
On pourrait croire que cette requête renvoie tout le monde dont l'âge est différent de 25. Eh bien non, on n'obtient rien. SQLite développe NOT IN (25, NULL) en quelque chose comme age <> 25 AND age <> NULL, or age <> NULL vaut toujours NULL — du coup, la condition complète n'est jamais vraie. La solution : exclure les valeurs NULL de la liste (ou de la colonne) avant la comparaison.
DISTINCT, en revanche, considère que deux NULL sont égaux entre eux lorsqu'il s'agit de dédupliquer :
Vous obtenez... pardon, vous obtenez trois lignes : l'email d'Ada, celui de Cleo, et un seul NULL (qui regroupe ceux de Boris et Dan). Même histoire avec GROUP BY et UNION : ces opérations considèrent les valeurs nulles comme un groupe unique, soit l'inverse exact du comportement de =. SQL manque parfois de cohérence sur ce point ; mieux vaut savoir de quel côté se range chaque opérateur.
Mémo express
- Pour tester une valeur manquante, utilisez
IS NULL/IS NOT NULL. Jamais= NULL. - Toute opération arithmétique, concaténation ou comparaison impliquant
NULLrenvoieNULL. COALESCE(a, b, c, ...)remplace les nulls par une valeur de repli.IFNULL(a, b)en est la version courte à deux arguments.- La chaîne vide
''n'est pas équivalente àNULL. Choisissez l'une ou l'autre pour représenter « valeur absente » dans chaque colonne, et tenez-vous-y. NOT IN (..., NULL)est presque toujours un bug. Retirez les nulls de la liste avant.
La suite : trier les résultats
Une fois que vous savez filtrer correctement vos lignes — y compris celles à NULL —, l'étape suivante consiste à les organiser dans un ordre utile. ORDER BY fait l'objet de la page suivante, et il a, lui aussi, son petit avis sur la place que prennent les nulls dans un tri.
Questions fréquentes
Pourquoi column = NULL ne fonctionne pas en SQLite ?
column = NULL ne fonctionne pas en SQLite ?Parce que NULL signifie « valeur inconnue », et toute comparaison avec un inconnu donne elle-même un résultat inconnu — pas vrai. Du coup, WHERE col = NULL ne ramène aucune ligne, même celles où la colonne est effectivement nulle. Il faut écrire WHERE col IS NULL. Même logique pour <> : on utilise IS NOT NULL.
Quelle différence entre IFNULL et COALESCE en SQLite ?
IFNULL(a, b) prend exactement deux arguments et renvoie a, sauf s'il est null, auquel cas il renvoie b. COALESCE(a, b, c, ...) accepte autant d'arguments que vous voulez et retourne le premier qui n'est pas null. En clair, IFNULL est un raccourci à deux arguments ; COALESCE est la version générique, et c'est elle qu'on retrouve dans à peu près tous les SGBD SQL.
NULL et chaîne vide, c'est pareil en SQLite ?
Non, surtout pas. NULL veut dire « aucune valeur », alors que '' est une chaîne de longueur zéro — une vraie valeur, parfaitement connue. '' IS NULL renvoie 0 (faux), length('') vaut 0 tandis que length(NULL) vaut NULL. Si une colonne autorise les deux, vos requêtes doivent les traiter séparément, ou alors normaliser pour ne garder qu'une seule forme.