PostgreSQL有没有办法让我找到一个数组是否包含在另一个数组中,但具有相同的顺序?
例如,我想知道array1是否在array2中,匹配元素的顺序相同。
array1[1, 3, 6, 8]
array2[3, 8, 2, 9, 10, 1, 6]
显然不是示例中的情况,但在PostgreSQL中是否有内置的方法,还是应该创建自己的函数?
PostgreSQL的版本是9.6。查询将运行的实际数字是bigints。
答案 0 :(得分:2)
第二个数组的所有元素也在第一个元素中。按相同的顺序,但允许有差距。
我建议使用这种多态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;
作为@a_horse commented,我们可以省略数组下标中的上限,表示“无界”(arr1[pos:]
)。在9.6之前的旧版本中,替换为arr1[pos:2147483647]
- 2147483647 = 2 ^ 31 - 1 是理论上的最大数组索引,是最大的有符号int4数。
这适用于......
integer[]
。array_position()
,它也适用于NULL。仅适用于以1开头的默认数组下标。如果需要,您可以轻松覆盖非标准下标:
Normalize array subscripts for 1-dimensional array so they start with 1
关于ANYELEMENT
技巧:
我进行了快速性能测试,将此功能与the one @a_horse supplied进行了比较。这个速度提高了约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[]
或任何其他数组类型!
小心,如果您安装了intarray extension,@>
为int[]
提供了CREATE INDEX ON intarr USING gin (arr gin__int_ops);
运算符的变体。您可以使用其特殊的运算符类创建(附加)GIN索引(在适用的情况下稍快一些):
WHERE arr OPERATOR(pg_catalog.@>) '{2,160,134,58,149,111}'::int[]
或 ,虽然您只有一个带有默认运算符类的GIN索引,但您必须明确表示标准运算符与索引合作:
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;
详细说明:
如评论所述,您的案例更简单:
完整的第二个数组包含在第一个数组中(相同顺序,无间隙!)。
@If(@Adjust(ServiceDate; 0; 6; 0; 0; 0; 0) >= @Today; "Editable"; "ReadOnly")
对于更长的阵列,比上述速度快得多。所有其他考虑因素仍然适用。