每个项目最多强制执行一个 null 的唯一约束

vek*_*tor 5 postgresql database-design exclusion-constraint unique-constraint

我正在使用 PostgreSQL 9.3。假设我们正在制造某种类型的某些物品,在任何时候我们最多只能生产某种类型的一件物品。下表记录了我们制造物品的历史记录,其中可能有一行manufactured_untilnull- 这些是当前生产的物品。

create table item
(
  id int,
  type_id int,
  manufactured_from timestamp,
  manufactured_until timestamp
)
Run Code Online (Sandbox Code Playgroud)

样本数据:

1  | 101  | 1.1.2000    | 31.12.2012
2  | 102  | 1.4.2003    | 1.1.2010
3  | 101  | 1.1.2013    | 
4  | 102  | 2.1.2010    | 4.5.2014
5  | 102  | 5.5.2014    | 
Run Code Online (Sandbox Code Playgroud)

以下逻辑应该成立:对于每种物品类型,此时最多应该生产一个物品 ( manufactured IS NULL)。在示例中,我应该无法添加记录(6, 101, 27.8.2014, NULL)

我想写一个UNIQUE约束来保护它。是否可以?对于奖励积分,是否有一种相当复杂的方法来保护一种项目类型的间隔不重叠?

a_h*_*ame 9

部分唯一索引应该这样做:

create unique index max_one_null 
   on item (type_id) 
where manufactured_until is null;
Run Code Online (Sandbox Code Playgroud)

对于奖励积分,是否有一种相当复杂的方法来保护一种项目类型的间隔不重叠

查看范围类型和排除约束。它们是专门为这个问题设计的。

像这样的东西(未经测试):

create table item
(
  id int,
  type_id int,
  manufactured_during tsrange
);
Run Code Online (Sandbox Code Playgroud)

插入内容看起来有点不同,因为现在只有一列:

insert into item 
  (id, type_id, manufactured_during)
values 
  (1, 101, '[2000-01-01,2012-12-31]'),
  (1, 101, '[2013-01-01,)');
Run Code Online (Sandbox Code Playgroud)

[2000-01-01,2012-12-31]定义一个包含两个日期的闭区间。[2013-01-01,)定义一个没有结束的开放区间(映射到manufactured_until is null您当前的表设计)

然后添加一个排除约束来保护范围:

alter table item
  add constraint check_manufactured_range
  exclude using gist (type_id with =, manufactured_during with &&);
Run Code Online (Sandbox Code Playgroud)

您不再需要唯一索引,因为约束将确保没有任何内容重叠。

手册中的更多信息:http : //www.postgresql.org/docs/current/static/rangetypes.html

如果您在 Internet 上搜索“Postgres 排除约束”,您将找到一些关于此主题的演示文稿和博客文章。

PS:似乎您实际上想要一个daterange(而不是“时间戳范围”),因为您可能不想将一天中的时间包含在定义某物制造时间的间隔中。

  • 排除约束要求您为包含的“整数”安装扩展“btree_gist”。详细信息:http://dba.stackexchange.com/questions/37351/postgresql-exclude-using-error-data-type-in​​teger-has-no-default-operator-class/37373#37373 (3认同)