Оператор
Це спирається на розумного оператора @ Daniel .
Перебуваючи в ньому, створіть функцію / оператор комбо, використовуючи поліморфні типи . Тоді він працює для будь-якого типу - як і конструкція.
І зробити функцію 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
);
Швидкий пошук з символікою виявився порожнім, тому оператор, <!>
здається, не використовується в жодному з модулів.
Якщо ви збираєтеся використовувати цей оператор багато, ви можете сформувати його ще для допомоги планувальнику запитів ( наприклад, Losthorse, запропонованому в коментарі ). Для початку ви можете додати пункти 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 = <!>
);
Але додаткові пропозиції не допоможуть у випадку використання, а звичайні індекси все ще не використовуються. Домогтися цього набагато складніше. (Я не намагався.) Докладніше прочитайте розділ "Інформація про оптимізацію оператора" .
Тестовий випадок
Тестовий випадок у питанні може досягти успіху лише за умови, що всі значення масиву однакові. Для масиву у питанні ( '{null,A}'::text[]
) результат завжди є ІСТИНИМ. Це призначено? Я додав ще один тест для "ВІДДАЛЕ ВСІХ":
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
елементів.
Якщо будь-кого можна виключити, ми закінчили. Тому використовуйте простий тест, якщо
- стовпець визначений NOT NULL
.
- або ви знаєте, що масив ніколи не є всім 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
, але істотно швидше.