在 Postgres 中的 INSERT 之前确定下一个 auto_increment 值

Determine next auto_increment value before an INSERT in Postgres

我正在从 MariaDB 切换到 Postgres,遇到了一个小问题 运行。有时我需要在进行 INSERT 之前建立下一个 AUTO_INCREMENT 值。这是因为 INSERT 对其他一些 table 有影响,如果完成 post INSERT 本身,修复起来会很麻烦。在 mySQL/MariaDB 这很容易。我只是做了

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

并在进行实际插入之前使用返回值预先更正其他 table。我知道使用 pgSQL 可以使用 RETURNINGwith SELECT,INSERT 和 UPDATE 语句。但是,这将需要对其他 table 进行 post-INSERT 更正,这反过来又会涉及破坏已经过测试并证明有效的代码。我想象有一种方法可以找到下一个 AUTO_INCREMENT,但我一直找不到。除其他外,我尝试了 nextval('users_id_seq') 但没有做任何有用的事情。

为了将我原来的 MariaDB 模式移植到 Postgres,我编辑了管理员使用 MariaDB 版本发出的 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
 );

来自管理员的 SQL 运行 工作正常。但是,当我随后尝试让管理员在 Postgres 中导出新的 users table 时,它给了我

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

可能是我在移植 AUTO_INCREMENT 列时处理不当 - 在这种情况下,仍有时间更正错误。

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

SELECT nextval('TABLE_COLUMN_seq');

我看到你已经尝试过了,你能出示你的CREATE TABLE声明以便我们检查所有名称是否正确吗?

您可以 select 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 值的想法,但建议的方法不适合post 本身的需要。如果您在 INSERT 语句中为 RETURNING 指令寻找 "replacement",更好的方法实际上是 "reserving" 带有 nextval 的值,就像@fog 提议的那样。因此并发事务不会两次获得相同的值...

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

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

在 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 不会自动将序列值与 "manually" 提供的值同步。