postgresql 如何比较两个数组并只选择不匹配的元素

pdsfdshx  于 2023-01-30  发布在  PostgreSQL
关注(0)|答案(8)|浏览(228)

我怎样才能只挑选两个数组中不匹配的元素呢?
示例:

base_array [12,3,5,7,8]
temp_array [3,7,8]

所以这里我想比较两个数组并从基数组中删除匹配的元素。
现在base_array应该类似于[12,5]

r7xajy2e

r7xajy2e1#

我会用数组运算符来处理这个问题。

select array(select unnest(:arr1) except select unnest(:arr2));

如果:arr1和:arr2不相交,则使用array_agg()会导致空值。

2w3rbyxf

2w3rbyxf2#

select array_agg(elements)
from (
  select unnest(array[12,3,5,7,8])
  except
  select unnest(array[3,7,8])
) t (elements)
4xrmg8kj

4xrmg8kj3#

我构造了一组函数来专门处理这类问题:https://github.com/JDBurnZ/anyarray
最棒的是这些函数可以在所有数据类型上工作,而不仅仅是整数,因为intarray是有限的。
从GitHub加载完这些SQL文件中定义的函数后,你需要做的就是:

SELECT
  ANYARRAY_DIFF(
    ARRAY[12, 3, 5, 7, 8],
    ARRAY[3, 7, 8]
  )

返回类似于以下内容的内容:ARRAY[12, 5]
如果还需要返回排序后的值:

SELECT
  ANYARRAY_SORT(
    ANYARRAY_DIFF(
      ARRAY[12, 3, 5, 7, 8],
      ARRAY[3, 7, 8]
    )
  )

准确返回:ARRAY[5, 12]

pvcm50d1

pvcm50d14#

让我们试试unnest()/ except:

EXPLAIN ANALYZE SELECT array(select unnest(ARRAY[1,2,3,n]) EXCEPT SELECT unnest(ARRAY[2,3,4,n])) FROM generate_series( 1,10000 ) n;
 Function Scan on generate_series n  (cost=0.00..62.50 rows=1000 width=4) (actual time=1.373..140.969 rows=10000 loops=1)
   SubPlan 1
     ->  HashSetOp Except  (cost=0.00..0.05 rows=1 width=0) (actual time=0.011..0.011 rows=1 loops=10000)
           ->  Append  (cost=0.00..0.04 rows=2 width=0) (actual time=0.002..0.008 rows=8 loops=10000)
                 ->  Subquery Scan "*SELECT* 1"  (cost=0.00..0.02 rows=1 width=0) (actual time=0.002..0.003 rows=4 loops=10000)
                       ->  Result  (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.002 rows=4 loops=10000)
                 ->  Subquery Scan "*SELECT* 2"  (cost=0.00..0.02 rows=1 width=0) (actual time=0.001..0.003 rows=4 loops=10000)
                       ->  Result  (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.002 rows=4 loops=10000)
 Total runtime: 142.531 ms

而阵列内特殊运算符:

EXPLAIN ANALYZE SELECT ARRAY[1,2,3,n] - ARRAY[2,3,4,n] FROM generate_series( 1,10000 ) n;
 Function Scan on generate_series n  (cost=0.00..15.00 rows=1000 width=4) (actual time=1.338..11.381 rows=10000 loops=1)
 Total runtime: 12.306 ms

基线:

EXPLAIN ANALYZE SELECT ARRAY[1,2,3,n], ARRAY[2,3,4,n] FROM generate_series( 1,10000 ) n;
 Function Scan on generate_series n  (cost=0.00..12.50 rows=1000 width=4) (actual time=1.357..7.139 rows=10000 loops=1)
 Total runtime: 8.071 ms

每个阵列交叉点的时间:

intarray -           :  0.4 µs
unnest() / intersect : 13.4 µs

当然,intarray的方式要快得多,但我发现postgres可以在13.4 µs内快速处理一个依赖子查询(包含散列和其他内容),这令人惊讶。

xsuvu9jc

xsuvu9jc5#

contrib/intarray模块提供了这种功能--不管怎样,是针对整数数组的,对于其他数据类型,您可能需要编写自己的函数(或者修改intarray提供的函数)。

2ic8powd

2ic8powd6#

Denis答案的一个扩展,它返回差值,而不管先输入哪个数组。这不是最简洁的查询,也许有人有更整洁的方法。

select array_cat(
   (select array(select unnest(a.b::int[]) except select unnest(a.c::int[]))),
   (select array(select unnest(a.c::int[]) except select unnest(a.b::int[]))))
from (select '{1,2}'::int[] b,'{1,3}'::int[] c) as a;

退货:

{2,3}
jm2pwxwz

jm2pwxwz7#

我将使用与@a_horse_with_no_name描述的相同的except逻辑创建一个函数:

CREATE FUNCTION array_subtract(a1 int[], a2 int[]) RETURNS int[] AS $$
DECLARE
    ret int[];
BEGIN
    IF a1 is null OR a2 is null THEN
        return a1;
    END IF;
    SELECT array_agg(e) INTO ret
    FROM (
        SELECT unnest(a1)
        EXCEPT
        SELECT unnest(a2)
    ) AS dt(e);
    RETURN ret;
END;
$$ language plpgsql;

然后可以使用此函数相应地更改base_array变量:

base_array := array_subtract(base_array, temp_array);

使用@Denis的快速解决方案,并且只使用SQL,我们可以将泛型函数表示为

CREATE FUNCTION array_subtract(anyarray,anyarray) RETURNS anyarray AS $f$
  SELECT array(
    SELECT unnest($1)
    EXCEPT
    SELECT unnest($2)
  )
$f$ language SQL IMMUTABLE;
r55awzrz

r55awzrz8#

这是我的建议-它与其他答案的不同之处在于以下几点:

  • 它是一个可重用函数
  • 最重要的是,它保持了原始数组元素的顺序
CREATE OR REPLACE FUNCTION array_difference_ordered(array1 anyarray, array2 anyarray)
    RETURNS anyarray
    LANGUAGE sql
    IMMUTABLE AS
$$
SELECT ARRAY_AGG(array1_ordered_row.element ORDER BY array1_ordered_row.ordering)
FROM UNNEST(array1) WITH ORDINALITY AS array1_ordered_row(element, ordering)
WHERE NOT EXISTS
    (
        SELECT 1
        FROM UNNEST(array2) AS array2_row(element)
        WHERE array1_ordered_row.element = array2_row.element
    )
$$;

相关问题