我有一个表:

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?

评论

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

@chander:您对此主张有参考吗?绝对不建议使用currval()。

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

#1 楼

如果您将列创建为serial,则PostgreSQL会自动为此创建序列。

插入表格后,您可以使用以下序列名称调用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=>


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

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

评论


我猜在多用户设置中使用currval()不好。例如在网络服务器上。

–乔纳斯(Jonas)
11年6月13日在11:29

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

– a_horse_with_no_name
2011年6月13日11:31



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

–leonbloy
2014年3月26日13:58

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

–leonbloy
2014年3月26日14:36

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

– PhilHibbs
16年7月21日在15:12

#2 楼

这直接来自Stack Overflow

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

但是从Postgres文档中可以清楚地表明,


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

因此严格来说,为了正确地在会话中对序列使用currval或last_value,您需要做类似的事情吗?

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

评论


我想不出什么情况会有用。

–超立方体ᵀᴹ
2014年10月2日,11:14

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

–Slak
2014年10月2日,14:30

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

– Peter M. Elias
16-09-19在17:07



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

–邱汉ley
17年2月13日在21:09

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

–邱汉ley
17-2-14在21:26



#3 楼

您需要在此会话中为此序列调用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的列的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)


#4 楼

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

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

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

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


请注意,RETURNING子句的作用远不只是获取序列,因为它也会:


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

返回要删除的值:

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


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

更新表一组X ='y',其中blah ='blech'返回*


使用删除结果进行更新:

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



评论


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

–RDFozz
17年8月4日在14:41

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

–钱德
17年8月8日在20:27

#5 楼

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

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


这是一个python flask + postgresql项目。

#6 楼

如果您想获取序列的值而无需调用nextval(),而不必修改序列,我将PL / pgSQL函数组合在一起来处理它:查找所有数据库序列的值

#7 楼

您需要在架构上使用GRANT,例如:

GRANT USAGE ON SCHEMA schema_name to user;




GRANT ALL PRIVILEGES ON schema_name.sequence_name TO user;


评论


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

– Peter Vandivier
18/12/12在15:36

#8 楼

在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。

#9 楼

select  *, 'select '''||secuencia||''' as secuencia, nextval('''||secuencia||''') as currentval, (select max('||campo||') from '||tabla||') as maxtabla UNION ALL ' as sentencia from (
select 
relname as secuencia
, substr(relname,0, strpos(relname, SPLIT_PART(relname, '_', nseparadores))-1 ) as tabla -- tabla sobre el que hace referencia la secuencia
--, strpos(relname, SPLIT_PART(relname, '_', nseparadores)) as posicion_campo --posición donde comienza el nombre del campo
,SPLIT_PART(relname, '_', nseparadores) as campo -- campo sobre el que hace referencia la secuencia
from (
select nspname, relname, length(relname) - length(replace(relname, '_', '')) as nseparadores  from pg_class c join pg_namespace n on c.relnamespace=n.oid where relkind = 'S' order by nspname
) sentencia01
) sentencia02


评论


这个代码片段确实可以回答这个问题,但是,如果添加一个解释说明它如何工作以及为什么它与所有其他答案不同的话,它将更好。

–必须
6月19日18:54

#10 楼

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

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

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

如果上述语句不起作用,则可以使用select currval('schemaName.sequence_name');

评论


任何不同版本的证明都不同吗?

– dezso
15年10月28日在16:34