让我们在PostgreSQL中拥有以下东西:
CREATE TYPE struct AS (x INT, y INT);
CREATE TABLE tbl (a INT, b struct);
CREATE FUNCTION find_tbl_entry(clear BOOL) RETURNS tbl AS $$
DECLARE k tbl;
BEGIN
IF clear THEN
k.b := NULL;
END IF;
RETURN k;
END;
$$ LANGUAGE plpgsql;
也就是说,我们有一个函数返回复合类型的值tbl
,而复合类型struct
的属性b
又作为其属性之一。(原始问题更有趣 - 一个翻译函数返回一行,并相应地翻译了一些属性;不过,问题归结为呈现的代码。
SELECT find_tbl_entry(FALSE)
导致(,(,))
,即 NULL 作为a
的值和一个空结构(NULL 和 NULL 的一对)作为b
的值,这在某种程度上是意料之中的。
现在,即使SELECT find_tbl_entry(TRUE)
结果是(,(,))
,即即使b
属性显式设置为NULL
,结果也不是NULL
,但它仍然是空结构。
如何让函数find_tbl_entry
在b
属性中返回NULL
?
编辑:事实证明,奇怪的是分配k.b := NULL
。扩展函数时:
k.b := NULL;
RAISE NOTICE '%', k.b IS DISTINCT FROM NULL;
它发出"注意:t"。因此,似乎将NULL
分配给复合值实际上分配了具有所有属性NULL
的复合。考虑到NULL
值与存储在表中时(NULL,NULL)
值是可区分的,这很奇怪(UPDATE tbl SET b = NULL
导致每一行b IS NOT DISTINCT FROM NULL
保持;另一方面,UPDATE tbl SET b = (NULL,NULL)
对于该测试false
)。
有必要:
直接返回null
,而不是返回分配的变量:
create or replace function find_tbl_entry()
returns tbl as $$
declare s struct;
begin
s := null;
return (1, nullif(s, (null,null)::struct));
end;
$$ language plpgsql;
select a, b, b is null from find_tbl_entry();
a | b | ?column?
---+---+----------
1 | | t
或者在函数使用时进行比较:
select coalesce(nullif((find_tbl_entry()).b, (null,null)::struct), (1,2)::struct);
coalesce
----------
(1,2)
在SQL中,当复合值的所有组件都NULL
时,复合值(SQL标准称之为"度>值1")是NULL
的,所以PostgreSQL的行为是正确的。
ISO/IEC 9075-2:2003,第8章,第7节,说:
8.7<空谓词>空谓词>
功能
为空值指定测试。
格式
<null predicate> ::= <row value predicand> <null predicate part 2> <null predicate part 2> ::= IS [ NOT ] NULL
语法规则
没有。
访问规则
没有。
一般规则
1) 设R为<行值谓词>的值。行值谓词>
2)案例:
一)如果R是空值,则 "
RIS NULL
" 为True。b)否则:
一)"
RIS NULL
"的值是箱:
1)如果R中每个字段的值都是空值,则为 True。
2)否则,为假。
二)"
RIS NOT NULL
"的值为箱:
1)如果R中没有字段的值为空值,则为 True。
2)否则,为假。
(如果你认为这很疯狂,你并不孤单。
您可以验证 PostgreSQL 是否正确处理了这样的值:
SELECT (ROW(NULL,ROW(NULL,NULL))::tbl).b IS NULL;
?column?
----------
t
(1 row)
我知道你宁愿希望值读取(NULL, NULL)
,但你不能用PostgreSQL得到它。我希望它仍然会正确运行,这对您来说是一种安慰。