如何在 PostgreSQL 中使用 currval() 来获取最后插入的 id?

Jon*_*nas 81 postgresql sequence

我有一张桌子:

CREATE TABLE names (id serial, name varchar(20))
Run Code Online (Sandbox Code Playgroud)

我想要那个表中的“最后插入的 id”,而不是RETURNING id在插入时使用。好像有一个函数CURRVAL(),但我不明白如何使用它。

我试过:

SELECT CURRVAL() AS id FROM names_id_seq
SELECT CURRVAL('names_id_seq')
SELECT CURRVAL('names_id_seq'::regclass)
Run Code Online (Sandbox Code Playgroud)

但它们都不起作用。如何使用currval()获取最后插入的 ID?

小智 72

这直接来自 Stack Overflow

正如@a_horse_with_no_name 和@Jack Douglas 指出的那样,currval 仅适用于当前会话。因此,如果您认为结果可能会受到另一个会话的未提交事务的影响,并且您仍然希望可以跨会话工作,则可以使用以下命令:

SELECT last_value FROM your_sequence_name;
Run Code Online (Sandbox Code Playgroud)

使用指向 SO 的链接以获取更多信息。

但是,从Postgres 文档中,它明确指出

如果在当前会话中尚未调用 nextval,则调用 lastval 是错误的。

所以我想严格来说,为了在会话中正确使用 currval 或 last_value 序列,您需要做类似的事情吗?

SELECT setval('serial_id_seq',nextval('serial_id_seq')-1);
Run Code Online (Sandbox Code Playgroud)

当然,假设您在当前会话中没有使用序列字段的插入或任何其他方式。

  • 我想不出这会有用的情况。 (3认同)
  • 这个答案根本不涉及修改表格。检查的一个。理想情况下,您只想知道最后一个 ID,而无需进行任何更改。如果这是一个生产数据库怎么办?您不能在没有可能的打击乐的情况下插入随机行。因此,这个答案更安全,也更正确。 (2认同)

a_h*_*ame 70

如果您创建一个列作为serialPostgreSQL 会自动为此创建一个序列。

序列的名称是自动生成的,并且始终是 tablename_columnname_seq,在您的情况下,序列将是 names names_id_seq

插入表后,您可以currval()使用该序列名称调用:

postgres=> CREATE TABLE names in schema_name (id serial, name varchar(20));
CREATE TABLE
postgres=> insert into names (name) values ('Arthur Dent');
INSERT 0 1
postgres=> select currval('names_id_seq');
 currval
---------
       1
(1 row)
postgres=>
Run Code Online (Sandbox Code Playgroud)

除了硬编码序列名称外,您还可以使用pg_get_serial_sequence()

select currval(pg_get_serial_sequence('names', 'id'));
Run Code Online (Sandbox Code Playgroud)

这样你就不需要依赖 Postgres 使用的命名策略。

或者,如果您根本不想使用序列名称,请使用 lastval()

  • 不,你错了。`currval()` 是您当前连接的“本地”。所以在多用户环境下使用是没有问题的。这就是序列的全部目的。 (12认同)

Jac*_*las 15

需要nextval在此会话中调用此序列之前currval

create sequence serial;
select nextval('serial');
 nextval
---------
       1
(1 row)

select currval('serial');
 currval
---------
       1
(1 row)
Run Code Online (Sandbox Code Playgroud)

因此,除非insert在同一会话中完成,否则您无法从序列中找到“最后插入的 id” (事务可能会回滚,但序列不会)

正如 a_horse 的回答中所指出的,create table使用类型的列serial将自动创建一个序列并使用它来生成该列的默认值,因此insert通常nextval隐式访问:

create table my_table(id serial);
NOTICE:  CREATE TABLE will create implicit sequence "my_table_id_seq" for 
         serial column "my_table.id"

\d my_table
                          Table "stack.my_table"
 Column |  Type   |                       Modifiers
--------+---------+-------------------------------------------------------
 id     | integer | not null default nextval('my_table_id_seq'::regclass)

insert into my_table default values;
select currval('my_table_id_seq');
 currval
---------
       1
(1 row)
Run Code Online (Sandbox Code Playgroud)


小智 5

因此,这些不同的方法存在一些问题:

Currval 只获取当前会话中生成的最后一个值 - 如果您没有其他任何生成值的东西,这很好,但在您可能调用触发器和/或在当前事务中多次推进序列的情况下,它是不会返回正确的值。对于 99% 的人来说,这不是问题 - 但这是一个人应该考虑的问题。

获得插入操作后分配的唯一标识符的最佳方法是使用 RETURNING 子句。下面的示例假设与序列相关的列称为“id”:

insert into table A (cola,colb,colc) values ('val1','val2','val3') returning id;
Run Code Online (Sandbox Code Playgroud)

请注意, RETURNING 子句的用处远不止获取序列,因为它还可以:

  • 返回用于“最终插入”的值(例如,在 BEFORE 触发器可能更改了正在插入的数据之后。)
  • 返回被删除的值:

    从表 A 中删除,其中 id > 100 返回 *

  • 更新后返回修改后的行:

    更新表 A set X='y' where blah='blech' 返回 *

  • 使用删除的结果进行更新:

    WITH A as (delete * from table A as returns id) update B set deleted=true where id in (select id from A);