有哪些选项可用于在PostgreSQL中应用设置级别约束?

有哪些选项可用于在PostgreSQL中应用设置级别约束?,sql,postgresql,constraints,Sql,Postgresql,Constraints,在这种情况下,我需要确保在任何时候只有一条活动记录具有相同的object_id和user_id。以下是一张具有代表性的表格: CREATE TABLE actions ( id SERIAL PRIMARY KEY, object_id integer, user_id integer, active boolean default true, created_at timestamptz default now() ); 如果一次只有一条活动记录,我的

在这种情况下,我需要确保在任何时候只有一条活动记录具有相同的object_id和user_id。以下是一张具有代表性的表格:

CREATE TABLE actions (
    id SERIAL PRIMARY KEY,
    object_id integer,
    user_id integer,
    active boolean default true,
    created_at timestamptz default now()
);
如果一次只有一条活动记录,我的意思是可以有如下插入序列:

insert into actions (object_id, user_id, active) values (1, 1, true);
insert into actions (object_id, user_id, active) values (1, 1, false);
但是做一个后续的

insert into actions (object_id, user_id, active) values (1, 1, true);
应该失败,因为此时已存在1个活动元组,其中object_id=1,user_id=1

我正在使用PostgreSQL 8.4

我看到这个看起来很有趣,但它是Oracle特有的

我也看到了这一点,但它需要更多地关注事务隔离级别。我认为它不会像在读提交模式下那样工作

我的问题是,对于这种约束,还有哪些其他选择

编辑:删除了第一组中的第三个插入。我认为它混淆了这个例子。我还添加了created_at time stamp来帮助处理上下文。重申一下,可以有多个(object\u id、user\u id、false)元组,但只有一个(object\u id、user\u id、true)元组

更新:我接受了克雷格的答案,但对于其他可能偶然发现类似问题的人来说,这里有另一个可能的(虽然不太理想)解决方案

CREATE TABLE action_consistency (
    object_id integer,
    user_id integer,
    count integer default 0,
    primary key (object_id, user_id),
    check (count >= 0 AND count <= 1)
);


CREATE OR REPLACE FUNCTION keep_action_consistency()
  RETURNS TRIGGER AS
$BODY$
    BEGIN

    IF NEW.active THEN
        UPDATE action_consistency
        SET count = count + 1
        WHERE object_id = NEW.object_id AND
        user_id   = NEW.user_id;

        INSERT INTO action_consistency (object_id, user_id, count)
        SELECT NEW.object_id, NEW.user_id, 1
        WHERE NOT EXISTS (SELECT 1 
                          FROM action_consistency
                          WHERE object_id = NEW.object_id AND
                                user_id   = NEW.user_id);
    ELSE
        -- assuming insert will be active for simplicity
        UPDATE action_consistency
        SET count = count - 1
        WHERE object_id = NEW.object_id AND
        user_id   = NEW.user_id;
    END IF;

    RETURN NEW;

    END;
$BODY$
LANGUAGE plpgsql;

CREATE TRIGGER ensure_action_consistency AFTER INSERT OR UPDATE ON actions
    FOR EACH ROW EXECUTE PROCEDURE keep_action_consistency();
