返回NULL,而不是从PostgreSQL的功能复合价值
让我们有以下几点PostgreSQL中:返回NULL,而不是从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
值和作为b
的值的空结构(NULL和NULL对),这是有点期待的。
现在,即使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;
它发出“NOTICE:t”。因此,似乎将NULL
分配给合成值实际上分配了具有所有属性NULL
的合成。考虑到NULL
值存储在表中时(UPDATE tbl SET b = NULL
结果b IS NOT DISTINCT FROM NULL
持有每一行;另一方面,UPDATE tbl SET b = (NULL,NULL)
是false
用于该测试),NULL
值可与(NULL,NULL)
区分。
有必要之一:
返回的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中,一个复合值(SQL标准呼叫这种程度> 1 ”的“值)为NULL
当其所有组件都是NULL
,所以PostgreSQL的正确行为。
ISO/IEC 9075-2:2003,第8章,第7节,sayeth:
8.7 <空谓词>
功能
指定一个测试空值。
格式
<null predicate> ::= <row value predicand> <null predicate part 2> <null predicate part 2> ::= IS [ NOT ] NULL
语法规则
无。
访问规则
无。
通则
1)让[R是<行值的值predicand >。
2)情况:
a)如果- [R是空值,则“ ”
R IS NULL
是真。b)否则:
ⅰ)“ ”
R IS NULL
值是案例:
1)如果每个场的- [R的值是空值,则真。
2)否则,假。
ⅱ)的“ ”
R IS NOT NULL
值是案例:
1)如果没有场的- [R的值是空值,则真。
2)否则,假。
(如果你认为这是疯狂的,你并不孤单。)
您可以验证PostgreSQL处理正确这样的值:
SELECT (ROW(NULL,ROW(NULL,NULL))::tbl).b IS NULL;
?column?
----------
t
(1 row)
据我所知,你宁愿喜欢值为(NULL, NULL)
,但您无法通过PostgreSQL获得该值。我希望这对你来说是一种安慰,但它仍然会正确行事。
感谢您回答。但是,它并不完全解决确切的问题。我已经玩过了,发现'(NULL,NULL)IS NULL'返回'true'。毫无疑问,Postgres的行为也是正确的。原始地,我使用'COALESCE((find_tbl_entry(...)).b,X)',如果'b'属性是'NULL',我希望返回'X'。出乎意料的是,它保持返回'(NULL,NULL)'而不是'X',这是因为'COALESCE'显然使用'IS DISTINCT FROM NULL'来测试返回哪个值。 我想为'COALESCE'写一个函数safe,当'b'返回为'NULL'的时候。 –
...并且,尽管存在实际问题,但我仍对将“NULL”赋予“b”字段并仍然在返回值中获得“(NULL,NULL)”的情况感到好奇。尽管定义了'IS NULL'运算符,这真的很奇怪。 –
是的,这是一团糟。一直在讨论如何改进它,但没有找到好的方向。 –
您是否碰巧知道第一个选项的变体,该变体将防御“tbl”列的修改?我的意思是,当一个额外的列被添加到'tbl'时,函数不会中断 - 特别是当问题只出现在运行时,实际被调用的时候。 –
@OndřejBouda编辑 –
感谢您的编辑,代码似乎并不正确,尽管(在返回表的函数中'RETURN'应该没有参数),并且说到类型,它会返回一个表而不是单个行。然而,我真的明白了这一点。简单地将返回类型定义为一个新的组合类型't'并返回它,使用所提出的结构'return(1,nullif(s,(null,null):: struct));'解决问题。你能否修改编辑后的版本,以便我能接受答案? –