跨多列排列的唯一约束

问题描述 投票:0回答:4

给定 Postgres 数据库中的以下三列:第一、第二、第三;如何创建一个约束以使排列是唯一的?

例如如果

('foo', 'bar', 'shiz')
存在于数据库中,则
('bar', 'shiz', 'foo')
将被排除为非唯一。

sql postgresql database-design constraints unique-constraint
4个回答
6
投票

您可以使用 hstore 创建唯一索引:

CREATE UNIQUE INDEX hidx ON test USING BTREE (hstore(ARRAY[a,b,c], ARRAY[a,b,c]));

小提琴

更新

其实

CREATE UNIQUE INDEX hidx ON test USING BTREE (hstore(ARRAY[a,b,c], ARRAY[null,null,null]));

可能是一个更好的主意,因为它的工作原理相同,但占用的空间更少(fiddle)。


6
投票

仅对于三列,这个仅使用基本表达式的唯一索引应该表现得很好。不需要 hstore 或自定义函数等额外模块:

CREATE UNIQUE INDEX t_abc_uni_idx ON t (
  LEAST(a,b,c)
, GREATEST(LEAST(a,b), LEAST(b,c), LEAST(a,c))
, GREATEST(a,b,c)
);

小提琴
sqlfiddle

还需要最少的磁盘空间:

SELECT pg_column_size(row(hstore(t))) AS hst_row
     , pg_column_size(row(hstore(ARRAY[a,b,c], ARRAY[a,b,c]))) AS hst1
     , pg_column_size(row(hstore(ARRAY[a,b,c], ARRAY[null,null,null]))) AS hst2
     , pg_column_size(row(ARRAY[a,b,c])) AS arr
     , pg_column_size(row(LEAST(a,b,c)
                        , GREATEST(LEAST(a,b), LEAST(b,c), LEAST(a,c))
                        , GREATEST(a,b,c))) AS columns
FROM t;
 hst_row | hst1 | hst2 | arr | columns
---------+------+------+-----+---------
      59 |   59 |   56 |  69 |      30

数字是小提琴示例中索引行的字节,用

pg_column_size()
测量。我的示例仅使用单个字符,大小差异是恒定的。


3
投票

您可以通过在函数上创建唯一索引来实现此目的,该函数返回列中值的排序数组:

CREATE OR REPLACE FUNCTION sorted_array(anyarray)
RETURNS anyarray
AS $BODY$
  SELECT array_agg(x) FROM (SELECT unnest($1) AS x FROM test ORDER BY x) AS y;
$BODY$
LANGUAGE sql IMMUTABLE;

CREATE UNIQUE index ON test (sorted_array(array[first,second,third]));

0
投票

来自同事的建议,@julien 想法的变体:

按字母顺序对术语进行排序,并在每个术语的两侧放置一个分隔符。将它们连接起来并将它们放置在成为主键的单独字段中。

为什么要使用分隔符?这样,“a”、“aa”、“aaa”和“aa”、“aa”、“aa”都可以插入。

© www.soinside.com 2019 - 2024. All rights reserved.