Topic 4.5 - SQL 中的 Null 值处理¶
1. Null 值的定义¶
在 SQL 中,Null 值表示缺失或未知的数据。它不同于空字符串或零值,Null 表示没有值:
-
Null 值的产生原因有很多:
- 一方面是数据库本身的原因,数据缺失、未知、不适用等情况都会导致 Null 值的产生
- 另一方面,我们了解过,SQL 中 Left Join、Right Join、Full Outer Join 等连接操作也会产生 Null 值
-
Null 值在 SQL 中的处理方式主要就两种:
- 一方面是使用
IS NOT NULL来判断某个字段是否为 Null 值,这个我们在之前的课程中已经讲过了 - 另一方面是使用
COALESCE(column, value)函数来将 Null 值替换为其他值,这个我们本节课来重点讲解
- 一方面是使用
在 SQL 运算中:
- 当我们对 Null 值进行任何运算时,结果都会是 Null 值,包括加减乘除等基本运算,以及比较运算、逻辑运算等
-
在聚合函数中:
COUNT(*)函数会忽略 Null 值,只统计非 Null 的记录数COUNT(column)、SUM(column)、AVG(column)、MAX(column)、MIN(column)等函数在计算时会忽略 Null 值
2. Null 值的填充¶
在 SQL 中,我们可以使用 COALESCE(column, value) 函数来将 Null 值替换为其他值:
- 在
COALESCE(column, value)函数中,column是我们要检查的字段,value是当column为 Null 时要替换的值 - 当
column的值为 Null 时,我们会将其替换为value,否则我们会保留column的原值
我们在 Chinook 数据库中来看一个具体例子
- 假设我们想统计2025年12月,每种音乐流派的销售额,我们可以使用以下 SQL 查询:
SELECT
G.Name AS Genre,
SUM(IL.UnitPrice * IL.Quantity) AS TotalRevenue
FROM InvoiceLine AS IL
JOIN Invoice AS I ON IL.InvoiceId = I.InvoiceId
JOIN Track AS T ON IL.TrackId = T.TrackId
JOIN Genre AS G ON T.GenreId = G.GenreId
WHERE I.InvoiceDate BETWEEN '2025-12-01 00:00:00.000' AND '2025-12-31 23:59:59.999'
GROUP BY G.Name
========================
Genre TotalRevenue
---------- ------------
Latin 3.96
Metal 1.98
Reggae 0.99
Rock 29.70
TV Shows 1.99
((5 rows affected))
-
从上面的查询中,我们可以看到,许多的音乐流派都没有展示出来,因为这些流派在2025年12月没有任何销售数据
-
这时候,我们将这个结果和流派表进行 Left Join,这时候许多流派的销售额都是 Null 值:
WITH
GenreRevenue AS (
SELECT
G.Name AS Genre,
SUM(IL.UnitPrice * IL.Quantity) AS TotalRevenue
FROM InvoiceLine AS IL
JOIN Invoice AS I ON IL.InvoiceId = I.InvoiceId
JOIN Track AS T ON IL.TrackId = T.TrackId
JOIN Genre AS G ON T.GenreId = G.GenreId
WHERE I.InvoiceDate BETWEEN '2025-12-01 00:00:00.000' AND '2025-12-31 23:59:59.999'
GROUP BY G.Name
)
SELECT
G.Name AS Genre,
GR.TotalRevenue
TotalRevenue
FROM Genre AS G
LEFT JOIN GenreRevenue AS GR ON G.Name = GR.Genre
;
================================
Genre TotalRevenue
------------------ ------------
Rock 29.70
Jazz NULL
Metal 1.98
Alternative & Punk NULL
Rock And Roll NULL
Blues NULL
Latin 3.96
Reggae 0.99
Pop NULL
Soundtrack NULL
Bossa Nova NULL
Easy Listening NULL
Heavy Metal NULL
R&B/Soul NULL
Electronica/Dance NULL
World NULL
Hip Hop/Rap NULL
Science Fiction NULL
TV Shows 1.99
Sci Fi & Fantasy NULL
Drama NULL
Comedy NULL
Alternative NULL
Classical NULL
Opera NULL
((25 rows affected))
- 这个时候,我们可以使用
COALESCE(GR.TotalRevenue, 0)来将 Null 值替换为 0:
WITH
GenreRevenue AS (
SELECT
G.Name AS Genre,
SUM(IL.UnitPrice * IL.Quantity) AS TotalRevenue
FROM InvoiceLine AS IL
JOIN Invoice AS I ON IL.InvoiceId = I.InvoiceId
JOIN Track AS T ON IL.TrackId = T.TrackId
JOIN Genre AS G ON T.GenreId = G.GenreId
WHERE I.InvoiceDate BETWEEN '2025-12-01 00:00:00.000' AND '2025-12-31 23:59:59.999'
GROUP BY G.Name
)
SELECT
G.Name AS Genre,
COALESCE(GR.TotalRevenue, 0) AS TotalRevenue
FROM Genre AS G
LEFT JOIN GenreRevenue AS GR ON G.Name = GR.Genre
;
================================
Genre TotalRevenue
------------------ ------------
Rock 29.70
Jazz 0.00
Metal 1.98
Alternative & Punk 0.00
Rock And Roll 0.00
Blues 0.00
Latin 3.96
Reggae 0.99
Pop 0.00
Soundtrack 0.00
Bossa Nova 0.00
Easy Listening 0.00
Heavy Metal 0.00
R&B/Soul 0.00
Electronica/Dance 0.00
World 0.00
Hip Hop/Rap 0.00
Science Fiction 0.00
TV Shows 1.99
Sci Fi & Fantasy 0.00
Drama 0.00
Comedy 0.00
Alternative 0.00
Classical 0.00
Opera 0.00
((25 rows affected))
- 最终这个表其实就是我们想要的结果了,既可以展示所有的流派,又可以展示每个流派的销售额,如果没有销售额的话,我们就用 0 来替代 Null 值了