PostgreSQL 中的 (x IS NOT NULL) 与 (NOT x IS NULL)

Ani*_*nil 17 postgresql null

为什么x IS NOT NULL不等于NOT x IS NULL

这段代码:

CREATE TABLE bug_test (
    id int,
    name text
);

INSERT INTO bug_test
VALUES (1, NULL);

DO $$
DECLARE
    v_bug_test bug_test;
BEGIN
    RAISE NOTICE '%: %', v_bug_test, (v_bug_test IS NULL);
    RAISE NOTICE '%: %', v_bug_test, (v_bug_test IS NOT NULL);
    RAISE NOTICE '%: %', v_bug_test, (NOT v_bug_test IS NULL);

    SELECT *
    INTO v_bug_test
    FROM bug_test
    WHERE id = 1;

    RAISE NOTICE '%: %', v_bug_test, (v_bug_test IS NULL);
    RAISE NOTICE '%: %', v_bug_test, (v_bug_test IS NOT NULL);
    RAISE NOTICE '%: %', v_bug_test, (NOT v_bug_test IS NULL);
END
$$;

DROP TABLE bug_test;
Run Code Online (Sandbox Code Playgroud)

给出以下输出:

(,): t
(,): f
(,): f
(1,): f
(1,): f ???
(1,): t
Run Code Online (Sandbox Code Playgroud)

虽然我希望得到这个输出:

(,): t
(,): f
(,): f
(1,): f
(1,): t <<<
(1,): t
Run Code Online (Sandbox Code Playgroud)

joa*_*olo 18

您必须区分两种情况:将一个 COLUMN 与 NULL 进行比较,或者将整个 ROW (RECORD) 与 NULL 进行比较。

考虑以下查询:

SELECT
    id, 
    txt, 
    txt     IS NULL AS txt_is_null, 
    NOT txt IS NULL AS not_txt_is_null, 
    txt IS NOT NULL AS txt_is_not_null
FROM
    (VALUES
        (1::integer, NULL::text)
    ) 
    AS x(id, txt) ;
Run Code Online (Sandbox Code Playgroud)

你得到这个:

+----+-----+-------------+-----------------+-----------------+
| id | txt | txt_is_null | not_txt_is_null | txt_is_not_null | 
+----+-----+-------------+-----------------+-----------------+
|  1 |     | t           | f               | f               | 
+----+-----+-------------+-----------------+-----------------+
Run Code Online (Sandbox Code Playgroud)

我想,这就是你和我所期望的。您正在根据 NULL 检查一个 COLUMN,并且您得到“txt IS NOT NULL”和“NOT txt IS NULL”是等效的。

但是,如果您进行不同的检查:

SELECT
    id, 
    txt, 
    x       IS NULL AS x_is_null,
    NOT x   IS NULL AS not_x_is_null,
    x   IS NOT NULL AS x_is_not_null
FROM
    (VALUES
        (1, NULL)
    ) 
    AS x(id, txt) ;
Run Code Online (Sandbox Code Playgroud)

然后你得到

+----+-----+-----------+---------------+---------------+
| id | txt | x_is_null | not_x_is_null | x_is_not_null |
+----+-----+-----------+---------------+---------------+
|  1 |     | f         | t             | f             |
+----+-----+-----------+---------------+---------------+
Run Code Online (Sandbox Code Playgroud)

这可能令人惊讶。一件事看起来合理 (x IS NULL) 和 (NOT x IS NULL) 是相反的。另一件事(“x IS NULL”和“x IS NOT NULL”都不为真),看起来很奇怪。

但是,这是PostgreSQL 文档所说的应该发生的情况:

如果表达式是行值,那么当行表达式本身为空或所有行的字段为空时,IS NULL 为真,而当行表达式本身为非空且所有行的字段都为空时,IS NOT NULL 为真非空。由于这种行为,IS NULL 和 IS NOT NULL 并不总是为行值表达式返回相反的结果;特别是,同时包含空字段和非空字段的行值表达式将为两个测试返回 false。在某些情况下,最好编写 row IS DISTINCT FROM NULL 或 row IS NOT DISTINCT FROM NULL,这将简单地检查整个行值是否为空,而无需对行字段进行任何额外测试。

我必须承认,我认为我从未使用过针对 null 的行值比较,但我想如果存在这种可能性,那么可能会有一些用例。无论如何,我认为这并不常见。