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


64

我有一张桌子:

CREATE TABLE names (id serial, name varchar(20))

我想要该表中的“最后插入的ID”,而无需RETURNING id在插入时使用。似乎有一个功能CURRVAL(),但我不知道如何使用它。

我尝试过:

SELECT CURRVAL() AS id FROM names_id_seq
SELECT CURRVAL('names_id_seq')
SELECT CURRVAL('names_id_seq'::regclass)

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


2
该问题/解决方案的读者应该意识到,通常不鼓励使用currval(),因为RETURNING子句提供了标识符,而没有附加查询的开销,并且没有可能返回WRONG VALUE(该currval将在一些用例。)
chander

1
@chander:您对此主张有任何参考吗?使用的currval() 是最肯定没有气馁。
a_horse_with_no_name

关于是否不建议使用currval,可能是一个意见问题,但在某些情况下,用户应注意,它可能提供的值并非您所期望的(因此,请在支持的情况下使RETURNING成为更好的选择。)假设您在表A中使用序列a_seq,在表B中也使用a_seq(为PK列调用nextval('a_seq')。)假设您还有一个触发器(a_trg)插入表B中,并在插入表A时插入。这种情况下,使用currval()函数(在表A中插入后)将返回用于在表B中,不表A.插入产生的数
钱德尔

Answers:


51

如果您创建列,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=>

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

select currval(pg_get_serial_sequence('names', 'id'));

这样,您就不必依赖Postgres使用的命名策略。

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


我想currval()在多用户设置中使用它不是很好。例如在网络服务器上。
乔纳斯(Jonas)

9
不,你误会了。currval()对您当前的连接“本地”。因此,在多用户环境中使用它没有问题。这就是序列的全部目的。
a_horse_with_no_name 2011年

1
在极少数情况下,您的插入会在同一表中触发更多插入,这可能会中断,不是吗?
leonbloy 2014年

1
@a_horse_with_no_name:我不是在考虑多个插入(很容易发现),而是考虑在表上定义的(也许是未知的)触发器。例如,在上面尝试:gist.github.com/anonymous/9784814
leonbloy 2014年

1
并非总是表和列名。如果已经有一个具有该名称的序列,则它将通过串联或增加一个数字来生成一个新序列,如果超过限制(它看起来像是62个字符),则可能需要缩短该名称。
PhilHibbs16年

47

这直接来自 堆栈溢出

正如@a_horse_with_no_name和@Jack Douglas指出的那样,currval仅适用于当前会话。因此,如果您对结果可能会受到另一个会话的未提交事务的影响感到满意,而您仍然希望某些东西可以跨会话使用,则可以使用以下方法:

SELECT last_value FROM your_sequence_name;

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

不过,从Postgres文档中可以清楚地看出:

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

所以我想严格来说,为了在会话之间的序列中正确使用currval或last_value,您需要执行类似的操作吗?

SELECT setval('serial_id_seq',nextval('serial_id_seq')-1);

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


3
我想不出什么情况会有用。
ypercubeᵀᴹ

我只是想知道如果当前会话中未调用nextval,这是否是一种获取currval的方法。有什么建议吗?
Slak 2014年

当我对生成的夹具数据的主键进行硬编码时,我必须这样做,因为我希望可以提前确定通常将以增量方式生成的PK值,以简化客户端测试。为了在通常受a的默认值支配的列上执行插入操作时支持插入,nextval()您必须手动设置顺序以匹配使用硬编码ID插入的灯具记录的数量。同样,解决currval()/ lastval()在下一个nextval之前不可用的问题的方法是SELECT直接在序列上。
彼得·埃里亚斯

@ypercubeᵀᴹ相反,我认为没有充分的理由来使用所选的“正确”答案。此答案不需要在表中插入记录。这也回答了这个问题。同样,我认为没有充分的理由不对所选的答案使用此答案。
Henley Chiu

1
这个答案根本不涉及修改表。被检查的一个。理想情况下,您只想知道最后一个ID,而无需进行任何更改。如果这是生产数据库怎么办?您不能只插入随机的行而不会造成打击。因此,这个答案既安全又正确。
Henley Chiu

14

需要nextval在此会话中为此序列调用以下命令currval

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

select currval('serial');
 currval
---------
       1
(1 row)

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

正如a_horse的答案所指出的,create table具有类型为column的列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)

3

因此,这些各种方法存在一些问题:

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

在插入操作之后获取分配的唯一标识符的最佳方法是使用RETURNING子句。下面的示例假定与该序列绑定的列称为“ id”:

insert into table A (cola,colb,colc) values ('val1','val2','val3') returning id;

请注意,RETURNING子句的用途远远超出了获得序列的范围,因为它还将:

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

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

  • 在UPDATE之后返回修改后的行:

    更新表A集合X ='y',blah ='blech'返回*

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

    用A作为(从表A删除*作为返回ID)更新B设置delete = true,其中id在(从A选择ID);


当然,OP 明确表示他们不想使用RETURNING子句-但是,让其他人更清楚使用它的好处没有错。
RDFozz

我实际上只是在尝试指出其他各种方法的陷阱(因为除非谨慎操作,否则它可能会返回预期值以外的其他值),并阐明最佳做法。
chander

1

尽管使用SQLALchemy,但我仍然必须执行查询,因为使用currval失败。

nextId = db.session.execute("select last_value from <table>_seq").fetchone()[0] + 1

这是一个python flask + postgresql项目。



1

您需要GRANT在模式上使用,如下所示:

GRANT USAGE ON SCHEMA schema_name to user;

GRANT ALL PRIVILEGES ON schema_name.sequence_name TO user;

1
欢迎来到dba.se!为您的第一篇文章加油!看起来原始帖子似乎不是专门针对权限的。也许在调用该currval()函数使其与该线程更加相关时考虑扩大您的答案以包含有关权限失败的一些细节?
彼得·范迪维尔

0

在PostgreSQL 11.2中,您可以像看表一样对待序列:

例如,如果您有一个名为:“ names_id_seq”的序列

select * from names_id_seq;
 last_value | log_cnt | is_called
------------+---------+-----------
          4 |      32 | t
(1 row)

这应该为您提供最后插入的ID(在这种情况下为4),这意味着当前值(或接下来应用于ID的值)应为5。


-2

不同版本的PostgreSQL可能具有不同的功能来获取当前或下一个序列ID。

首先,您必须了解Postgres的版本。使用select version(); 获取版本。

在PostgreSQL 8.2.15中,您可以使用获取当前序列ID select last_value from schemaName.sequence_name

如果以上陈述无效,您可以使用 select currval('schemaName.sequence_name');


2
任何不同版本的证明都不同吗?
dezso
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.