2017-03-06 3 views
2

uns folgende Dinge in PostgreSQL Lassen Sie haben:Return NULL anstelle von Composite-Wert von PostgreSQL Funktion

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; 

Das heißt, wir haben eine Funktion einen Wert des zusammengesetzten Typ Rückkehr tbl, was wiederum ein Attribut hat b des zusammengesetzten Typs struct als eines seiner Attribute. (Das ursprüngliche Problem ist interessanter - eine Übersetzungsfunktion eine Zeile mit einigen Attributen übersetzt entsprechend zurückkehrt;. Das Problem läuft darauf hinaus, den präsentierten Code nach unten, obwohl)

SELECT find_tbl_entry(FALSE) Ergebnisse in (,(,)), dh NULL als Wert a und eine leere Struktur (ein Paar von NULL und NULL) als der Wert von b, die etwas erwartet wird. Jetzt

, auch SELECT find_tbl_entry(TRUE) Ergebnisse in (,(,)), das heißt, auch wenn das b Attribut explizit auf NULL gesetzt ist, ist das Ergebnis nicht NULL, aber es ist immer noch die leere Struktur.

Was kann ich tun, damit die Funktion find_tbl_entryNULL im Attribut b zurückgibt?


EDIT: Wie sich herausstellt, das Merkwürdige ist, die Zuordnung k.b := NULL. Wenn die Funktion erweitert wird:

k.b := NULL; 
RAISE NOTICE '%', k.b IS DISTINCT FROM NULL; 

es emittiert "HINWEIS: t". Daher scheint es, dass das Zuweisen von NULL zu einem zusammengesetzten Wert tatsächlich einem Verbund mit allen Attributen NULL zuweist. Das ist ziemlich merkwürdig, wenn man bedenkt, dass NULL Werte von (NULL,NULL) unterscheidbar sind, wenn sie in einer Tabelle gespeichert sind (UPDATE tbl SET b = NULL ergibt b IS NOT DISTINCT FROM NULL Halten für jede Reihe; andererseits ist UPDATE tbl SET b = (NULL,NULL)false für diesen Test).

Antwort

0

Es ist notwendig, entweder:

Rückkehr der null direkt in anstatt die zugeordnete Variable Rückkehr:

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 

oder bei Funktionsnutzungszeit vergleichen:

select coalesce(nullif((find_tbl_entry()).b, (null,null)::struct), (1,2)::struct); 
coalesce 
---------- 
(1,2) 
+0

Kennen Sie zufällig eine Variante der ersten Option, die gegen Modifikationen von 'tbl'-Spalten defensiv wäre?Ich meine, wenn eine zusätzliche Spalte zu "tbl" hinzugefügt wird, wäre es schön, wenn die Funktion nicht bricht - besonders wenn das Problem nur zur Laufzeit auftritt, wenn die Funktion tatsächlich aufgerufen wird. –

+0

@ OndřejBouda Bearbeitet –

+0

Danke für die Bearbeitung, der Code scheint jedoch nicht korrekt zu sein ('RETURN' sollte keinen Parameter in einer Funktion haben, die eine Tabelle zurückgibt), und spricht von Typen, gibt es eine Tabelle und nicht eine einzelne Zeile zurück. Ich verstehe den Punkt jedoch. Definieren Sie einfach den Rückgabetyp als neuen zusammengesetzten Typ "t" und geben Sie ihn unter Verwendung des vorgeschlagenen Konstrukts return (1, nullif (s, (null, null) :: struct)) zurück; 'löst das Problem. Könnten Sie bitte die bearbeitete Version korrigieren, damit ich die Antwort akzeptieren kann? –

1

In SQL ein Verbundwert (die SQL-Standard nennt dies einen Wert von “ Graden> 1 ”) ist NULL wenn alle seine Komponenten NULL sind, so PostgreSQL korrekt verhält.

ISO/IEC 9075-2: 2003, Kapitel 8, Vers 7, spricht:

8,7 < null Prädikat >

Funktion

Geben Sie einen Test für eine Nullwert

Format

<null predicate> ::= <row value predicand> <null predicate part 2> 
<null predicate part 2> ::= IS [ NOT ] NULL 

Syntaxregeln

Keine.

Zugriffsregeln

Keine.

Allgemeine Regeln

1) Lassen Sie R der Wert der < Zeilenwert sein > predicand.

2) Gehäuse:

        a) Wenn R der Nullwert ist, dann “ R IS NULL ” ist Wahre.

        b) Ansonsten:

                i) Der Wert von “ R IS NULL ” ist

                        Fall:

                        1) Wenn der Wert jedes Feld in R der Nullwert ist, dann Wahr.

                        2) Andernfalls Falsch.

                ii) Der Wert des “ R IS NOT NULL ” ist

                        Fall:

                        1) Wenn der Wert kein Feld in R den Nullwert ist, dann Wahr.

                        2) Andernfalls Falsch.

(Wenn Sie glauben, dass dies verrückt ist, sind Sie nicht allein.)

Sie können überprüfen, ob PostgreSQL so richtig einen Wert behandelt:

SELECT (ROW(NULL,ROW(NULL,NULL))::tbl).b IS NULL; 
?column? 
---------- 
t 
(1 row) 

Ich verstehe, dass würden Sie eher wie der Wert zu lesen (NULL, NULL), aber Sie können das nicht mit PostgreSQL bekommen. Ich hoffe, es ist ein Trost für Sie, dass es sich dennoch korrekt verhält.

+0

Danke für Ihr Antworten. Es löst jedoch nicht genau die genaue Frage. Ich habe bereits damit gespielt und herausgefunden, dass '(NULL, NULL) IS NULL' 'true' zurückgibt. Kein Zweifel, auch Postgres verhält sich korrekt. Ursprünglich habe ich 'COALESCE ((find_tbl_entry (...)). B, X)' verwendet, wo ich 'X' zurückgeben wollte, wenn das' b' Attribut 'NULL' war. Unerwarteterweise gab es '(NULL, NULL)' anstelle von 'X' zurück, weil' COALESCE' anscheinend 'IS DISTINCT FROM NULL' verwendet, um zu testen, welcher Wert zurückgegeben werden soll. Ich möchte eine Funktion Safe für 'COALESCE' schreiben, die sein wird, wenn' b' als 'NULL' zurückgegeben wird. –

+0

... und trotz der praktischen Probleme, war ich auch neugierig auf den Fall, wenn ich 'NULL' dem' b'-Feld zuordne und immer noch '(NULL, NULL)' im zurückgegebenen Wert bekomme. Was trotz der Definition des 'IS NULL'-Operators wirklich seltsam erscheint. –

+0

Ja, es ist ein Durcheinander. Es gab Diskussionen darüber, wie man es verbessern könnte, aber es wurde keine gute Richtung gefunden. –

Verwandte Themen