如何创建对数组中所有值的唯一性的约束,如:
CREATE TABLE mytable
(
interface integer[2],
CONSTRAINT link_check UNIQUE (sort(interface))
)
我的排序功能
create or replace function sort(anyarray)
returns anyarray as $$
select array(select $1[i] from generate_series(array_lower($1,1),
array_upper($1,1)) g(i) order by 1)
$$ language sql strict immutable;
我需要的是值{10,22}和{22,10}被认为是相同的并且在UNIQUE CONSTRAINT下检查
我认为您不能使用具有唯一约束的函数,但您可以使用唯一索引.所以给出一个像这样的排序函数:
create function sort_array(integer[]) returns integer[] as $$ select array_agg(n) from (select n from unnest($1) as t(n) order by n) as a; $$ language sql immutable;
然后你可以这样做:
create table mytable ( interface integer[2] ); create unique index mytable_uniq on mytable (sort_array(interface));
然后发生以下情况:
=> insert into mytable (interface) values (array[11,23]); INSERT 0 1 => insert into mytable (interface) values (array[11,23]); ERROR: duplicate key value violates unique constraint "mytable_uniq" DETAIL: Key (sort_array(interface))=({11,23}) already exists. => insert into mytable (interface) values (array[23,11]); ERROR: duplicate key value violates unique constraint "mytable_uniq" DETAIL: Key (sort_array(interface))=({11,23}) already exists. => insert into mytable (interface) values (array[42,11]); INSERT 0 1
@mu已经演示了表达式的索引如何解决您的问题.
我的注意力被用过的功能所吸引.对于两个整数的数组来说,两者似乎都是过度杀伤力.这可能是对实际情况的简化.(?)
无论如何,我很感兴趣并且使用了几个变体进行了测试.
-- temporary table with 10000 random pairs of integer CREATE TEMP TABLE arr (i int[]); INSERT INTO arr SELECT ARRAY[(random() * 1000)::int, (random() * 1000)::int] FROM generate_series(1,10000);
测试候选人的简短评论来解释每一个:
-- 1) mu's query CREATE OR REPLACE FUNCTION sort_array1(integer[]) RETURNS int[] AS $$ SELECT array_agg(n) FROM (SELECT n FROM unnest($1) AS t(n) ORDER BY n) AS a; $$ LANGUAGE sql STRICT IMMUTABLE; -- 2) simplified with ORDER BY inside aggregate (pg 9.0+) CREATE OR REPLACE FUNCTION sort_array2(int[]) RETURNS int[] AS $$ SELECT array_agg(n ORDER BY n) FROM unnest($1) AS t(n); $$ LANGUAGE sql STRICT IMMUTABLE; -- 3) uralbash's query CREATE OR REPLACE FUNCTION sort_array3(anyarray) RETURNS anyarray AS $$ SELECT ARRAY( SELECT $1[i] FROM generate_series(array_lower($1,1), array_upper($1,1)) g(i) ORDER BY 1) $$ LANGUAGE sql STRICT IMMUTABLE; -- 4) change parameters to int[] CREATE OR REPLACE FUNCTION sort_array4(int[]) RETURNS int[] AS $$ SELECT ARRAY( SELECT $1[i] FROM generate_series(array_lower($1,1), array_upper($1,1)) g(i) ORDER BY 1) $$ LANGUAGE sql STRICT IMMUTABLE; -- 5) simplify array_lower() - it's always 1 CREATE OR REPLACE FUNCTION sort_array5(int[]) RETURNS int[] AS $$ SELECT ARRAY( SELECT $1[i] FROM generate_series(1, array_upper($1,1)) g(i) ORDER BY 1) $$ LANGUAGE sql STRICT IMMUTABLE; -- 6) further simplify to case with 2 elements CREATE OR REPLACE FUNCTION sort_array6(int[]) RETURNS int[] AS $$ SELECT ARRAY( SELECT i FROM (VALUES ($1[1]),($1[2])) g(i) ORDER BY 1) $$ LANGUAGE sql STRICT IMMUTABLE; -- 7) my radically simple query CREATE OR REPLACE FUNCTION sort_array7(int[]) RETURNS int[] AS $$ SELECT CASE WHEN $1[1] > $1[2] THEN ARRAY[$1[2], $1[1]] ELSE $1 END; $$ LANGUAGE sql STRICT IMMUTABLE; -- 8) without STRICT modifier CREATE OR REPLACE FUNCTION sort_array8(int[]) RETURNS int[] AS $$ SELECT CASE WHEN $1[1] > $1[2] THEN ARRAY[$1[2], $1[1]] ELSE $1 END; $$ LANGUAGE sql IMMUTABLE;
我执行了大约20次并取得了最好的结果EXPLAIN ANALYZE
.
SELECT sort_array1(i) FROM arr -- Total runtime: 183 ms SELECT sort_array2(i) FROM arr -- Total runtime: 175 ms SELECT sort_array3(i) FROM arr -- Total runtime: 183 ms SELECT sort_array4(i) FROM arr -- Total runtime: 183 ms SELECT sort_array5(i) FROM arr -- Total runtime: 177 ms SELECT sort_array6(i) FROM arr -- Total runtime: 144 ms SELECT sort_array7(i) FROM arr -- Total runtime: 103 ms SELECT sort_array8(i) FROM arr -- Total runtime: 43 ms (!!!)
这些是Debian Squeeze上的v9.0.5服务器的结果.关于v.8.4的类似结果.
我还测试了plpgsql变种,这些变体比预期的要慢一点:对于一个微小的操作来说太多开销,没有缓存的查询计划.
的简单的函数(NR 7)比其它实质上更快.这是可以预料的,其他变种的开销对于一个小阵列来说太多了.
但是,离开STRICT
修改器的速度超过了速度的两倍是不可预料的.至少我没有.我在这里发了一个关于这个现象的问题.
只需在两个值上创建唯一索引:
create unique index ix on mytable(least(interface[1], interface[2]), greatest(interface[1], interface[2]));