สามารถแยกความแตกต่างจากการรวมเข้ากับอะไรก็ได้หรือไม่?


13

เป็นวิธีการรวมIS DISTINCT FROMกับpostgres ANYหรือวิธีอื่น ๆ ที่เป็นระเบียบเพื่อให้ได้ผลลัพธ์เดียวกันหรือไม่?

select count(*)
from (select 'A' foo union all select 'Z' union all select null) z
where foo <> any(array[null, 'A']);

 count
-------
     1
(1 row)

select count(*)
from (select 'A' foo union all select 'Z' union all select null) z
where foo is distinct from any(array[null, 'A']);  

ERROR:  syntax error at or near "any"
LINE 3: where foo is distinct from any(array[null, 'A']);
                                   ^

คำตอบ:


7

อาจจะเป็นเช่นนี้ :

select foo
     , exists (values (null), ('A') except select foo) chk_any
     , not exists (values (null), ('A') intersect select foo) chk_all
from ( values ('A'),('Z'),(null) ) z(foo);

 foo | chk_any | chk_all
-----+---------+---------
 A   | t       | f
 Z   | t       | t
     | t       | f

โปรดทราบว่าไม่เพียง แต่nullใน "อาร์เรย์" แต่ยังnullในzจะถูกเมื่อเทียบกับวิธีนี้


13

มองว่ามันเป็นปัญหาไวยากรณ์ANYถูกกำหนดเป็น (ในการเปรียบเทียบแถวและอาร์เรย์ ):

ผู้ประกอบการแสดงออกใด ๆ (การแสดงออกอาร์เรย์)

แต่is distinct fromไม่ใช่ผู้ประกอบการมันเป็น "สร้าง" ตามที่เราบอกไว้ในตัวดำเนินการเปรียบเทียบ :

เมื่อพฤติกรรมนี้ไม่เหมาะสมให้ใช้ IS [NOT] DISTINCT FROM จาก โครงสร้าง

เนื่องจาก PostgreSQL มีตัวดำเนินการที่ผู้ใช้กำหนดเราอาจกำหนดตัวดำเนินการ / ฟังก์ชันคอมโบสำหรับวัตถุประสงค์นี้:

create function is_distinct_from(text, text) returns bool as 
'select $1 is distinct from $2;' language sql;

create operator <!> (
 procedure=is_distinct_from(text,text),
 leftarg=text, rightarg=text
);

จากนั้นสามารถนำหน้าANY:

select count(*)
from (select 'A' foo union all select 'Z' union all select null) z
where foo <!> any(array[null, 'A']);  
 นับ 
-------
     3
(1 แถว)

1
คำตอบที่ยอดเยี่ยมและลึกซึ้ง
Erwin Brandstetter

นี่เป็นวิธีที่เหนือกว่าวิธีแก้ปัญหาที่ฉันแนะนำโดยเฉพาะกับการปรับปรุงของ @ Erwin
Andriy M

คำตอบนี้และการปรับแต่งที่ @ Erwin แนะนำนั้นยอดเยี่ยมจริงๆ ฉันยอมรับของ Andriy แต่เป็นเพียงกรณีของการตั้งค่าส่วนตัว: ฉันแน่ใจว่าหลายคนจะชอบความสง่างามของคุณ
แจ็คบอกว่าลอง topanswers.xyz

@JackDouglas: ฉันเพิ่มโซลูชันทางเลือกด้วยตัวดำเนินการมาตรฐาน
Erwin Brandstetter

นั่นเป็นเรื่องโชคร้าย ... สำหรับเจตจำนงและจุดประสงค์ทั้งหมดไม่ควรIS DISTINCT FROMเป็นผู้ดำเนินการหรือ ดูเหมือนว่าเป็นเพียงข้อ จำกัด ทางเทคนิคของ parser มากกว่าปัญหาความหมาย
Andy

10

ผู้ประกอบการ

นี้จะสร้าง@ ประกอบการที่ชาญฉลาดของแดเนียล
ในขณะที่ถูกที่มันสร้างฟังก์ชั่นคำสั่งผสม / ผู้ประกอบการใช้ประเภท 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แต่มันเร็วกว่ามาก


เมื่อสร้างส่วนOPERATORคำสั่งCOMMUTATOR(และNEGATORอาจจะมีกับตัวIS NOT DISTINCT FROMดำเนินการผกผัน) ควรให้มาหรือไม่ postgresql.org/docs/current/static/xoper-optimization.html
losthorse

1
@losthorse: ฉันได้เพิ่มที่อยู่เล็กน้อย
Erwin Brandstetter

ฉันใช้ดำเนินการนี้จะกำจัดการบันทึกอยู่บนพื้นฐานของ app_status (จำนวนเต็ม) app_status <!> any(array[3,6])เช่นนี้ น่าเสียดายที่มันไม่มีผลกระทบใด ๆ กับบันทึก มันทำงานกับจำนวนเต็ม?
M. Habib

@ M.Habib: กรุณาถามคำถามของคุณใหม่คำถาม (พร้อมรายละเอียดที่เกี่ยวข้องทั้งหมด!) คุณสามารถเชื่อมโยงกับบริบทนี้ได้ตลอดเวลาและวางความคิดเห็นที่นี่เพื่อลิงค์กลับ
Erwin Brandstetter
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.