PostgreSQL序列的下一个值是什么?

48

我正在为我的Codeigniter网站使用PostgreSQL。在添加、编辑和删除操作中,我使用grocery crud。在进行编辑或添加时,我想根据内容的id动态地重新命名已上传的文件。我能够使用grocery crud的callback_after_upload函数来实现这一点。

当我添加一个新内容时,我想要得到下一个内容的id。我尝试使用nextval()函数,但它会增加序列。有没有不使用nextval()函数就能获得序列的最后一个值的简单方法?

或者有没有其他简单的方法可以做到这一点?


@saji89:实际上,这已经过时且效率低下。你可以通过一次与服务器的往返来完成此操作。我添加了一个答案。 - Erwin Brandstetter
@ErwinBrandstetter 值得一提的是,mu演示了一个单独的nextval调用,它被用于插入操作。与INSERT ... RETURNING不同的是,两者都可以通过查询生成引擎和其他可能不理解RETURNING扩展的痛苦事物来工作 :S - Craig Ringer
1
@CraigRinger: 如果你的查询生成器不理解“RETURNING”子句(这个特性已经存在多年了!),那么它可能也已经过时了 - 或者只是一个非常糟糕的替代品,正在寻找替换。尽管如此,像 a_horse 提供的后备解决方案还是很好的。毫无争议。 - Erwin Brandstetter
8个回答

44

返回

只需进行一次数据库往返即可实现:

INSERT INTO tbl(filename)
VALUES ('my_filename')
RETURNING tbl_id;

tbl_id 通常是一个 serialIDENTITY (Postgres 10 或更新版本) 列。详见手册

显式获取值

如果filename需要包含tbl_id(冗余),您仍然可以使用单个查询。

使用lastval()或更具体的currval()

INSERT INTO tbl (filename)
VALUES ('my_filename' || currval('tbl_tbl_id_seq')   -- or lastval()
RETURNING tbl_id;

参见:

如果在过程中可能会推进多个序列(甚至通过触发器或其他副作用),则确定的方法是使用currval('tbl_tbl_id_seq')

序列名称

在我的示例中,'tbl_tbl_id_seq'这个字符串文字应该是序列的实际名称,并被强制转换为regclass,如果在当前的search_path中找不到该名称的序列,则会引发异常。

tbl_tbl_id_seq是带有序列列tbl_id的表格tbl的自动生成默认值。但是并没有保证。如果定义了列默认值可以从任何序列中获取值。如果在创建表时使用该默认名称,则Postgres根据简单算法选择下一个可用名称。

如果您不知道serial列的序列名称,请使用专用函数pg_get_serial_sequence()。可以即时执行:

INSERT INTO tbl (filename)
VALUES ('my_filename' || currval(pg_get_serial_sequence('tbl', 'tbl_id'))
RETURNING tbl_id;

在这里使用db<>fiddle
旧版sqlfiddle


但是,如果生成的值应该被合并到正在插入的值中,那么这并没有帮助。我对“以便我可以动态命名上传的文件”的理解是,i_nomad想要将生成的ID放入正在插入到表中的一个值(文件名)中。但问题非常不清楚,很难说。 - user330315
@a_horse_with_no_name:tbl_id已经在序列列中表示了。但是如果您想将其包含在“文件名”中,也可以这样做。我在我的答案中添加了一点内容。 - Erwin Brandstetter

37

使用currval()函数可以访问序列之前获取的值。

但是,这只有在先调用nextval()函数才能返回一个值。

绝对没有办法"窥视"序列的下一个值而不实际获取它。

但是您的问题不清楚。如果在插入操作之前调用nextval(),则可以在插入中使用该值。或者更好的方法是,在您的插入语句中使用currval()

select nextval('my_sequence') ...

... do some stuff with the obtained value

insert into my_table(id, filename)
values (currval('my_sequence'), 'some_valid_filename');

1
我正在使用 grocery crud 进行插入、删除和编辑操作。我正在使用“callback_after_upload”函数来重命名文件。我没有传递插入的 id。 - i_nomad
4
这个“杂货店”的话题突然从哪儿冒出来的?你之前问了一个关于在PostgreSQL中使用序列的问题。 - user330315
是的,这与在PostgreSQL中使用序列有关,我只是在解释我正在尝试使用序列的'nextval'做什么。 - i_nomad

13

我偶然遇到了这个问题,因为我想通过表格找到下一个序列值。虽然这并没有回答我的问题,但这就是如何完成它,它可能会帮助那些寻找不通过名称而是通过表格来查找序列值的人:

SELECT nextval(pg_get_serial_sequence('<your_table>', 'id')) AS new_id; 

希望对您有所帮助:)


8

2022年的答案

如果您知道序列的名称,可以使用pg_sequence_last_value()

SELECT pg_sequence_last_value('public.person_id_seq');

1
这不是一个 PostgreSQL 函数。 - user330315
https://www.postgresql.org/message-id/flat/CAAaqYe_YXrkpLb2xZ0Vxi9hNnwn6tDeE3XAHw1YN8boBjaNX4Q%40mail.gmail.com#fabb846529213ba918c6d7554c962798 @a_horse_with_no_name 就是这样。 - jian
https://www.postgresql.org/message-id/flat/CAAaqYe_YXrkpLb2xZ0Vxi9hNnwn6tDeE3XAHw1YN8boBjaNX4Q%40mail.gmail.com#fabb846529213ba918c6d7554c962798 @a_horse_with_no_name 是的... - undefined
嗨,链接已损坏。 - jian
嗨,链接坏了。 - undefined

5
如果您没有会话,只需使用nextval('your_sequence_name')即可。

2

字面上回答你的问题,以下是如何获取序列的下一个值而不增加它:

SELECT
 CASE WHEN is_called THEN
   last_value + 1
 ELSE
   last_value
 END
FROM sequence_name

显然,在实际应用中使用这段代码并不是一个好主意。不能保证下一行真的会有这个ID。 然而,为了调试目的,知道序列的值而不增加它可能是有趣的,这就是你可以做到的。


0

我尝试了这个,它完美地运行了。

@Entity
public class Shipwreck {
  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq")
  @Basic(optional = false)
  @SequenceGenerator(name = "seq", sequenceName = "shipwreck_seq", allocationSize = 1)
  Long id;

....

CREATE SEQUENCE public.shipwreck_seq
    INCREMENT 1
    START 110
    MINVALUE 1
    MAXVALUE 9223372036854775807
    CACHE 1;

-2
即使可以以某种方式完成这个操作,这仍然是一个可怕的想法,因为可能会出现一个序列被另一条记录使用的情况!
更好的做法是先保存记录,然后再检索序列。

这不是真的。请参阅https://www.postgresql.org/docs/8.1/static/functions-sequence.html。 - helvete

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接