PostgreSQL 函数获取最后插入的 ID

发布于 2024-09-04 00:33:57 字数 153 浏览 5 评论 0原文

在 PostgreSQL 中,如何获取插入表中的最后一个 id?

在 MS SQL 中,有 SCOPE_IDENTITY()。

请不要建议我使用这样的东西:

select max(id) from table

In PostgreSQL, how do I get the last id inserted into a table?

In MS SQL there is SCOPE_IDENTITY().

Please do not advise me to use something like this:

select max(id) from table

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

沉默的熊 2024-09-11 00:33:57

( tl;dr : goto option 3: INSERT with RETURNING )

回想一下,在 postgresql 中,表没有“id”概念,只有序列(通常但不一定)用作代理主键的默认值,带有 SERIAL伪类型 - 或者,从版本 10 开始,使用 生成为身份)。

如果您有兴趣获取新插入行的 id,有多种方法:


选项 1: CURRVAL(<序列名称>);

例如:

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval('persons_id_seq');

序列的名称必须是已知的,它真的是任意的;在此示例中,我们假设表 persons 具有使用 SERIAL 伪类型创建的 id 列。为了避免依赖于此并感觉更干净,您可以使用 < code>pg_get_serial_sequence

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval(pg_get_serial_sequence('persons','id'));

警告:currval() 仅在 INSERT 后有效(已执行 nextval() ),在同一会话中


选项 2: LASTVAL();

这与前一个类似,只是您不需要指定序列名称:它会查找最近修改的序列(始终在您的会话内,与上面相同的警告)。


CURRVALLASTVAL 都是完全并发安全的。 PG 中序列的行为经过精心设计,以便不同的会话不会干扰,因此不存在竞争条件的风险(如果另一个会话在我的 INSERT 和 SELECT 之间插入另一行,我仍然会得到正确的值)。

但是他们确实有一个微妙的潜在问题。如果数据库有一些 TRIGGER (或规则),则在插入时到 persons 表中,在其他表中进行一些额外的插入...然后 LASTVAL 可能会给我们错误的值。如果在同一个 persons 表中进行额外的插入,则 CURRVAL 甚至可能会出现此问题(这种情况不太常见,但风险仍然存在)。


选项 3: INSERT RETURNING

INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John') RETURNING id;

这是获取 id 的最干净、最高效、最安全的方法。它没有以前的任何风险。

缺点?几乎没有:您可能需要修改调用 INSERT 语句的方式(在最坏的情况下,也许您的 API 或 DB 层不期望 INSERT 返回值);它不是标准 SQL(谁在乎);它从 Postgresql 8.2(2006 年 12 月)开始可用。


结论:如果可以的话,请选择选项 3。在其他地方,更喜欢选项 1。

注意:如果您打算获取全局最后插入的 id,所有这些方法都是无用的。 strong>(不一定是您的会话)。为此,您必须求助于SELECT max(id) FROM table(当然,这不会从其他事务中读取未提交的插入)。

相反,您应该永远使用SELECT max(id) FROM table而不是上面的3个选项之一来获取刚刚由您的INSERT 语句,因为(除了性能之外)这不是并发安全的:在您的 INSERTSELECT 之间,另一个会话可能插入了另一条记录。

( tl;dr : goto option 3: INSERT with RETURNING )

Recall that in postgresql there is no "id" concept for tables, just sequences (which are typically but not necessarily used as default values for surrogate primary keys, with the SERIAL pseudo-type - or, since version 10, with GENERATED AS IDENTITY).

If you are interested in getting the id of a newly inserted row, there are several ways:


Option 1: CURRVAL(<sequence name>);.

For example:

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval('persons_id_seq');

The name of the sequence must be known, it's really arbitrary; in this example we assume that the table persons has an id column created with the SERIAL pseudo-type. To avoid relying on this and to feel more clean, you can use instead pg_get_serial_sequence:

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval(pg_get_serial_sequence('persons','id'));

Caveat: currval() only works after an INSERT (which has executed nextval() ), in the same session.


Option 2: LASTVAL();

This is similar to the previous, only that you don't need to specify the sequence name: it looks for the most recent modified sequence (always inside your session, same caveat as above).