创建表操作\u一致性(
对象id为整数,
用户id整数,
计数整数默认值为0,
主键(对象id、用户id),
选中(count>=0和count,可用于确保列包含唯一值。。。
在这里,
object\u id
user\u id
的集合是唯一的

CREATE TABLE actions (
    id SERIAL PRIMARY KEY,
    object_id integer,
    user_id integer,
    active boolean default true,
    UNIQUE (object_id , user_id )

);
查看

类似地,如果要将
对象id
用户id
活动的
设置为
唯一的
,只需在
唯一的
列表中添加列名即可

CREATE TABLE actions (
    id SERIAL PRIMARY KEY,
    object_id integer,
    user_id integer,
    active boolean default true,
    UNIQUE (object_id , user_id,active )

);
查看

您可以使用以确保列包含唯一值。。。 在这里,
object\u id
user\u id
的集合是唯一的

CREATE TABLE actions (
    id SERIAL PRIMARY KEY,
    object_id integer,
    user_id integer,
    active boolean default true,
    UNIQUE (object_id , user_id )

);
查看

类似地,如果要将
对象id
用户id
活动的
设置为
唯一的
,只需在
唯一的
列表中添加列名即可

CREATE TABLE actions (
    id SERIAL PRIMARY KEY,
    object_id integer,
    user_id integer,
    active boolean default true,
    UNIQUE (object_id , user_id,active )

);
查看原始版本:

CREATE TABLE actions (
    id SERIAL PRIMARY KEY,
    object_id integer,
    user_id integer,
    active boolean default true
);
我的版本:

CREATE TABLE actions (
    object_id integer NOT NULL REFERENCES objects (id),
    user_id integer NOT NULL REFERENCES users(id),
    PRIMARY KEY (user_id, object_id)
);
区别是什么:

  • 省略了代理键。它是无用的,它不强制任何约束,并且没有人会引用它
  • 添加了一个(复合)主键,它恰好是逻辑键
  • 将这两个字段更改为NOTNULL,并将它们转换为外键(users或objects表中不存在的行的含义是什么
  • 删除了布尔标志。不存在的{user\u id,object\u id}元组与确实存在但其“active”标志设置为false的元组之间的语义区别是什么?为什么在只需要两个状态时创建三个状态
原件:

CREATE TABLE actions (
    id SERIAL PRIMARY KEY,
    object_id integer,
    user_id integer,
    active boolean default true
);
我的版本:

CREATE TABLE actions (
    object_id integer NOT NULL REFERENCES objects (id),
    user_id integer NOT NULL REFERENCES users(id),
    PRIMARY KEY (user_id, object_id)
);
区别是什么:

  • 省略了代理键。它是无用的,它不强制任何约束,并且没有人会引用它
  • 添加了一个(复合)主键,它恰好是逻辑键
  • 将这两个字段更改为NOTNULL,并将它们转换为外键(users或objects表中不存在的行的含义是什么
  • 删除了布尔标志。不存在的{user\u id,object\u id}元组与确实存在但其“active”标志设置为false的元组之间的语义区别是什么?为什么在只需要两个状态时创建三个状态

如果您的规范要求一次只允许一个条目处于活动状态,请尝试:

CREATE TABLE actions (
    id SERIAL PRIMARY KEY,
    object_id integer,
    user_id integer,
    active boolean default true,
    created_at timestamptz default now()
);

CREATE UNIQUE INDEX actions_unique_active_y ON actions(object_id,user_id) WHERE (active = 't');
这是一个部分唯一索引,是PostgreSQL特有的功能-请参阅。它限制集合,以便在
活动的
真的
的位置只能存在一个
(object\u id,user\u id)
元组


正如您在评论中进一步解释的那样,这严格地回答了您的问题,但我认为它描述了更正确的选择和最佳方法。

鉴于您的规范,您希望一次只将一个条目限制为活动的
,请尝试:

CREATE TABLE actions (
    id SERIAL PRIMARY KEY,
    object_id integer,
    user_id integer,
    active boolean default true,
    created_at timestamptz default now()
);

CREATE UNIQUE INDEX actions_unique_active_y ON actions(object_id,user_id) WHERE (active = 't');
这是一个部分唯一索引,是PostgreSQL特有的功能-请参阅。它限制集合,以便在
活动的
真的
的位置只能存在一个
(object\u id,user\u id)
元组


正如您在评论中进一步解释的那样,这严格地回答了您的问题,但我认为它描述了更正确的选择和最佳方法。

布尔标志只会使事情复杂化。对于NM关系表,该行要么存在,要么不存在。无需将其标记为“不使用”。Wrt您的更新(添加了“created_at”时间戳):添加一个单独的历史记录表。@wildplasser布尔标志更复杂。在应用程序中,它是一个位掩码。我在这里使用布尔标志来尝试简化事情以说明问题。历史记录表可以工作。这将允许我维护actions表上的唯一约束。布尔标志只会使事情复杂化。对于NM关系表,该行存在或不存在。无需将其标记为“不使用”。Wrt您的更新(添加了“created_at”时间戳):添加一个单独的历史记录表。@wildplasser布尔标志更复杂。在应用程序中,它是一个位掩码。我在这里使用布尔标志来尝试简化问题。历史记录表可以工作。这将允许我在操作表上保持唯一约束。这将导致第二次插入失败。请参阅我包括的示例。这里的上下文是,您可以插入一条记录,稍后将活动字段设置为false,然后插入另一条记录,并将活动集设置为true。是否要将每行的object\u id、user\u id、active设置为唯一?否,object\u id、user\u id、active不需要唯一,但只能有一个元组(o