NULL and three-valued logic
NULL means “unknown”, not zero or empty. See why `= NULL` never matches, how NULL spreads through expressions, the NOT IN trap, and the functions that replace NULL with a real value.
N''). It is the absence of a value.
SELECT FirstName, City, Email, PhoneFROM dbo.Customers;FirstName City Email Phone --------- --------- ----------------- ------------- Asha Leeds asha@example.com 0113 496 0001 Ben York NULL 01904 555 010 Chloe Leeds chloe@example.com NULL Dev Bradford dev@example.com 01274 555 220 Ella NULL ella@example.com NULL Farid York farid@example.com 01904 555 777 Grace Leeds NULL NULL Zoë Harrogate zoe@example.com 01423 555 123
Three-valued logic
=, <>, <, …) gives UNKNOWN. WHERE keeps a row only when the condition is TRUE, so UNKNOWN rows are dropped, exactly like FALSE ones.
IS NULL, not = NULL
= NULL asks “is this equal to an unknown value?”, which is always UNKNOWN, so this query returns no rows at all, even though three customers have no phone:
SELECT FirstName, PhoneFROM dbo.CustomersWHERE Phone = NULL;FirstName Phone --------- -----
IS NULL (or IS NOT NULL). It asks “is a value missing?”, which always has a TRUE or FALSE answer:
SELECT FirstName, PhoneFROM dbo.CustomersWHERE Phone IS NULL;FirstName Phone --------- ----- Chloe NULL Ella NULL Grace NULL
The missing row
SELECT FirstName, CityFROM dbo.CustomersWHERE City <> N'Leeds';FirstName City --------- --------- Ben York Dev Bradford Farid York Zoë Harrogate
WHERE City <> N'Leeds' OR City IS NULL.
NULL spreads through expressions
+ with a NULL gives NULL: 5 plus an unknown number is unknown. CONCAT is the exception; it treats NULL as empty text.
SELECT 5 + NULL AS AddNull, N'Phone: ' + NULL AS PlusNull, CONCAT(N'Phone: ', NULL) AS ConcatNull;AddNull PlusNull ConcatNull ------- -------- ---------- NULL NULL Phone:
Replacing NULL: ISNULL and COALESCE
ISNULL(x, fallback) returns x, or the fallback when x is NULL. COALESCE(a, b, c, …) returns the first value in its list that is not NULL, so it can try several columns in turn:
SELECT FirstName, ISNULL(City, N'(unknown)') AS City, COALESCE(Phone, Email, 'no contact details') AS BestContactFROM dbo.Customers;FirstName City BestContact --------- --------- ------------------ Asha Leeds 0113 496 0001 Ben York 01904 555 010 Chloe Leeds chloe@example.com Dev Bradford 01274 555 220 Ella (unknown) ella@example.com Farid York 01904 555 777 Grace Leeds no contact details Zoë Harrogate 01423 555 123
COALESCE is standard SQL and takes any number of arguments; ISNULL is T-SQL only and takes exactly two. NULLIF(a, b) goes the other way: it returns NULL when a equals b, which is handy to avoid dividing by zero: x / NULLIF(Stock, 0).
The NOT IN trap
City NOT IN (N'Leeds', NULL) means City <> N'Leeds' AND City <> NULL. The second test is always UNKNOWN, so the whole condition can never be TRUE:
SELECT FirstName, CityFROM dbo.CustomersWHERE City NOT IN (N'Leeds', NULL);FirstName City --------- ----
| Expression | Result |
|---|---|
NULL = NULL | |
NULL <> 5 | |
NULL IS NULL | |
TRUE AND UNKNOWN | |
FALSE AND UNKNOWN | |
TRUE OR UNKNOWN | |
NOT UNKNOWN | |
5 + NULL |
FirstName, LastName and City, plus a Phone column that shows (none) when the phone is missing.
Show a hint
Show the solution
SELECT FirstName, LastName, City, ISNULL(Phone, '(none)') AS PhoneFROM dbo.CustomersWHERE City IS NOT NULL AND Email IS NULL;FirstName LastName City Phone --------- -------- ----- ------------- Ben Carter York 01904 555 010 Grace Lee Leeds (none)
Remember
NULL means unknown or missing: not zero, not empty text. Comparisons with NULL give UNKNOWN, and WHERE keeps only TRUE rows, so test with IS NULL/IS NOT NULL.A condition and its opposite miss the NULL rows; decide where they belong. NULL spreads through +and arithmetic;CONCATtreats it as empty text.ISNULLandCOALESCEreplace NULL with a value;NOT INwith a NULL in the list returns nothing.