返回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,这反过来有一个属性的值作为其属性之一的复合型structb。 (原始问题是更有趣 - 平移函数返回与某些属性相应地翻译一排;所述问题归结为所呈现的代码,虽然)

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) 
+0

您是否碰巧知道第一个选项的变体,该变体将防御“tbl”列的修改?我的意思是,当一个额外的列被添加到'tbl'时,函数不会中断 - 特别是当问题只出现在运行时,实际被调用的时候。 –

+0

@OndřejBouda编辑 –

+0

感谢您的编辑,代码似乎并不正确,尽管(在返回表的函数中'RETURN'应该没有参数),并且说到类型,它会返回一个表而不是单个行。然而,我真的明白了这一点。简单地将返回类型定义为一个新的组合类型't'并返回它,使用所提出的结构'return(1,nullif(s,(null,null):: struct));'解决问题。你能否修改编辑后的版本,以便我能接受答案? –

在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获得该值。我希望这对你来说是一种安慰,但它仍然会正确行事。

+0

感谢您回答。但是,它并不完全解决确切的问题。我已经玩过了,发现'(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'的时候。 –

+0

...并且,尽管存在实际问题,但我仍对将“NULL”赋予“b”字段并仍然在返回值中获得“(NULL,NULL)”的情况感到好奇。尽管定义了'IS NULL'运算符,这真的很奇怪。 –

+0

是的,这是一团糟。一直在讨论如何改进它,但没有找到好的方向。 –