【问题标题】:Always use next sequence in Serial Column (no user value allowed)始终在序列列中使用下一个序列(不允许用户值)
【发布时间】:2016-10-28 09:52:40
【问题描述】:

在 PostgreSQL 中,我想创建一个带有自动增量列的表,其中用户无法指定自定义值。

在 Oracle 中,您有两种创建自动递增列的方法。

在第一个示例中,如果用户不指定值,则 id 列会自动递增。这是 SERIAL 在 postgres 中的当前实现。

CREATE TABLE identity_test_tab ( 
   id NUMBER GENERATED BY DEFAULT AS DENTITY,
   description VARCHAR2(30) 
);

在下一个示例中,id 列总是自动递增,用户不能指定任何其他值。

CREATE TABLE identity_test_tab (
  id          NUMBER ALWAYS AS IDENTITY,
  description VARCHAR2(30)
);

我想知道 postgres 中第二个例子的等价物吗?

【问题讨论】:

  • 您需要一个触发器来执行此操作。
  • 触发器应“重写”序列中的值,尽管用户定义了值。顺便说一句 - 这种行为会给用户带来他的值被接受的错觉,而触发器会消耗序列中的值
  • @VaoTsun:如果用户提供了值,您总是可以在触发器中抛出异常
  • 是的,但随后行没有进入表格。大多数应用程序都会忽略发出通知
  • @VaoTsun:不,引发异常将中止事务。应用程序不能忽略它。看我的回答

标签: database oracle postgresql


【解决方案1】:

可以通过仅对特定列授予插入来完成:

drop table if exists t;
create table t(i serial, x text, y int);
grant insert (x,y) on table t to abelisto;
grant usage on sequence t_i_seq to abelisto;
grant select on table t to abelisto;

然后:

postgres=# insert into t(i,x,y) values(10,'x',1);
ERROR:  permission denied for relation t
postgres=# insert into t(i,x,y) values(default,'x',1);
ERROR:  permission denied for relation t
postgres=# insert into t(x,y) values('x',1);
INSERT 0 1
postgres=# select * from t;
 i | x | y 
---+---+---
 1 | x | 1
(1 row)

【讨论】:

    【解决方案2】:

    如果要确保始终从序列中获取值,则需要触发器。 Postgres 中没有与 generated always 等效的功能:

    create table foo (id integer not null primary key);
    create sequence foo_id_seq;
    alter sequence foo_id_seq owned by foo.id; -- this is essentially what `serial` does in the background
    
    create function generate_foo_id()
      returns trigger
    as
    $$
    begin
      new.id := nextval('foo_id_seq');
      return new;
    end;
    $$
    language plpgsql;    
    
    create trigger foo_id_trigger
      before insert on foo
      for each row execute procedure generate_foo_id();
    

    上面将默默地用序列值替换任何用户为foo.id 提供的值。如果您希望在执行此操作时出现显式错误,请在触发函数中引发异常:

    create function generate_foo_id()
      returns trigger
    as
    $$
    begin
      if new.id is not null then 
         raise 'No manual value for id allowed';
      end if;
      new.id := nextval('foo_id_seq');
      return new;
    end;
    $$
    language plpgsql;
    

    引发异常会中止当前事务,并将强制插入值的应用程序回滚并正确执行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-21
      • 2021-11-07
      • 1970-01-01
      • 2015-02-15
      • 2021-12-30
      相关资源
      最近更新 更多