Arrays 数组2中包含的数组1和元素的顺序相同
PostgreSQL是否有方法让我确定一个数组是否包含在另一个数组中,但顺序相同?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 一般情况 第二个数组的所有元素也在第一个数组中。顺序相同,但允许有
例如,我想知道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.6arr1[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;