ผู้ประกอบการ
นี้จะสร้าง@ ประกอบการที่ชาญฉลาดของแดเนียล
ในขณะที่ถูกที่มันสร้างฟังก์ชั่นคำสั่งผสม / ผู้ประกอบการใช้ประเภท polymorphic จากนั้นใช้ได้กับทุกประเภท - เช่นเดียวกับโครงสร้าง และทำให้ฟังก์ชั่น
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ว่างเปล่าดังนั้นผู้ปฏิบัติงาน<!>
จึงไม่ได้ใช้งานในโมดูลใด ๆ
ถ้าคุณจะใช้ดำเนินการนี้เป็นจำนวนมากคุณอาจเนื้อมันออกมาบางมากขึ้นเพื่อช่วยในการวางแผนการสอบถาม ( เช่น 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[]
) ผลลัพธ์จะเป็นจริงเสมอ นั่นตั้งใจหรือไม่ ฉันได้เพิ่มการทดสอบอื่นสำหรับ "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
ทางเลือกกับผู้ประกอบการมาตรฐาน
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
นี่เป็น verbose มากกว่าโซลูชันของ AndriyEXCEPT
แต่มันเร็วกว่ามาก