оператор
Это основано на умном операторе @ Дэниела .
Находясь в этом, создайте комбо функции / оператора, используя полиморфные типы . Тогда это работает для любого типа - так же, как конструкция.
И сделать функцию IMMUTABLE
.
CREATE FUNCTION is_distinct_from(anyelement, anyelement)
RETURNS bool LANGUAGE sql IMMUTABLE AS
'SELECT $1 IS DISTINCT FROM $2';
CREATE OPERATOR <!> (
PROCEDURE = is_distinct_from(anyelement,anyelement),
LEFTARG = anyelement
, RIGHTARG = anyelement
);
Быстрый поиск по Symbolhound оказался пустым, поэтому оператор <!>
, похоже, не используется ни в одном из модулей.
Если вы собираетесь многократно использовать этот оператор, вы можете еще немного его уточнить, чтобы помочь планировщику запросов ( как, например, в примечании ). Для начала, вы можете добавить COMMUTATOR
и NEGATOR
положение , чтобы помочь оптимизатору запросов. Заменить CREATE OPERATOR
сверху на это:
CREATE OPERATOR <!> (
PROCEDURE = is_distinct_from(anyelement,anyelement),
LEFTARG = anyelement
, RIGHTARG = anyelement
, COMMUTATOR = <!>
, NEGATOR = =!=
);
И добавить:
CREATE FUNCTION is_not_distinct_from(anyelement, anyelement)
RETURNS bool LANGUAGE sql IMMUTABLE AS
'SELECT $1 IS NOT DISTINCT FROM $2';
CREATE OPERATOR =!= (
PROCEDURE = is_not_distinct_from(anyelement,anyelement),
LEFTARG = anyelement
, RIGHTARG = anyelement
, COMMUTATOR = =!=
, NEGATOR = <!>
);
Но дополнительные пункты не помогут с вариантом использования под рукой, и простые индексы все равно не будут использоваться. Это гораздо сложнее, чтобы достичь этого. (Я не пробовал.) Подробности читайте в главе «Информация по оптимизации оператора» в руководстве.
Прецедент
Тестовый пример в вопросе может быть успешным, только если все значения в массиве идентичны. Для массива в question ( '{null,A}'::text[]
) результат всегда TRUE. Это предназначено? Я добавил еще один тест для "ОТЛИЧАЕТСЯ ОТ ВСЕХ":
SELECT foo
, foo <!> ANY ('{null,A}'::text[]) AS chk_any
, foo <!> ALL ('{null,A}'::text[]) AS chk_all
FROM (
VALUES ('A'),('Z'),(NULL)
) z(foo)
foo | chk_any | chk_all
-----+---------+---------
A | t | f
Z | t | t
| t | f
Альтернатива со стандартными операторами
foo IS DISTINCT FROM ANY (test_arr) -- illegal syntax
можно почти перевести на
foo = ALL (test_arr) IS NOT TRUE
foo = ALL (test_arr)
дает ...
TRUE
.. если все элементы foo
FALSE
.. если любой NOT NULL
элемент есть <> foo
NULL
.. если хотя бы один элемент IS NULL
и нет элемента<> foo
Таким образом, оставшийся угол случай , где
- foo IS NULL
- и test_arr
состоит из ничего , кроме NULL
элементов.
Если любой из них может быть исключен, мы сделали. Поэтому используйте простой тест if
- столбец определен NOT NULL
.
- или вы знаете, что массив никогда не бывает пустым.
Остальное, тест дополнительно:
AND ('A' = ALL(test_arr) IS NOT NULL OR
'B' = ALL(test_arr) IS NOT NULL OR
foo IS NOT NULL)
Где 'A'
и 'B'
могут быть какие-то разные значения. Объяснение и альтернативы в этом связанном вопросе о SO:
массив всех NULL в PostgreSQL
Опять же, если вы знаете о каком-либо значении, которое не может существовать test_arr
, например, в пустой строке ''
, вы все равно можете упростить:
AND ('' = ALL(test_arr) IS NOT NULL OR
foo IS NOT NULL)
Вот полная тестовая матрица для проверки всех комбинаций:
SELECT foo, test_arr
, foo = ALL(test_arr) IS NOT TRUE AS test_simple
, foo = ALL(test_arr) IS NOT TRUE
AND ('A' = ALL(test_arr) IS NOT NULL OR
'B' = ALL(test_arr) IS NOT NULL OR
foo IS NOT NULL) AS test_sure
FROM (
VALUES ('A'),('Z'),(NULL)
) v(foo)
CROSS JOIN (
VALUES ('{null,A}'::text[]),('{A,A}'),('{null,null}')
) t(test_arr)
foo | test_arr | test_simple | test_sure
-----+-------------+-------------+-----------
A | {NULL,A} | t | t
A | {A,A} | f | f -- only TRUE case
A | {NULL,NULL} | t | t
Z | {NULL,A} | t | t
Z | {A,A} | t | t
Z | {NULL,NULL} | t | t
| {NULL,A} | t | t
| {A,A} | t | t
| {NULL,NULL} | t | f -- special case
Это немного более многословно, чем решение АндреяEXCEPT
, но оно значительно быстрее.