1

假设我们有一个数组:

ARRAY[1,2,3]

使用运算符 <@ 我可以查询左操作数是否是右操作数的子数组:

ARRAY[1, 2] <@ ARRAY[1,2,3]

上面的方法很有效,但现在让我们看下面的例子:

ARRAY[1, 2] <@ ARRAY[1,3,7]

在这种情况下,上面将按预期返回 false。

我们是否有一个操作员说以下内容:

  • 左数组包含在右数组中
  • 或者左数组中的每个元素根本不包含在右数组中?

到今天为止,我可以很容易地想出一个涉及重叠运算符的解决方案,但这并不是我真正想要的。它极大地复杂了我的查询,我需要在应用程序端做一些更复杂的字符串机制来构建查询。

我正在使用 PostgreSQL 13。

4

2 回答 2

1

您可以通过检查子数组匹配或零重叠来表达。

这是真的,因为第一个数组是第二个数组的子数组:

ARRAY[1,2] <@ ARRAY[1,2,3] OR NOT ARRAY[1,2] && ARRAY[1,2,3];

这也是正确的,因为第一个数组不是子数组,但它也有零重叠:

ARRAY[4,5] <@ ARRAY[1,2,3] OR NOT ARRAY[4,5] && ARRAY[1,2,3];
于 2021-01-21T23:47:03.900 回答
1

没有单个操作员可以检查两者(包含不重叠)。
不在 Postgres 13 中,不在任何标准 Postgres 发行版中。

但是您可以轻松创建自己的运算符。我选择了<@!&&明确的操作员名称,因为它不会与任何现有的操作员发生冲突。选择你喜欢的,也许更短一些,因为你的目标是短代码。

CREATE FUNCTION f_array_contained_or_no_overlap(anyarray, anyarray)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE AS
'SELECT $1 <@ $2 OR NOT $1 && $2';

CREATE OPERATOR <@!&& (
  FUNCTION = f_array_contained_or_no_overlap
, LEFTARG  = anyarray
, RIGHTARG = anyarray
);

那么你就可以:

SELECT ARRAY[1,2] <@!&& ARRAY[1,2,7] AS contained     -- true
     , ARRAY[1,2] <@!&& ARRAY[4,5,6] AS no_overlap    -- true
     , ARRAY[1,2] <@!&& ARRAY[4,2,6] AS part_overlap; -- false
包含 no_overlap 部分重叠
F

db<>在这里摆弄

实现您声明的简短代码的目标。
适用于任何数组(任何元素类型),但当然,两个操作数必须兼容。

但 ...

不允许 NULL 元素,因为底层的泛型数组运算符也不允许。

不能使用任何索引。看:

如果你直接使用底层函数f_array_contained_or_no_overlap(anyarray, anyarray)而不是操作符,Postgres 应该可以内联它,这样一个适用的 GIN 索引仍然可以使用。

如果都是整数数组,则可以使用附加模块intarray(也没有内置运算符)来实现更快的实现。

于 2021-01-22T01:26:00.117 回答