Operator
Opiera się to na sprytnym operatorze @ Daniela .
Będąc przy tym, utwórz kombinację funkcji / operatora, używając typów polimorficznych . Następnie działa dla każdego typu - podobnie jak konstrukcja.
I wykonaj tę funkcję 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
);
Szybkie wyszukiwanie z symbolhoundem było puste, więc operator <!>
nie wydaje się być używany w żadnym module.
Jeśli zamierzasz często korzystać z tego operatora, możesz go trochę dopracować, aby pomóc planerowi zapytań ( jak sugerowany w komentarzu Losthorse ). Na początek możesz dodać klauzule COMMUTATOR
i NEGATOR
, aby wspomóc optymalizator zapytań. Zamień CREATE OPERATOR
z góry na:
CREATE OPERATOR <!> (
PROCEDURE = is_distinct_from(anyelement,anyelement),
LEFTARG = anyelement
, RIGHTARG = anyelement
, COMMUTATOR = <!>
, NEGATOR = =!=
);
I dodaj:
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 = <!>
);
Ale dodatkowe klauzule nie pomogą w danym przypadku użycia, a zwykłe indeksy nadal nie będą używane. Osiągnięcie tego jest znacznie bardziej wyrafinowane. (Nie próbowałem.) Aby uzyskać szczegółowe informacje, przeczytaj rozdział „Informacje dotyczące optymalizacji operatora” w podręczniku.
Przypadek testowy
Przypadek testowy w pytaniu może się powieść tylko wtedy, gdy wszystkie wartości w tablicy są identyczne. Dla tablicy w pytaniu ( '{null,A}'::text[]
) wynik jest zawsze PRAWDA. Czy to jest zamierzone? Dodałem kolejny test dla „IS DISTINCT FROM ALL”:
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
Alternatywa dla standardowych operatorów
foo IS DISTINCT FROM ANY (test_arr) -- illegal syntax
można prawie przetłumaczyć na
foo = ALL (test_arr) IS NOT TRUE
foo = ALL (test_arr)
daje ...
TRUE
.. jeśli wszystkie elementy są foo
FALSE
.. jeśli jakikolwiek NOT NULL
element jest <> foo
NULL
.. jeśli przynajmniej jeden element IS NULL
i żaden element nie jest<> foo
Tak więc pozostały przypadek narożny jest tam, gdzie
- foo IS NULL
- i test_arr
składa się tylko z NULL
elementów.
Jeśli jedno z nich można wykluczyć, to koniec. Dlatego skorzystaj z prostego testu, jeśli
- kolumna jest zdefiniowana NOT NULL
.
- czy ty wiesz, tablica nie jest wszystkie wartości null.
W przeciwnym razie przetestuj dodatkowo:
AND ('A' = ALL(test_arr) IS NOT NULL OR
'B' = ALL(test_arr) IS NOT NULL OR
foo IS NOT NULL)
Gdzie 'A'
i 'B'
mogą być dowolne odrębne wartości. Wyjaśnienie i alternatywy w ramach tego pokrewnego pytania na temat SO:
Czy tablica zawiera wszystkie wartości NULL w PostgreSQL
Ponownie, jeśli znasz dowolną wartość, która nie może istnieć test_arr
, na przykład pusty ciąg ''
, nadal możesz uprościć:
AND ('' = ALL(test_arr) IS NOT NULL OR
foo IS NOT NULL)
Oto pełna matryca testowa do sprawdzenia wszystkich kombinacji:
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
Jest to nieco bardziej szczegółowe niż rozwiązanie AndriyaEXCEPT
, ale jest znacznie szybsze.