13

我们有一张表,上面有一个唯一的约束,用于一个用户留下的反馈,另一个,与销售有关。

ALTER TABLE feedback
ADD CONSTRAINT unique_user_subject_and_sale
UNIQUE (user_id, subject_id, sale_id)

这可以确保我们不会意外获得重复的反馈行。

目前,我们有时会硬删除错误留下的反馈并让用户再次离开。我们要改为软删除:

ALTER TABLE feedback
ADD COLUMN deleted_at timestamptz

如果deleted_at IS NOT NULL是,请考虑删除反馈,尽管我们的数据库中仍然有审计跟踪(并且可能会向站点管理员显示它的影子)。

当我们像这样使用软删除时,我们如何保持我们的唯一约束?是否可以不使用更一般CHECK()的约束来进行聚合检查(我从未尝试过使用这样的检查约束)。

这就像我需要在约束中附加一个 WHERE 子句。

4

3 回答 3

28

你的唯一索引,后来被编辑掉了。

CREATE UNIQUE INDEX feedback_unique_user_subject_and_sale_null
ON feedback(user_id, subject_id, sale_id)
WHERE deleted_at IS NULL

您的唯一索引至少有两个可能会给您带来麻烦的副作用。

  1. 在其他表中,您不能设置引用“反馈”的外键约束。外键引用需要将某些列组合声明为primary keyor 或unique
  2. 您的唯一索引允许在“deleted_at”时间戳不同的多行。因此,最终可能会得到如下例所示的行。这是否是一个问题取决于应用程序。

例子

user_id  subject_id  sale_id  deleted_at
--
1        1           1        2012-01-01 08:00:01.33
1        1           1        2012-01-01 08:00:01.34
1        1           1        2012-01-01 08:00:01.35

PostgreSQL 将这种索引记录为部分索引,如果您需要在某个时候谷歌它。其他平台对它使用不同的术语——过滤索引就是其中之一。您可以使用一对部分索引将问题限制在一定程度上。

CREATE UNIQUE INDEX feedback_unique_user_subject_and_sale_null
ON feedback(user_id, subject_id, sale_id)
WHERE deleted_at IS NULL

CREATE UNIQUE INDEX feedback_unique_user_subject_and_sale_not_null
ON feedback(user_id, subject_id, sale_id)
WHERE deleted_at IS NOT NULL

我认为没有理由去这么麻烦,特别是考虑到外键的潜在问题。如果你的桌子看起来像这样

create table feedback (
  feedback_id integer primary key,
  user_id ...
  subject_id ...
  sale_id ...
  deleted_at ...
  constraint unique_user_subj_sale 
    unique (user_id, subject_id, sale_id)
);

那么您所需要的就是对 {user_id, subject_id, sale_id} 的唯一约束。您可能会进一步考虑让所有删除都使用“deleted_at”列,而不是进行硬删除。

于 2012-11-06T23:04:18.423 回答
8

尽管 PostgreSQL 文档建议不要使用唯一索引而不是约束(如果要点是有约束),但您似乎可以这样做

CREATE UNIQUE INDEX feedback_unique_user_subject_and_sale
ON feedback(user_id, subject_id, sale_id)
WHERE deleted_at IS NULL
于 2012-11-06T23:03:29.610 回答
0

您可以使用 deleted_at 签名创建约束。

ALTER TABLE feedback
ADD CONSTRAINT unique_user_subject_and_sale
UNIQUE (user_id, subject_id, sale_id, deleted_at)

这不会产生像唯一索引这样的问题。

唯一的问题是删除用户反馈,创建一个新的,然后在同一时间再次删除它。

考虑到色谱柱的准确性,用户必须在 1 微秒内适应。这是不可能的,即使他做到了,这些请求之间的时间肯定会大于1微秒。

唯一的问题是空值不是唯一的。所以它不会起作用。

但是您可以为未删除的行添加一个默认值作为您可以存储在该列中的最低值。

如果您不能接受 的默认值deleted_at,如果对象没有被删除,您可以考虑添加一deleted_token列并在删除值时为其生成键,对于未删除的对象,保留0或其他方式。

于 2021-11-16T15:11:30.107 回答