Both CURRVAL and LASTVAL are totally concurrent safe. The behaviour of sequence in PG is designed so that different session will not interfere, so there is no risk of race conditions (if another session inserts another row between my INSERT and my SELECT, I still get my correct value).

However they do have a subtle potential problem. If the database has some TRIGGER (or RULE) that, on insertion into persons table, makes some extra insertions in other tables... then LASTVAL will probably give us the wrong value. The problem can even happen with CURRVAL, if the extra insertions are done intto the same persons table (this is much less usual, but the risk still exists).


Option 3: INSERT with RETURNING

INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John') RETURNING id;

This is the most clean, efficient and safe way to get the id. It doesn't have any of the risks of the previous.

Drawbacks? Almost none: you might need to modify the way you call your INSERT statement (in the worst case, perhaps your API or DB layer does not expect an INSERT to return a value); it's not standard SQL (who cares); it's available since Postgresql 8.2 (Dec 2006...)


Conclusion: If you can, go for option 3. Elsewhere, prefer 1.

Note: all these methods are useless if you intend to get the last inserted id globally (not necessarily by your session). For this, you must resort to SELECT max(id) FROM table (of course, this will not read uncommitted inserts from other transactions).

Conversely, you should never use SELECT max(id) FROM table instead one of the 3 options above, to get the id just generated by your INSERT statement, because (apart from performance) this is not concurrent safe: between your INSERT and your SELECT another session might have inserted another record.

友谊不毕业 2024-09-11 00:33:57

请参阅 INSERT 语句的 RETURNING 子句。基本上,INSERT 兼作查询并返回插入的值。

See the RETURNING clause of the INSERT statement. Basically, the INSERT doubles as a query and gives you back the value that was inserted.

简单 2024-09-11 00:33:57

Leonbloy的答案非常完整。我只会添加一种特殊情况,即需要从 PL/pgSQL 函数中获取最后插入的值,而选项 3 并不完全适合。

例如,如果我们有下表:

CREATE TABLE person(
   id serial,
   lastname character varying (50),
   firstname character varying (50),
   CONSTRAINT person_pk PRIMARY KEY (id)
);

CREATE TABLE client (
    id integer,
   CONSTRAINT client_pk PRIMARY KEY (id),
   CONSTRAINT fk_client_person FOREIGN KEY (id)
       REFERENCES person (id) MATCH SIMPLE
);

如果我们需要插入客户记录,我们必须引用人员记录。但是,假设我们想要设计一个 PL/pgSQL 函数,它将新记录插入客户端,同时也负责插入新人员记录。为此,我们必须使用 leonbloy 的选项 3 的细微变化:

INSERT INTO person(lastname, firstname) 
VALUES (lastn, firstn) 
RETURNING id INTO [new_variable];

请注意,有两个 INTO 子句。因此,PL/pgSQL 函数的定义如下:

CREATE OR REPLACE FUNCTION new_client(lastn character varying, firstn character varying)
  RETURNS integer AS
$BODY$
DECLARE
   v_id integer;
BEGIN
   -- Inserts the new person record and retrieves the last inserted id
   INSERT INTO person(lastname, firstname)
   VALUES (lastn, firstn)
   RETURNING id INTO v_id;

   -- Inserts the new client and references the inserted person
   INSERT INTO client(id) VALUES (v_id);

   -- Return the new id so we can use it in a select clause or return the new id into the user application
    RETURN v_id;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE;

现在我们可以使用以下命令插入新数据:

SELECT new_client('Smith', 'John');

SELECT * FROM new_client('Smith', 'John');

我们得到新创建的 id。

new_client
integer
----------
         1

Leonbloy's answer is quite complete. I would only add the special case in which one needs to get the last inserted value from within a PL/pgSQL function where OPTION 3 doesn't fit exactly.

For example, if we have the following tables:

CREATE TABLE person(
   id serial,
   lastname character varying (50),
   firstname character varying (50),
   CONSTRAINT person_pk PRIMARY KEY (id)
);

CREATE TABLE client (
    id integer,
   CONSTRAINT client_pk PRIMARY KEY (id),
   CONSTRAINT fk_client_person FOREIGN KEY (id)
       REFERENCES person (id) MATCH SIMPLE
);

