PostgreSQL 9.4-сравнение нулевых значений


При нормальных условиях сравнение значения NULL с любым другим значением приводит к другому значению NULL.

SELECT NULL = NULL;

Возвращает NULL


Это справедливо (в основном) при сравнении произвольных строк, как описано в документации, 9.23.5. Сравнение Конструктора Строк :

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

Возвращает NULL


Однако при сравнении хорошо определенных составных типов значения NULL рассматриваются как равные.
CREATE TYPE test_type AS (
    foo INTEGER,
    bar BIGINT,
    baz TEXT
);

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

Возвращает TRUE

Это поведение кажется недокументированным (я посмотрел и не нашел ссылки на это поведение).

Я хотел бы использовать это поведение для реализации некоторых бизнес-правил и хочу убедиться, что это безопасно.

  1. согласуется ли это с какой-либо спецификацией SQL?
  2. вероятно ли , что это изменится в будущем?
2 8

2 ответа:

Я нашел это в официальной документации : [...] В других контекстах, где сравниваются два значения составного типа, два значения нулевого поля считаются равными, а нулевое значение считается большим, чем ненулевое. Это необходимо для того, чтобы иметь согласованное поведение сортировки и индексирования для составных типов.. Я думаю, что это решает ваш вопрос.

Иллюстрация (извините, я не могу сделать это в комментарии, нужно форматирование):

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
        ;

Результат:

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)

  • Примечание: сравниваемые объекты (экземпляры типов и кортежи) не являются нулевыми, только некоторые из их элементов.
  • я действительно думаю, что это и есть предполагаемое поведение
  • ИМХО это близко к проблеме с допуском нулевых элементов в составных ключах, (смотрите разглагольствования Криса дэйта об этом)
  • вероятно, другой поведение привело бы к еще более странным артефактам