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


16

为什么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;

给出以下输出:

(,): t
(,): f
(,): f
(1,): f
(1,): f ???
(1,): t

而我希望得到以下输出:

(,): t
(,): f
(,): f
(1,): f
(1,): t <<<
(1,): t

1
您是否正在考虑实际上正在对照NULL检查整个记录的事实。(您是
joanolo 2016年

@joanolo是的。我已经切换了代码以id在我的真实代码库中进行检查,但是只有在花了几个小时搜索问题之后才进行检查。
尼尔,2016年

1
在我看来,rec_variable IS NOT NULL正在检查是否所有列都不为空,而rec_variable IS NULL正在检查是否所有列都为空。因此,NOT rec_variable IS NULL给出了我所期望的-对“里面有东西吗?”这个问题的答案。
尼尔

Answers:


17

您必须区分两种情况:将一个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) ;

你得到这个:

+----+-----+-------------+-----------------+-----------------+
| id | txt | txt_is_null | not_txt_is_null | txt_is_not_null | 
+----+-----+-------------+-----------------+-----------------+
|  1 |     | t           | f               | f               | 
+----+-----+-------------+-----------------+-----------------+

我想这就是您和我所期望的。您正在对照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) ;

然后你得到

+----+-----+-----------+---------------+---------------+
| id | txt | x_is_null | not_x_is_null | x_is_not_null |
+----+-----+-----------+---------------+---------------+
|  1 |     | f         | t             | f             |
+----+-----+-----------+---------------+---------------+

这可能令人惊讶。一件事看起来很合理(x IS NULL)和(NOT x IS NULL)彼此相反。另一件事(“ x IS NULL”和“ x IS NOT NULL”都不成立)看起来很奇怪。

但是,这就是PostgreSQL文档所说的:

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

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


是的,这种解释很有意义,并且与我自发布以来所做的实验结果相符。之所以比较整个记录变量,是因为我的背景是非SQL语言,这种情况很普遍。关于用例,当人们想检查记录变量中的所有字段是否都被填充(rec IS NOT NULL)而不是逐字段进行检查时,这很方便。
尼尔2016年

1
@Anil:正是你所提到的情况下,使用以前弹出:stackoverflow.com/questions/21021102/...
欧文Brandstetter修改
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.