IS DISTINCT FROM 可以以某种方式与 ANY 或 ALL 结合吗?

Jac*_*las 14 postgresql null postgresql-9.3

是结合一个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']);
                                   ^
Run Code Online (Sandbox Code Playgroud)

Dan*_*ité 15

将其视为语法问题,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
);
Run Code Online (Sandbox Code Playgroud)

然后它可以先于ANY

select count(*)
from (select 'A' foo union all select 'Z' union all select null) z
where foo <!> any(array[null, 'A']);  
Run Code Online (Sandbox Code Playgroud)
 数数 
-------
     3
(1 行)

  • 优秀、富有洞察力的答案。 (2认同)
  • 不幸的是……无论出于何种意图和目的,“IS DISTINCT FROM”不应该是一个运算符吗?看起来只是解析器的技术限制而不是语义问题。 (2认同)

Erw*_*ter 12

操作员

这是建立在@Daniel 聪明的 operator之上
在此过程中,使用多态类型创建函数/运算符组合。然后它适用于任何类型 - 就像构造一样。
并制作功能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
);
Run Code Online (Sandbox Code Playgroud)

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

如果您打算大量使用此运算符,则可以将其充实一些以协助查询规划器(如评论中建议的 losthorse)。对于初学者,您可以添加COMMUTATORandNEGATOR子句来帮助查询优化器。CREATE OPERATOR从上面替换为:

CREATE OPERATOR <!> (
  PROCEDURE = is_distinct_from(anyelement,anyelement),
  LEFTARG  = anyelement
, RIGHTARG = anyelement
, COMMUTATOR = <!>
, NEGATOR = =!=
);
Run Code Online (Sandbox Code Playgroud)

并添加:

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 = <!>
);
Run Code Online (Sandbox Code Playgroud)

但是附加子句对手头的用例没有帮助,并且仍然不会使用普通索引。实现这一目标要复杂得多。(我没试过。)详细阅读手册中的“操作员优化信息”一章。

测试用例

问题中的测试用例只有在数组中的所有值都相同时才能成功。对于问题 ( '{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
Run Code Online (Sandbox Code Playgroud)

标准运算符的替代

foo IS DISTINCT FROM ANY (test_arr) -- illegal syntax
Run Code Online (Sandbox Code Playgroud)

几乎被翻译成

foo = ALL (test_arr) IS NOT TRUE
Run Code Online (Sandbox Code Playgroud)

foo = ALL (test_arr) 产生...

TRUE .. 如果所有元素都是foo
FALSE.. 如果有任何NOT NULL元素是<> foo
NULL .. 如果至少有一个元素IS NULL并且没有元素是<> foo

所以,剩下的极端情况是
- foo IS NULL
- test_arrNULL元素组成。

如果可以排除任何一个,我们就完成了。因此,请使用简单测试 if
- the column is defined NOT NULL
-或者知道数组永远不会全是 NULL。

否则,额外测试:

AND ('A' = ALL(test_arr) IS NOT NULL OR 
     'B' = ALL(test_arr) IS NOT NULL OR
     foo IS NOT NULL)
Run Code Online (Sandbox Code Playgroud)

其中'A''B'可以是任何不同的值。这个相关问题下的解释和替代方案:
Is array all NULLs in PostgreSQL

同样,如果您知道中不存在的任何值,test_arr例如空字符串'',您仍然可以简化:

AND ('' = ALL(test_arr) IS NOT NULL OR
     foo IS NOT NULL)
Run Code Online (Sandbox Code Playgroud)

这是一个完整的测试矩阵,用于检查所有组合:

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
Run Code Online (Sandbox Code Playgroud)

这比Andriy 的EXCEPT解决方案更冗长,但速度要快得多。


And*_*y M 8

也许像这样

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
Run Code Online (Sandbox Code Playgroud)

请注意,不仅null在“数组”中,而且还在以这种方式比较nullin z