2015-12-26 38 views
8

W normalnych warunkach porównanie wartości NULL z dowolną inną wartością daje inną wartość NULL.PostgreSQL 9.4 - Porównywanie wartości NULL

SELECT NULL = NULL; 

Zwraca NULL


trzyma to (w większości) prawdziwe przy porównywaniu dowolnych wierszy, jak wyjaśniono w the documentation, 9.23.5. Row Constructor Comparison:

SELECT ROW(1, NULL, 'baz text') = ROW(1, NULL, 'baz text'); 

Returns NULL


Jednak przy porównywaniu dobrze zdefiniowanych typów kompozytów wartości NULL są traktowane jako równe.

CREATE TYPE test_type AS (
    foo INTEGER, 
    bar BIGINT, 
    baz TEXT 
); 

SELECT (1, NULL, 'baz text')::test_type = (1, NULL, 'baz text')::test_type; 

Zwraca TRUE

Takie zachowanie wydaje się być udokumentowany (Szukałem i znalazłem żadnego odniesienia do zachowań).

Chciałbym użyć tego zachowania, aby zaimplementować niektóre reguły biznesowe i upewnić się, że jest to bezpieczne.

  1. Czy jest to zgodne z dowolną specyfikacją SQL?
  2. Czy to prawdopodobnie to zmieni się w przyszłości?
+0

Dla stabilnych wyników zawsze można użyć: 'SELECT ROW (1, NULL, 'bazowy tekst') NIE ODRĘBNA OD WIERSZA (1, NULL," bazowy tekst "); ' – lad2025

+0

@ lad2025 - Znam klauzulę' IS NOT DISTINCT FROM'; jednak ** Interesuje mnie operator '=', gdy jest używany z typami złożonymi **. – losthorse

+0

To jest interesujące, myślę, że porównuje ciąg znaków ** [Demo] (http://sqlfiddle.com/#!15/9eecb7db59d16c80417c72d1e1f4fbf1/5221/1) **. Więc '{" type ":" typ_testu "," wartość ":" (,,) "} = {" typ ":" typ_testu "," wartość ":" (,,) "}' => true – lad2025

Odpowiedz

2

Znalazłem to w official documentation: [...] w innych kontekstach, gdzie dwie wartości typu composite porównywane są dwa NULL wartości pól są uznawane za równe, a NULL jest uważane za większe niż NIE-NULL. Jest to konieczne, aby zapewnić spójne sortowanie i indeksowanie dla typów kompozytowych.. Myślę, że to rozwiąże twoje pytanie.

0

Ilustracja (przepraszam, nie mogę tego zrobić w komentarzu, trzeba formatowanie):

CREATE TYPE test_type AS (
    foo INTEGER 
    , bar BIGINT 
    , baz TEXT 
    ); 

     -- plain table with three fields 
CREATE TABLE test_table0 (
    foo INTEGER 
    , bar BIGINT 
    , baz TEXT 
    ); 

     -- the same, but with a composite type 
CREATE TABLE test_table1 (
     tt test_type 
     ); 

INSERT INTO test_table0 (foo,bar,baz) 
     VALUES (1, NULL, 'baz text'); 

INSERT INTO test_table1 (tt) 
     VALUES((1, NULL, 'baz text')::test_type) ; 

     -- union needs a "whole row" -compare 
SELECT * FROM test_table0 
UNION 
SELECT * FROM test_table0 
     ; 

     -- union needs a "whole row" -compare 
     -- and type needs a whole "composite" compare 
SELECT * FROM test_table1 
UNION 
SELECT * FROM test_table1 
     ; 

Wynik:

CREATE TYPE 
CREATE TABLE 
CREATE TABLE 
INSERT 0 1 
INSERT 0 1 
foo | bar | baz  
-----+-----+---------- 
    1 |  | baz text 
(1 row) 

     tt   
----------------- 
(1,,"baz text") 
(1 row) 

  • UWAGA: obiekty, które są porównywane (instancje i krotki), nie mają wartości NULL, tylko niektóre z ich elementów.
  • zrobić myśleć jest to zamierzone zachowanie
  • IMHO ten zbliża się do problemu z pozwalając elementów NULL w kluczach kompozytowych (patrz rants przez Chris Data o tym)
  • prawdopodobnie inne zachowanie doprowadziłoby nawet dziwniejszych artefaktów
+0

Widzę, co robisz, ale nie jestem pewien, czy rozumiem, że to implikacje ... czy masz jakiekolwiek informacje, czy to zachowanie powinno być zaufane dzisiaj, a także w przyszłości? – losthorse

+0

Przynajmniej konstrukcja 'UNION' opiera się na porównaniu całych wierszy, nawet jeśli istnieją tam wartości NULL. I owszem: niektórzy ludzie polegają na UNIONach, aby działać tak, jak pokazano. – wildplasser