跳转至

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 值了