可以将DISTINCT FROM与ANY或ALL结合吗?


13

是结合一个Postgres的方式IS DISTINCT FROMANY或得到同样结果的其他一些巧妙的方法?

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']);
                                   ^

Answers:


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“数组”中的in,而且还要比较nullin z中的in 。


13

将其视为语法问题,ANY定义为(在行和数组比较中):

表达式运算符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 2014年

这绝对优于我建议的解决方法,尤其是@Erwin的改进。
Andriy M

这个答案和@Erwin的建议调整非常好。我接受安德里(Andriy)的情况,但这只是个人喜好的一个例子:我敢肯定,很多人会喜欢您的优雅。
杰克说请尝试topanswers.xyz 2014年

@JackDouglas:我添加了标准运算符的替代解决方案。
Erwin Brandstetter 2014年

不幸的是……出于所有意图和目的,不IS DISTINCT FROM应该成为操作员吗?似乎仅仅是解析器的技术限制,而不是语义问题。
安迪(Andy)

10

操作员

这是基于@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
);

使用symbolhound进行的快速搜索显示为空,因此<!>似乎没有在任何模块中使用该运算符。

如果您打算大量使用此运算符,则可以充实它,以辅助查询计划程序(例如在注释中建议的losthorse)。对于初学者,您可以添加COMMUTATORNEGATOR子句以辅助查询优化器。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[])中的数组,结果始终为TRUE。那是故意的吗?我为“ 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元素。

如果可以排除其中任何一个,我们就完成了。因此,使用简单的测试
if-定义了column 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的此相关问题的解释和替代方案:
PostgreSQL中的数组是否为所有NULL

同样,如果您知道在中不存在的任何值,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

这比Andriy的EXCEPT解决方案更为冗长,但是速度更快。


在创建时OPERATOR,是否应提供COMMUTATOR(和NEGATOR,也许带有逆IS NOT DISTINCT FROM运算符)子句?postgresql.org/docs/current/static/xoper-optimization.html
losthorse 2015年

1
@losthorse:我添加了一些地址。
Erwin Brandstetter

我正在使用此运算符消除基于app_status(integer)的记录,如下所示app_status <!> any(array[3,6])。不幸的是,它对记录没有任何影响。它适用于整数吗?
哈比卜

@ M.Habib:请问您的问题为新问题。(具有所有相关的详细信息!)您始终可以链接到此链接以获得上下文-并在此处添加评论以链接回去。
Erwin Brandstetter,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.