If we need to insert a client record we must refer to a person record. But let's say we want to devise a PL/pgSQL function that inserts a new record into client but also takes care of inserting the new person record. For that, we must use a slight variation of leonbloy's OPTION 3:

INSERT INTO person(lastname, firstname) 
VALUES (lastn, firstn) 
RETURNING id INTO [new_variable];

Note that there are two INTO clauses. Therefore, the PL/pgSQL function would be defined like:

CREATE OR REPLACE FUNCTION new_client(lastn character varying, firstn character varying)
  RETURNS integer AS
$BODY$
DECLARE
   v_id integer;
BEGIN
   -- Inserts the new person record and retrieves the last inserted id
   INSERT INTO person(lastname, firstname)
   VALUES (lastn, firstn)
   RETURNING id INTO v_id;

   -- Inserts the new client and references the inserted person
   INSERT INTO client(id) VALUES (v_id);

   -- Return the new id so we can use it in a select clause or return the new id into the user application
    RETURN v_id;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE;

Now we can insert the new data using:

SELECT new_client('Smith', 'John');

or

SELECT * FROM new_client('Smith', 'John');

And we get the newly created id.

new_client
integer
----------
         1
明月松间行 2024-09-11 00:33:57

其他答案没有显示如何使用 RETURNING 返回的值。下面是一个将返回值插入到另一个表中的示例。

WITH inserted_id AS (
  INSERT INTO tbl1 (col1)
  VALUES ('foo') RETURNING id
)

INSERT INTO tbl2 (other_id) 
VALUES ((select id from inserted_id));

The other answers don't show how one might use the value(s) returned by RETURNING. Here's an example where the returned value is inserted into another table.

WITH inserted_id AS (
  INSERT INTO tbl1 (col1)
  VALUES ('foo') RETURNING id
)

INSERT INTO tbl2 (other_id) 
VALUES ((select id from inserted_id));
岁月流歌 2024-09-11 00:33:57

您可以在 INSERT 语句中使用 RETURNING 子句,如下所示

wgzhao=# create table foo(id int,name text);
CREATE TABLE
wgzhao=# insert into foo values(1,'wgzhao') returning id;
 id 
----
  1
(1 row)

INSERT 0 1
wgzhao=# insert into foo values(3,'wgzhao') returning id;
 id 
----
  3
(1 row)

INSERT 0 1

wgzhao=# create table bar(id serial,name text);
CREATE TABLE
wgzhao=# insert into bar(name) values('wgzhao') returning id;
 id 
----
  1
(1 row)

INSERT 0 1
wgzhao=# insert into bar(name) values('wgzhao') returning id;
 id 
----
  2
(1 row)

INSERT 0 

you can use RETURNING clause in INSERT statement,just like the following

wgzhao=# create table foo(id int,name text);
CREATE TABLE
wgzhao=# insert into foo values(1,'wgzhao') returning id;
 id 
----
  1
(1 row)

INSERT 0 1
wgzhao=# insert into foo values(3,'wgzhao') returning id;
 id 
----
  3
(1 row)

INSERT 0 1

wgzhao=# create table bar(id serial,name text);
CREATE TABLE
wgzhao=# insert into bar(name) values('wgzhao') returning id;
 id 
----
  1
(1 row)

INSERT 0 1
wgzhao=# insert into bar(name) values('wgzhao') returning id;
 id 
----
  2
(1 row)

INSERT 0 
落花浅忆 2024-09-11 00:33:57

请参阅下面的示例

CREATE TABLE users (
    -- make the "id" column a primary key; this also creates
    -- a UNIQUE constraint and a b+-tree index on the column
    id    SERIAL PRIMARY KEY,
    name  TEXT,
    age   INT4
);

INSERT INTO users (name, age) VALUES ('Mozart', 20);

然后,为了获取最后插入的 id,请将其用于表“user”seq 列名称“id”

SELECT currval(pg_get_serial_sequence('users', 'id'));

See the below example

CREATE TABLE users (
    -- make the "id" column a primary key; this also creates
    -- a UNIQUE constraint and a b+-tree index on the column
    id    SERIAL PRIMARY KEY,
    name  TEXT,
    age   INT4
);

INSERT INTO users (name, age) VALUES ('Mozart', 20);

Then for getting last inserted id use this for table "user" seq column name "id"

