Postgres对行组的唯一约束

问题描述

我正在使用Postgresql 10.12

我已标记实体。有些是标准的,有些则不是。标准实体在所有用户之间共享,而不是用户拥有的标准实体。因此,假设我有一个Entity一个文本列Label一个user_id,对于标准实体而言该列为空。

CREATE TABLE Entity
(
  id uuid NOT NULL PRIMARY KEY,user_id integer,label text NOT NULL,)

这是我的约束:属于不同用户的两个非标准实体可以具有相同的标签。标准实体标签是唯一的,给定用户的实体具有唯一的标签。困难的部分是:标签在一组标准实体+给定用户的实体中必须唯一。

我正在使用sqlAlchemy,这是我到目前为止所做的约束:

__table_args__ = (
    UniqueConstraint("label","user_id",name="_entity_label_user_uc"),db.Index(
        "_entity_standard_label_uc",label,user_id.is_(None),unique=True,postgresql_where=(user_id.is_(None)),),)

此约束的问题是我不保证用户实体不会具有标准的实体标签

示例:

+----+---------+------------+
| id | user_id |   label    |
+----+---------+------------+
|  1 | null    | std_ent    |
|  2 | 42      | user_ent_1 |
|  3 | 42      | user_ent_2 |
|  4 | 43      | user_ent_1 |
+----+---------+------------+

这是一个有效的表。我想确保无法再创建带有标签std_ent的实体,用户42无法创建带有标签user_ent_1user_ent_2的另一个实体,并且用户43无法创建另一个具有标签user_ent_1的实体标签std_ent的实体。

在我当前的限制下,用户42和43仍可以创建带有标签AAssetManager_open(aassman,"/images/blah0.png")的实体,这是我要修复的问题。

有什么主意吗?

解决方法

如果您的唯一约束正在阻止用户为自己的“用户实体”输入重复的标签,那么您可以通过添加触发器来阻止他们输入“标准实体”的标签。

您创建了一个函数……

CREATE OR REPLACE FUNCTION public.std_label_check()
    RETURNS trigger
    LANGUAGE plpgsql
AS $function$
    begin
        if exists(
                select * from entity 
                where label = new.label and user_id is null) then
            raise exception '"%" is already a standard entity',new.label;
        end if;
        return new;
    end;
$function$
;

…,然后将其作为触发器附加到表上

CREATE TRIGGER entity_std_label_check
BEFORE INSERT 
ON public.entity FOR EACH ROW
EXECUTE PROCEDURE std_label_check()