2017-03-06 69 views
2

讓我們有以下幾點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)區分。

回答

0

有必要之一:

返回的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));'解決問題。你能否修改編輯後的版本,以便我能接受答案? –

1

在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

是的,這是一團糟。一直在討論如何改進它,但沒有找到好的方向。 –