操作员
这是基于@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)。对于初学者,您可以添加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[]
)中的数组,结果始终为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
解决方案更为冗长,但是速度更快。