SELECT currval(pg_get_serial_sequence('users', 'id'));
明月松间行 2024-09-11 00:33:57
SELECT CURRVAL(pg_get_serial_sequence('my_tbl_name','id_col_name'))

当然,您需要提供表名和列名。

这将用于当前会话/连接
http://www.postgresql.org/docs/8.3/static/functions -sequence.html

SELECT CURRVAL(pg_get_serial_sequence('my_tbl_name','id_col_name'))

You need to supply the table name and column name of course.

This will be for the current session / connection
http://www.postgresql.org/docs/8.3/static/functions-sequence.html

孤蝉 2024-09-11 00:33:57

您可以在插入查询后使用 RETURNING id。

INSERT INTO distributors (id, name) VALUES (DEFAULT, 'ALI') RETURNING id;

结果:

 id 
----
  1

在上面的例子中,id是自增字段。

You can use RETURNING id after insert query.

INSERT INTO distributors (id, name) VALUES (DEFAULT, 'ALI') RETURNING id;

and result:

 id 
----
  1

In the above example id is auto-increment filed.

谜兔 2024-09-11 00:33:57

对于需要获取所有数据记录的人,可以添加

returning *

到查询的末尾以获取包括id的所有对象。

For the ones who need to get the all data record, you can add

returning *

to the end of your query to get the all object including the id.

不…忘初心 2024-09-11 00:33:57

更好的方法是使用 Insert 并返回。虽然已经有相同的答案,但我只是想补充一点,如果你想将其保存到变量中,那么你可以这样做

insert into my_table(name) returning id into _my_id;

The better way is to use Insert with returning. Though there are already same answers, I just want to add, if you want to save this to a variable then you can do this

insert into my_table(name) returning id into _my_id;
慢慢从新开始 2024-09-11 00:33:57

Postgres 有一个内置的机制,它在同一个查询中返回 id 或您希望查询返回的任何内容。
这是一个例子。假设您创建了一个包含 2 列(column1 和 column2)的表,并且您希望在每次插入后返回 column1。

# create table users_table(id serial not null primary key, name character varying);
CREATE TABLE
#insert into users_table(name) VALUES ('Jon Snow') RETURNING id;
 id 
----
  1
(1 row)

# insert into users_table(name) VALUES ('Arya Stark') RETURNING id;
 id 
----
  2
(1 row)

Postgres has an inbuilt mechanism for the same, which in the same query returns the id or whatever you want the query to return.
here is an example. Consider you have a table created which has 2 columns column1 and column2 and you want column1 to be returned after every insert.

# create table users_table(id serial not null primary key, name character varying);
CREATE TABLE
#insert into users_table(name) VALUES ('Jon Snow') RETURNING id;
 id 
----
  1
(1 row)

# insert into users_table(name) VALUES ('Arya Stark') RETURNING id;
 id 
----
  2
(1 row)
清引 2024-09-11 00:33:57

试试这个:

select nextval('my_seq_name');  // Returns next value

如果返回 1(或者序列的 start_value 是什么),则将序列重置回原始值,传递 false 标志:

select setval('my_seq_name', 1, false);

否则,

select setval('my_seq_name', nextValue - 1, true);

这会将序列值恢复到原始状态,并且“setval”将返回您要查找的序列值。

Try this:

select nextval('my_seq_name');  // Returns next value

If this return 1 (or whatever is the start_value for your sequence), then reset the sequence back to the original value, passing the false flag:

select setval('my_seq_name', 1, false);

Otherwise,

select setval('my_seq_name', nextValue - 1, true);

This will restore the sequence value to the original state and "setval" will return with the sequence value you are looking for.

陈独秀 2024-09-11 00:33:57

我在使用 Java 和 Postgres 时遇到了这个问题。
我通过更新新的 Connector-J 版本修复了该问题。

postgresql-9.2-1002.jdbc4.jar

https://jdbc.postgresql.org/download.html:
版本 42.2.12

https://jdbc.postgresql.org/download/postgresql- 42.2.12.jar

I had this issue with Java and Postgres.
I fixed it by updating a new Connector-J version.

postgresql-9.2-1002.jdbc4.jar

https://jdbc.postgresql.org/download.html:
Version 42.2.12

https://jdbc.postgresql.org/download/postgresql-42.2.12.jar

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文