PostgreSQL函数用于最后插入的ID

时间:2010-05-31 14:55:24

标签: postgresql insert lastinsertid

在PostgreSQL中,如何将最后一个id插入表中?

在MS SQL中有SCOPE_IDENTITY()。

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

select max(id) from table

13 个答案:

答案 0 :(得分:512)

tl;dr:转到选项3:使用RETURNING INSERT)

回想一下,在postgresql中,表没有“id”概念,只有序列(通常但不一定用作代理主键的默认值,SERIAL伪-类型)。

如果您有兴趣获取新插入行的ID,可以采用以下几种方法:


选项1:CURRVAL(<sequence name>);

例如:

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

必须知道序列的名称,它实际上是任意的;在此示例中,我们假设表persons具有使用id伪类型创建的SERIAL列。为了避免依赖于此并感觉更干净,您可以使用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(或RULE),那么在插入persons表时,会在其他表中进行一些额外的插入......那么LASTVAL可能会给我们错误的值。问题甚至可能发生在CURRVAL,如果额外的插入是在同一个persons表中完成的(这通常不太常见,但风险仍然存在)。


选项3:INSERTRETURNING

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

这是获取ID的最干净,最有效和最安全的方式。它没有以前的任何风险。

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

以来可用

结论:如果可以,请选择选项3.在其他地方,请选择1。

注意:如果您打算获取最后一次全局插入的ID (不一定在您的会话中),则所有这些方法都无用。为此,您必须使用select max(id) from table(当然,这不会读取其他事务中未提交的插入内容)。

答案 1 :(得分:70)

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

答案 2 :(得分:29)

您可以在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 

答案 3 :(得分:20)

Leonbloy's answer非常完整。我只会添加一个特殊情况,其中需要从PL / pgSQL函数中获取最后一个插入值,其中OPTION 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

答案 4 :(得分:8)

见下面的例子

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'));

答案 5 :(得分:7)

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

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

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

答案 6 :(得分:4)

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

returning *

到查询结尾以获取包含id的所有对象。

答案 7 :(得分:1)

试试这个:

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”将返回您要查找的序列值。

答案 8 :(得分:1)

其他答案没有说明如何使用 RETURNING 返回的值。这是一个将返回值插入另一个表的示例。

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

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

答案 9 :(得分:0)

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)

答案 10 :(得分:0)

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

答案 11 :(得分:0)

插入查询后,您可以使用RETURNING ID。

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

和结果:

 id 
----
  1

在上面的示例中,id是自动递增的。

答案 12 :(得分:0)

基于上述@ooZman的答案,当您需要使用下一个“序列”值(类似于auto_increment)插入而不在表中累加任何东西时,这似乎适用于PostgreSQL v12。 s)。 (注意:尽管如此,我尚未在更复杂的数据库集群配置中对其进行测试...)

伪代码

$ insert_next_id = $ return_result-> query(“ select (setval('"your_id_seq"', (select nextval('"your_id_seq"')) - 1, true)) +1;”)