如何在第三列的范围内强制两列的唯一性?

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

给定一个包含三列的表格:

CREATE TABLE tbl
  column_scope text
, col1 int
, col2 int
);

我想强制执行,按

column_scope
分区,
col1
col2
中的值在两列中都是唯一的 - 除了同一行中的重复项。 也就是说,在该范围内,
col1
col2
中的两个值不能相同,除非
NULL
或它们位于同一行。

我尝试添加唯一索引(column_scope、col1、col2),但是这会失败,因为它不会考虑无效情况,例如:

row_1 -> (scope_1, x, NULL);
row_2 -> (scope_1, NULL, x);

row_1 -> (scope_1, x, y);
row_2 -> (scope_1, y, z);

我还尝试向表添加约束,但是我似乎无法获得正确的条件来确保

col1
col2
对于给定的
column_scope
没有共享值。

这与其他问题不同,因为我不关心值的特定组合,我只关心值在不同列之间不重复,全部在第三列的范围内。

sql postgresql validation indexing database-design
1个回答
0
投票

系好安全带。这是一个先进的解决方案。
首先安装两个附加模块(每个数据库一次):intarraybtree_gist。然后我们可以使用单个 排除约束:

来完成这项工作
CREATE EXTENSION intarray;    -- required!
CREATE EXTENSION btree_gist;  -- required!

CREATE TABLE tbl (
  column_scope text NOT NULL
, col1 int  -- can be null
, col2 int  -- can be null
);

-- Add THIS exclusion constraint !!!
ALTER TABLE tbl ADD CONSTRAINT tbl_cross_col_unique
EXCLUDE USING gist (column_scope WITH =
                  , array_remove(ARRAY[col1, col2], null) gist__int_ops WITH &&);

小提琴

请注意我如何假设类型

integer
代表
col1
col2
。保持简单。对于其他类型,您需要做更多...

您希望在

col1
col2
中允许空值。因此,从排除约束中排除那些。幸运的是,生成的数组中无论如何都不允许出现空值。
array_remove()
的手册:

比较是使用

IS NOT DISTINCT FROM
语义完成的,因此可以删除
NULL
s。

一切都恰到好处。

这有很多复杂的细节,涉及数据类型、索引方法、运算符类、空值、索引优化…… 但这超出了一个简单问题的范围,而是进入了付费咨询领域。

密切相关的案例,提供更多详细信息:

更简单的情况:仅禁止具有切换值的复合欺骗

此表达式索引实现了

UNIQUE
索引,其中
(1,2)
(2,1)
(col1, col2)
被视为相等:

CREATE UNIQUE INDEX tbl_uni_idx ON tbl
   (column_scope, GREATEST(col1, col2), LEAST(col1, col2));

参见:

空值不被视为相等,除非您添加

NULLS NOT DISTINCT
子句 - 这需要 Postgres 15 或更高版本。参见:

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