Operator
Ini berdasarkan operator pintar @ Daniel .
Saat berada di sana, buat combo fungsi / operator menggunakan tipe polimorfik . Kemudian ia bekerja untuk semua jenis - seperti konstruksi.
Dan buat fungsinya 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
);
Pencarian cepat dengan symbolhound muncul kosong, sehingga operator <!>
tampaknya tidak digunakan dalam modul apa pun.
Jika Anda akan sering menggunakan operator ini, Anda dapat menyempurnakannya untuk membantu perencana kueri ( seperti saran kuda yang hilang dalam komentar ). Sebagai permulaan, Anda bisa menambahkan COMMUTATOR
dan NEGATOR
klausa untuk membantu pengoptimal kueri. Ganti CREATE OPERATOR
dari atas dengan ini:
CREATE OPERATOR <!> (
PROCEDURE = is_distinct_from(anyelement,anyelement),
LEFTARG = anyelement
, RIGHTARG = anyelement
, COMMUTATOR = <!>
, NEGATOR = =!=
);
Dan tambahkan:
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 = <!>
);
Tetapi klausa tambahan tidak akan membantu dengan kasus penggunaan di tangan dan indeks polos masih tidak akan digunakan. Jauh lebih canggih untuk mencapainya. (Saya belum mencoba.) Baca bab "Informasi Optimalisasi Operator" di manual untuk detailnya.
Kasus cobaan
Kasus uji dalam pertanyaan hanya dapat berhasil jika semua nilai dalam array identik. Untuk larik dalam pertanyaan ( '{null,A}'::text[]
) hasilnya selalu BENAR. Apakah itu dimaksudkan? Saya menambahkan tes lain untuk "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
Alternatif dengan operator standar
foo IS DISTINCT FROM ANY (test_arr) -- illegal syntax
dapat hampir diterjemahkan ke
foo = ALL (test_arr) IS NOT TRUE
foo = ALL (test_arr)
hasil ...
TRUE
.. jika semua elemen adalah foo
FALSE
.. jika ada NOT NULL
elemen <> foo
NULL
.. jika setidaknya satu elemen IS NULL
dan tidak ada elemen<> foo
Jadi, kasing sudut yang tersisa adalah tempat
- foo IS NULL
- dan test_arr
terdiri dari NULL
elemen.
Jika salah satu dari mereka dapat dikesampingkan, kita sudah selesai. Karena itu, gunakan tes sederhana jika
- kolom didefinisikan NOT NULL
.
- atau Anda tahu array tidak pernah semua NULL.
Selain itu, tes tambahan:
AND ('A' = ALL(test_arr) IS NOT NULL OR
'B' = ALL(test_arr) IS NOT NULL OR
foo IS NOT NULL)
Di mana 'A'
dan 'B'
bisa ada nilai yang berbeda. Penjelasan dan alternatif di bawah pertanyaan terkait ini pada SO:
Apakah array semua NULL di PostgreSQL
Sekali lagi, jika Anda tahu tentang nilai apa pun yang tidak dapat ada test_arr
, misalnya string kosong ''
, Anda masih dapat menyederhanakan:
AND ('' = ALL(test_arr) IS NOT NULL OR
foo IS NOT NULL)
Ini adalah matriks tes lengkap untuk memeriksa semua kombinasi:
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
Ini sedikit lebih bertele-tele daripada solusi AndriyEXCEPT
, tetapi jauh lebih cepat.