• 《数据库基础语法》9. 小心 SQL 中的空值陷阱


    从本节开始我们将会学习进阶内容。进阶内容将会介绍更加复杂的多表查询、子查询、集合运算以及各种高级的数据分析技术,能够让我们真正体会到 SQL 数据处理和分析的强大。

    作为进阶的开始,我们先来讨论一下 SQL 中的空值问题,因为空值的特殊性导致我们很容易出现一些错误和问题。

    空值与三值逻辑:

    SQL 中的空值(NULL)是一个特殊的值,代表了 缺失 / 未知 的数据或者不适用的情况。例如,用户在注册时没有提供电子邮箱地址,那么该用户的邮箱就未知;是否怀孕对于男性员工就不适用。

    对于大多数的编程语言而言,逻辑运算的结果只有两种情况:真(Ture)或者假(False)。但是对于 SQL 而言,逻辑运算结果存在三种情况:真、假 或者 未知(Unknown)

    对于 SQL 查询中的 WHERE 条件,只有结果为真的数据才会返回,结果为假或者未知都不会返回。

    而布尔值和 NULL 进行逻辑运算的话,那么结果为 NULL,除非通过短路求值提前判断结果,举个例子

    -- AND 要求两边都为真,所以左边的 true 决定不了结果,因此会检测 NULL,而两者逻辑运算结果为 NULL
    true AND NULL; -- NULL
    -- AND 左边为假,直接判断结果,所以右边的 NULL 不会检测了,所以结果为false
    false AND NULL; -- false
    
    -- or要求有一边为真即可,左边为 true,所以右边的 NULL 不检测了,结果为true
    true OR NULL; -- true
    -- 左边为 false,所以看右边,而右边为 NULL,所以结果是 NULL
    false OR NULL; -- false
    
    -- 如果 NULL 在左边,不用想了,结果必为 NULL
    -- 同理对 NULL 取反,结果依旧是 NULL
    

    空值比较与运算:

    任何值与 NULL 值进行比较时,结果无法确认是真还是假。以下比较运算的结果都是未知:

    NULL = 0
    NULL != 0
    NULL > 1
    NULL = NULL
    NULL != NULL
    

    NULL 等于 NULL 的运算结果未知,NULL 不等于 NULL 的运算结果也未知。因此,为了判断某个值是否为空,SQL 提供了 IS NULL 和 IS NOT NULL 谓词。

    另外,当表达式或者函数涉及 NULL 值时,通常结果也是 NULL 值。例如:

    100 + NULL;
    UPPER(NULL);
    

    不过也存在一些例外,比如:

    SELECT CONCAT('hello', NULL); -- hello
    

    该查询只有在 MySQL 返回 NULL,CONCAT 函数在 Oracle、SQL Server 以及 PostgreSQL 中将 NULL 当作空字符串处理,因此返回字符串 "hello"。由于不同数据库采取了不同的实现,因此在使用时这些函数时需要小心。

    空值的排序:

    我们之前介绍了 ORDER BY 排序,并且讨论了空值的排序规则。由于 SQL 标准没有提出明确要求,导致在不同的数据库中存在两种空值排序方式:

    • Oracle 和 PostgreSQL,认为空值最大,升序时空值排在最后,降序时空值排在最前;同时支持使用 NULLS FIRST 和 NULLS LAST 指定空值的顺序
    • MySQL 和 SQL Server,认为空值最小,升序时空值排在最前,降序时空值排在最后

    我们可以利用 COALESCE 函数或者 CASE 表达式解决空值的排序问题,上面也给出了 CASE 表达式相关的示例。下面我们介绍一下 SQL 中关于空值处理的函数。

    空值的函数:

    SQL 中定义了两个与空值转换相关的函数:NULLIF 和 COALESCE。

    NULLIF(expr1, expr2) 函数接受 2 个参数,如果第一个参数等于第二个参数,返回 NULL;否则,返回第一个参数的值。

    SELECT NULLIF(1, 2), NULLIF(3, 3); -- 1	<null>
    

    因为 1 不等于 2,该查询的第一列结果为 1;因为 3 等于 3,第二列结果为 NULL。

    NULLIF 函数的一个常见用途是防止除零错误:

    SELECT 1 / 0; -- 除零错误
    
    SELECT 1 / NULLIF(0, 0); -- <null>
    

    第一个查询中被除数为 0,出现除零错误(MySQL 可能不会提示错误);第二个查询使用 NULLIF 函数将被除数 0 转换为 NULL,整个结果为 NULL。

    COALESCE:

    COALESCE(expr1, expr2, expr3, ...) 函数接受一个参数列表,并且返回第一个非空的参数;如果所有参数都为空,返回 NULL。

    SELECT COALESCE(type, 'UNKNOWN') FROM staff WHERE type IS NULL;
    /*
    UNKNOWN
    UNKNOWN
    UNKNOWN
    UNKNOWN
    UNKNOWN
     */
    

    专有函数:

    除了SQL 标准中定义的表达式之外,许多数据库还实现了一些类似的扩展函数:

    • Oracle 提供了 NVL(expr1, expr2) 以及 NVL2(expr1, expr2, expr3) 函数
    • MySQL 提供了 IFNULL(expr1, expr2) 以及 IF(expr1, expr2, expr3) 函数
    • SQL Server 提供了 ISNULL(expr1, expr2) 函数

    NVL、IFNULL、ISNULL 它们都相当于只有两个参数的COALESCE,但是 NVL2 和 IF 比较特殊、类似于编程语言中的三元运算符,对于NVL2来说,如果expr1不为空,那么返回expr2的值,否则返回expr3的值;而对于IF也是一样,如果 expr1 不为空、并且为真,那么返回 expr2,否则返回 expr3。

    分组与聚合函数中的空值:

    GROUP BY 子句、DISTINCT 运算符将所有的空值视为相同,因此将它们分为一组。

    我们介绍了常见的聚合函数(AVG、SUM、COUNT 等),聚合函数通常会忽略空值。

    NOT IN 中的空值:

    IN 操作符用于查询位于列表之中的数据,NOT IN 用于查询不位于列表中的数据。

    SELECT * FROM staff WHERE type in ('管理人员', NULL);
    

    该查询是不会返回 type 为 NULL 的记录,原因在于 IN 操作符使用等号(=)依次与列表中的数据进行比较,该查询等价于以下形式:

    SELECT * FROM staff WHERE type = '管理人员' OR type = NULL;
    

    任何值与 NULL 比较的结果都是未知;因此没有数据满足该条件,也就没有返回结果。为了避免各种情况下空值可能带来的问题,可以利用 SQL 提供的空值处理函数将 NULL 值转换为其他数据。

    空值(NULL)是 SQL 中容易被忽略的地方,经常会导致一些不可预知的错误结果。需要注意 SQL 在各个子句和函数中对于空值的处理方式。一个比较通用的解决方法就是利用 COALESCE 函数或者 CASE 表达式将这些空值转换成确定的数据,而且 COALESCE 函数 和 CASE 表达式可以用在各种地方,比如:SELECT、WHERE、GROUP BY、ORDER BY 等等。

  • 相关阅读:
    以链表为载体学习C++(1)
    以链表为载体学习C++(2)
    20191304商苏赫Python程序设计实验一
    Python实验二 20191304商苏赫
    七大基本排序算法之插入排序
    七大基本排序算法之归并排序
    《富爸爸,穷爸爸》
    七大基本排序算法之冒泡排序
    七大基本排序算法之选择排序
    七大基本排序算法之希尔排序
  • 原文地址:https://www.cnblogs.com/traditional/p/11719091.html
Copyright © 2020-2023  润新知