没有选择的Postgres条件唯一约束

Posted

技术标签:

【中文标题】没有选择的Postgres条件唯一约束【英文标题】:Postgres conditional unique constraint without select 【发布时间】:2017-10-04 01:34:55 【问题描述】:

我有下表:

汽车:ID、名称、状态

只要我们有一个状态为SOLD 的名称记录,我想对汽车名称实施条件约束。

例子:

1, 'BMW', 'NEW'
2, 'BMW', 'NEW'
3, 'BMW', 'SOLD'
4, 'BMW', 'NEW'

前 3 条记录应该可以正常插入。但是不应该插入第 4 条记录,因为我们现在有一辆状态为 SOLD 的 BMW。

这是我尝试过的:

CREATE UNIQUE INDEX cars_sold ON cars (name) WHERE (status = 'SOLD');

这并不像我希望的那样工作。这只能确保我们不能有 2 个BMWSOLD

注意:我可以使用INSERT SELECT 来完成此操作,但此表将变得非常大,因此我想要执行唯一约束的性能。我还看到我可以使用CONSTRAINT CHECK 来做到这一点,但我相信它基本上可以在引擎盖下使用SELECT

在没有SELECT 在大桌子上的性能影响的情况下,有没有办法做到这一点?从概念上讲,我似乎总是不得不做一个SELECT

【问题讨论】:

我认为您必须为此使用触发器。数据结构非常奇怪。为什么不直接更新? @GordonLinoff 触发器是否必须在后台运行选择?我真的想避免选择的性能影响,否则我只会做一个插入选择 标准涉及当前记录中不可用的信息。也许使用外部表来跟踪状态。 【参考方案1】:

您将无法使用约束来执行此操作,因为约束始终检查数据库的当前状态,并且不考虑应用修改的顺序。

我认为最好使用BEFORE INSERT 触发器来解决问题,该触发器为具有相同名称和状态SOLD 的汽车运行SELECT 语句。无论表有多大,您在问题中创建的唯一部分索引都应该使SELECT 的速度非常快。

【讨论】:

我相信我的约束不需要知道应用修改的顺序。它只需要数据库的当前状态。回到我的例子,当应用数字 4 时,它并不关心应用了哪个订单 1、2、3,它只关心 #3 有一个 BMW 状态为 SOLD 另外,部分索引对我有什么帮助?是不是因为索引树现在小得多,因为它只包含状态为SOLD 的值? 如果您假设增加id 反映了修改的时间,那么您是对的(但如果存在高并发,请不要指望这一点)。我仍然想不出能反映这一点的约束。部分索引会很快,因为它是唯一的 - 它可以快速命中匹配的叶条目并且不必扫描。

以上是关于没有选择的Postgres条件唯一约束的主要内容,如果未能解决你的问题,请参考以下文章

oracle数据导入时,提示违反唯一约束性?

Postgres 唯一约束与索引

勺子插入 postgres 会产生“重复键值违反唯一约束”

Postgres约束唯一的日期时间范围

使用一个查询递增具有唯一约束的字段中的一组值,Postgres

错误:引用表“bar”的给定键没有唯一约束匹配