2012-10-13 89 views
11

我有一个表4个阵列列的顺序..结果是这样的:比较是否相等阵列,忽略元件

ids  signed_ids new_ids new_ids_signed 
{1,2,3} | {2,1,3} | {4,5,6} | {6,5,4} 

反正比较idssigned_ids使得它们出来相等,通过忽略元素的顺序?

+1

这些阵列是不相等的。我猜对了,你真正的问题是“如何比较两个PostgreSQL数组,就好像它们是集合一样,即不考虑顺序?” –

+0

是的,这正是我要求的:) – user766987

+0

为了反映意图而编辑的问题。 –

回答

9

最简单的事情就是对它们进行排序并对它们进行排序。见sorting arrays in PostgreSQL

给定的样本数据:

CREATE TABLE aa(ids integer[], signed_ids integer[]); 
INSERT INTO aa(ids, signed_ids) VALUES (ARRAY[1,2,3], ARRAY[2,1,3]); 

做的最好的事情是,如果数组条目总是整数是使用intarray扩展,如Erwin explains in his answer。这是一个lot比任何纯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'; 

,并用它进行排序和比较排序的数组:

SELECT array_sort(ids) = array_sort(signed_ids) FROM aa; 

有一个重要的警告:

SELECT array_sort(ARRAY[1,2,2,4,4]) = array_sort(ARRAY[1,2,4]); 

将是错误的。这可能是也可能不是你想要的,取决于你的意图。


或者,定义一个函数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; 

然后:

SELECT array_compare_as_set(ids, signed_ids) FROM aa; 

这是来自比较两个array_sort ED值微妙的不同。 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])将为假。

这两种方法都会有很差的表现。考虑确保您始终将您的数组存储在首位。

+0

为你的餐桌添加一个别名,精彩!谢谢:) – user766987

+0

@ user766987更新了更好的定义 –

+0

@ user766987 ...并用不同的方法再次更新,只是为了好玩。 –

12

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

与(从Postgres 9.1或更高版本)每个数据库安装一次:

CREATE EXTENSION intarray; 

那么你可以:

SELECT uniq(sort(ids)) = uniq(sort(signed_ids));

或者:

SELECT ids @> signed_ids AND ids <@ signed_ids;

粗体强调功能和来自intarray的运营商。 这两个表达式都将忽略元素的顺序和重复性。更多关于这些功能和操作的帮助手册here

注:

  • intarray运营商只为integer,不bigintsmallint或任何其他数据类型阵列的工作。
  • 由于标准Postgres发行版中存在数组类型的通用变体,因此您可以使用遏制运算符@><@而不安装intarrayintarray只为int[]安装专门的操作员,通常速度更快。
  • 与泛型操作符不同,intarray不接受数组中的NULL值,这可能会造成混淆:现在,如果在任何涉及的数组中都有NULL,则会收到错误消息。
    如果需要使用NULL值的工作,你可以默认为标准,一般运营商通过架构进行资格与OPERATOR构建运营商:

    SELECT ARRAY[1,4,null,3]::int[] OPERATOR([email protected]>) ARRAY[3,1]::int[]
  • 通用运营商不能使用索引与一个intarray操作符类,反之亦然。

+0

'intarray'是否也适用于'bigint'阵列? – ma11hew28

+0

第一个解决方案(使用'sort')比第二个解决方案(使用'@>'和'<@')更快吗?如果没有,我想我可以不用'intarray'。 – ma11hew28

+1

@mattdipasquale:我增加了一些解释。 –

6

您可以使用包含在由操作者:

(ARRAY1 < @数组2和ARRAY1 @>数组2)