在Postgres

时间:2017-09-12 09:28:42

标签: postgresql auto-increment

我正在从MariaDB切换到Postgres,并遇到了一个小问题。有时我需要在进行INSERT之前建立下一个AUTO_INCREMENT值。这是因为INSERT对其他一些表有影响,如果在INSERT本身后完成修复将非常麻烦。在mySQL / MariaDB中,这很简单。我只是做了

"SELECT AUTO_INCREMENT 
 FROM information_schema.tables 
 WHERE table_name = 'users' 
       AND table_schema = DATABASE( ) ;";

并使用返回的值在进行实际INSERT之前预先校正其他表。我知道使用pgSQL可以将RETURNING用于SELECT,INSERT和UPDATE语句。但是,这需要对其他表进行INSERT后更正,这反过来会涉及破坏已经过测试并证明有效的代码。我想有一种方法可以找到下一个AUTO_INCREMENT,但我一直无法找到它。除了其他事情,我尝试nextval('users_id_seq')没有做任何有用的事情。希望有人能够提供帮助。

要将我的原始MariaDB架构移植到Postgres,我使用MariaDB版本编辑了Adminer发出的SQL,以确保它与Postgres一起使用。这主要涉及将INT(11)更改为INTEGER,TINYINT(3)更改为SMALL INT,VARCHAR更改为CHARACTER VARYING等。使用自动增量列,我读了一下并得出结论我需要使用SERIAL。因此,我给Postgres提供的典型SQL就是这样的

CREATE TABLE "users" 
(
 "id" SERIAL NOT NULL,
 "bid" INTEGER  NOT NULL DEFAULT 0,
 "gid" INTEGER  NOT NULL DEFAULT 0,
 "sid" INTEGER  NOT NULL DEFAULT 0,
 "s1" character varying(64)NOT NULL,
 "s2" character varying(64)NOT NULL,
 "name" character varying(64)NOT NULL,
 "apik" character varying(128)NOT NULL,
 "email" character varying(192)NOT NULL,
 "gsm" character varying(64)NOT NULL,
 "rights" character varying(64)NOT NULL,
 "managed" character varying(256)NOT NULL DEFAULT 
 'M_BepHJXALYpLyOjHxVGWJnlAMqxv0KNENmcYA,,',
  "senior" SMALLINT  NOT NULL DEFAULT 0,
  "refs" INTEGER  NOT NULL DEFAULT 0,
  "verified" SMALLINT  NOT NULL DEFAULT 0,
  "vkey" character varying(64)NOT NULL,
  "lang" SMALLINT  NOT NULL DEFAULT 0,
  "leader" INTEGER  NOT NULL
 );

Adminer的这个SQL运行正常。但是,当我尝试让Adminer导出Postgres中的新users表时,它给了我

CREATE TABLE "public"."users" 
(
 "id" integer DEFAULT nextval('users_id_seq') NOT NULL,
 "bid" integer DEFAULT 0 NOT NULL,

在移植AUTO_INCREMENT列时,我可能会错误地解决问题 - 在这种情况下仍有时间纠正错误。

3 个答案:

答案 0 :(得分:2)

As documented in the manual serial不是“真正的”数据类型,它只是从列中获取默认值的列的快捷方式。

如果在插入之前需要在代码中生成值,请使用nextval(),然后使用insert语句中的值:

在PL / pgSQL中,这将类似于以下内容。确切的语法显然取决于您使用的编程语言:

declare
  l_userid integer;
begin
  l_userid := nextval('users_id_seq');
  -- do something with that value
  insert into users (id, ...)
  values (l_userid, ...);
end;

重要的是,永远不会将值传递给序列生成的的插入语句。 Postgres不会自动将序列值与“手动”提供的值同步。

答案 1 :(得分:0)

如果您在列定义中使用了serial,那么您在表的同一名称空间中有一个名为TABLE_COLUMN_seq的序列(其中TABLECOLUMN分别位于表和列的名称)。你可以这样做:

SELECT nextval('TABLE_COLUMN_seq');

我看到你试过了,你能否展示你的CREATE TABLE声明,以便我们检查所有名字是否合适?

答案 2 :(得分:0)

您可以从序列本身中选择last_value+1,例如:

t=# create table so109(i serial,n int);
CREATE TABLE
Time: 2.585 ms
t=# insert into so109(n) select i from generate_series(1,22,1) i;
INSERT 0 22
Time: 1.236 ms
t=# select * from so109_i_seq ;
 sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called
---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
 so109_i_seq   |         22 |           1 |            1 | 9223372036854775807 |         1 |           1 |      11 | f         | t
(1 row)

或使用currval,例如:

t=# select currval('so109_i_seq')+1;
 ?column?
----------
       23
(1 row)

<强>更新

虽然这个答案给出了如何确定Postgres中INSERT之前的下一个auto_increment值(这是标题)的想法,但是提出的方法不适合发布本身的需要。如果您正在寻找&#34;替换&#34;对于RETURNING语句中的INSERT指令,更好的方法实际上是&#34;保留&#34; nextval的值,就像@fog建议的那样。因此,并发事务不会两次获得相同的值......