比较数组是否相等,忽略元素的顺序

use*_*987 19 arrays postgresql

我有一个包含4个数组列的表..结果如下:

ids       signed_ids   new_ids   new_ids_signed
{1,2,3} | {2,1,3}    | {4,5,6} | {6,5,4}
Run Code Online (Sandbox Code Playgroud)

无论如何要通过忽略元素的顺序来比较ids并使signed_ids它们相等?

Sel*_*maz 18

您可以使用运算符包含:

(array1 <@ array2和array1 @> array2)

  • 警告!此比较仅在所有元素都是唯一的情况下才有效:`select ARRAY[1,2] &lt;@ ARRAY[1,2,2], ARRAY[1,2] @&gt; ARRAY[1,2,2];` 将返回两个条件都为“true”,但数组不匹配(并且您不能只添加长度检查,因为那样您将无法区分“[1,2,2]”和“[1,1, 2]` (4认同)
  • 作为postgres数组的新手,这似乎是最好的答案。与上述相比,它有哪些缺点? (2认同)
  • @JasonAxelson,这使得它成为 `array_compare_as_set` 的一个很好的实现,您希望它的行为与您所描述的完全一样:) (2认同)

Erw*_*ter 13

处理整数数组,您可以安装扩展名intarray.

使用(在Postgres 9.1或更高版本中)为每个数据库安装一次:

CREATE EXTENSION intarray;
Run Code Online (Sandbox Code Playgroud)

然后你可以:

SELECT uniq(sort(ids)) = uniq(sort(signed_ids));
Run Code Online (Sandbox Code Playgroud)

要么:

SELECT ids @> signed_ids AND ids <@ signed_ids;
Run Code Online (Sandbox Code Playgroud)

大胆强调来自intarray的功能和操作员.两个表达式都将忽略元素的顺序和重复性.更多有关这些功能和运营商在有帮助的手册在这里.

笔记:

  • intarray运营商只对阵列工作integer,而不是bigintsmallint或任何其他数据类型.
  • 您可以使用包含运算符@><@无需安装,intarray因为标准Postgres分发中的数组类型有通用变体.仅intarray安装专用运算符int[],通常更快.
  • 与泛型运算符不同,intarray它们不接受数组中的NULL值,这可能会令人困惑:如果在任何涉及的数组中有NULL,则现在会收到错误消息.
    如果需要使用NULL值,则可以通过使用构造对运算进行模式限定来默认使用标准的泛型运算OPERATOR:

    SELECT ARRAY[1,4,null,3]::int[] OPERATOR(pg_catalog.@>) ARRAY[3,1]::int[]
    Run Code Online (Sandbox Code Playgroud)

    有关:

  • 泛型运算符不能将索引与intarray运算符类一起使用,反之亦然.


Cra*_*ger 11

最简单的方法是对它们进行排序并对它们进行排序.请参阅PostgreSQL中的排序数组.

给出样本数据:

CREATE TABLE aa(ids integer[], signed_ids integer[]);
INSERT INTO aa(ids, signed_ids) VALUES (ARRAY[1,2,3], ARRAY[2,1,3]);
Run Code Online (Sandbox Code Playgroud)

最好的办法是,如果数组条目总是整数,则使用intarray扩展,正如Erwin在他的回答中解释的那样.这是一个很多比任何纯SQL配方更快.

否则,对于适用于任何数据类型的通用版本,请定义array_sort(anyarray):

CREATE OR REPLACE FUNCTION array_sort(anyarray) RETURNS anyarray AS $$
SELECT array_agg(x order by x) FROM unnest($1) x;
$$ LANGUAGE 'SQL';
Run Code Online (Sandbox Code Playgroud)

并使用它排序和比较排序的数组:

SELECT array_sort(ids) = array_sort(signed_ids) FROM aa;
Run Code Online (Sandbox Code Playgroud)

有一个重要的警告:

SELECT array_sort( ARRAY[1,2,2,4,4] ) = array_sort( ARRAY[1,2,4] );
Run Code Online (Sandbox Code Playgroud)

将是假的.根据您的意图,这可能是您想要的,也可能不是.


或者,定义一个函数array_compare_as_set:

CREATE OR REPLACE FUNCTION array_compare_as_set(anyarray,anyarray) RETURNS boolean AS $$
SELECT CASE
  WHEN array_dims($1) <> array_dims($2) THEN
    'f'
  WHEN array_length($1,1) <> array_length($2,1) THEN
    'f'
  ELSE
    NOT EXISTS (
        SELECT 1
        FROM unnest($1) a 
        FULL JOIN unnest($2) b ON (a=b) 
        WHERE a IS NULL or b IS NULL
    )
  END
$$ LANGUAGE 'SQL' IMMUTABLE;
Run Code Online (Sandbox Code Playgroud)

然后:

SELECT array_compare_as_set(ids, signed_ids) FROM aa;
Run Code Online (Sandbox Code Playgroud)

这与比较两个array_sorted值略有不同.array_compare_as_set将消除重复,成为array_compare_as_set(ARRAY[1,2,3,3],ARRAY[1,2,3])真,而array_sort(ARRAY[1,2,3,3]) = array_sort(ARRAY[1,2,3])将是假的.

这两种方法都会有相当糟糕的表现.考虑确保始终将数组存储在首位.