Jac*_*las 14 postgresql null postgresql-9.3
是结合一个Postgres的方式IS DISTINCT FROM
与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']);
^
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 行)
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)。对于初学者,您可以添加COMMUTATOR
andNEGATOR
子句来帮助查询优化器。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_arr
由NULL
元素组成。
如果可以排除任何一个,我们就完成了。因此,请使用简单测试 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
解决方案更冗长,但速度要快得多。
也许像这样:
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
在“数组”中,而且还在以这种方式比较null
in z
。
归档时间: |
|
查看次数: |
1843 次 |
最近记录: |