Postgres UNIQUE CONSTRAINT用于数组

时间:2011-12-09 09:50:29

标签: arrays postgresql unique-constraint

如何在数组中的所有值的唯一性上创建约束,如:

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下检查

3 个答案:

答案 0 :(得分:12)

我认为您不能使用unique constraint的函数,但可以使用unique index。所以给定一个类似的排序函数:

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

答案 1 :(得分:9)

@mu已经演示了index on an expression如何解决您的问题。

我的注意力被用过的功能所吸引。对于两个整数的数组来说,两者似乎都是过度杀伤力。这可能是对实际情况的简化。 (?)

无论如何,我很感兴趣并且使用了几种变体进行了测试。

测试设置

-- 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变种,它们的速度比预期慢一点:对于一个微小的操作来说,开销太大,没有缓存的查询计划。

简单函数(第7行)比其他函数快得多。这是可以预料的,其他变体的开销对于一个小阵列来说太多了。

但是,STRICT修饰符不会超过加倍,速度是不可预期的。至少我没有。我发布了question about this phenomenon here

答案 2 :(得分:9)

只需在两个值上创建唯一索引:

create unique index ix on 
  mytable(least(interface[1], interface[2]), greatest(interface[1], interface[2]));