Arrays 数组2中包含的数组1和元素的顺序相同

Arrays 数组2中包含的数组1和元素的顺序相同,arrays,postgresql,Arrays,Postgresql,PostgreSQL是否有方法让我确定一个数组是否包含在另一个数组中,但顺序相同? 例如,我想知道array1是否在array2中,匹配元素的顺序是否相同 array1[1, 3, 6, 8] array2[3, 8, 2, 9, 10, 1, 6] 显然,示例中不是这样,但是PostgreSQL中是否有用于此的内置方法,或者我是否应该创建自己的函数 PostgreSQL的版本是9.6。查询将在其上运行的实际数字是bigint 一般情况 第二个数组的所有元素也在第一个数组中。顺序相同,但允许有

PostgreSQL是否有方法让我确定一个数组是否包含在另一个数组中,但顺序相同?
例如,我想知道array1是否在array2中,匹配元素的顺序是否相同

array1[1, 3, 6, 8]
array2[3, 8, 2, 9, 10, 1, 6]
显然,示例中不是这样,但是PostgreSQL中是否有用于此的内置方法,或者我是否应该创建自己的函数

PostgreSQL的版本是9.6。查询将在其上运行的实际数字是bigint

一般情况 第二个数组的所有元素也在第一个数组中。顺序相同,但允许有间隙

我建议使用此多态PL/pgSQL函数:

CREATE OR REPLACE FUNCTION array_contains_array_in_order(arr1 ANYARRAY
                                                       , arr2 ANYARRAY
                                                       , elem ANYELEMENT = NULL)
  RETURNS bool AS
$func$
DECLARE
   pos int := 1;
BEGIN
   FOREACH elem in ARRAY arr2
   LOOP
      pos := pos + array_position(arr1[pos:], elem);  -- see below
      IF pos IS NULL THEN
         RETURN FALSE;
      END IF;
   END LOOP; 

   RETURN true; --  all elements found in order
END
$func$ LANGUAGE plpgsql IMMUTABLE COST 3000;
同样,我们可以省略数组下标中的上界来表示“无界”(
arr1[pos://code>)。在9.6之前的旧版本中,用
arr1[pos:2147483647]
-2147483647=2^31-1替换为理论上的最大数组索引,即最大有符号整数4

这适用于

  • 任何一维数组类型,而不仅仅是
    integer[]

  • 具有空值的数组,这也得益于它也适用于空值

  • 具有重复元素的数组

  • 仅适用于以1开头的默认数组下标。如果需要,您可以轻松覆盖非标准下标:

关于
ANYELEMENT
技巧:

演出 我运行了一个快速性能测试,将此函数与。这个大约快5倍

如果您对一个大表使用此过滤器,我强烈建议您将其与(逻辑上冗余的)可搜索过滤器结合使用,如:

SELECT *
FROM   tbl
WHERE  arr @> '{2,160,134,58,149,111}'::int[] 
AND    array_contains_array_in_order(arr, '{2,160,134,58,149,111}')  
这将在数组列上使用GIN索引,如:

CREATE INDEX ON tbl USING gin (arr);
并且只过滤剩余的(通常很少!)共享所有元素的数组。通常要快得多

内置模块的注意事项 注意:仅适用于
integer[]
,而不是
smallint[]
bigint[]
或任何其他数组类型

如果已安装,请小心,它为
int[]
提供了自己的
@>
运算符变体。您可以使用其特殊运算符类创建一个(额外的)GIN索引(在适用的情况下,它会更快一些):

,虽然您只有一个带有默认运算符类的GIN索引,但必须明确表示与索引配合的标准运算符:

WHERE  arr OPERATOR(pg_catalog.@>) '{2,160,134,58,149,111}'::int[] 

对于较长的阵列,速度比上述方法快得多。所有其他注意事项仍然适用。

如果按顺序比较数组,为什么不比较
array2::text-like-concat(“%”,array1::text,“%”)
?。@VaoTsun,如果
array2[1,4,6,10,12]
array1[1,6,12]
那么
array2::text-like-concat(“%”,array1::text,“%”)
将为假,但根据OP的要求,这应该是真的。但是
array2::像concat('%',array_to_string(array1,''''),'%')这样的文本可能会工作(我还没有测试)。是的-我没有考虑顺序中可能存在的差距-真的:)啊,甚至没有考虑使用like操作符,但这很聪明。第二个解决方案是当场解决。非常感谢@Philip:Postgres 9.6
arr1[pos:2147483647]
中的
解决方案不适用于例如
array[1,2]
array[11,2]
,可以用
arr1[pos:
@a_horse_和_no_name:是的,这样更好。我在上面整合了你的改进,谢谢。即使没有你提到的索引,这个运行也非常快。我想我还需要做更多的测试,但这似乎真的很有希望。@Philip:你对另一个(现在删除了答案)发表了评论:
啊,我的问题应该更清楚。我想要完全相同的序列。所以{3,1,6}和{3,8,2,9,10,1,6}将是错误的,然而{3,1,6}和{3,8,2,9,10,3,1,6}将是正确的。
此解决方案适用于更一般的情况。你的情况更简单。请编辑您的问题以包含所有相关信息。我为您的简单问题添加了解决方案。
WHERE  arr OPERATOR(pg_catalog.@>) '{2,160,134,58,149,111}'::int[] 
CREATE OR REPLACE FUNCTION array_contains_array_exactly(arr1 ANYARRAY, arr2 ANYARRAY)
  RETURNS bool AS
$func$
DECLARE
   len int := array_length(arr2, 1) - 1;  -- length of arr2 - 1 to fix off-by-1
   pos int;                               -- for current search postition in arr1
BEGIN
   /*                                     -- OPTIONAL, if invalid input possible
   CASE array_length(arr1, 1) >  len      -- array_length(arr2, 1) - 1 
   WHEN TRUE  THEN                        -- valid arrays
      -- do nothing, proceed
   WHEN FALSE THEN                        -- arr1 shorter than arr2
      RETURN FALSE;                       -- or raise exception?
   ELSE                                   -- at least one array empty or NULL
      RETURN NULL;
   END CASE;
   */

   pos := array_position(arr1, arr2[1]);  -- pos of arr2's 1st elem in arr1

   WHILE pos IS NOT NULL
   LOOP
      IF arr1[pos:pos+len] = arr2 THEN    -- array slice matches arr2 *exactly*
         RETURN TRUE;                     -- arr2 is part of arr1
      END IF;

      pos := pos + array_position(arr1[(pos+1):], arr2[1]);
   END LOOP; 

   RETURN FALSE;
END
$func$ LANGUAGE plpgsql IMMUTABLE COST 1000;