PostgreSQL函数用于最后插入的ID

Ant*_*ton 297 postgresql insert lastinsertid

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

在MS SQL中有SCOPE_IDENTITY().

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

select max(id) from table
Run Code Online (Sandbox Code Playgroud)

leo*_*loy 557

(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');
Run Code Online (Sandbox Code Playgroud)

必须知道序列的名称,它实际上是任意的; 在这个例子中,我们假设该表persons有一个idSERIAL伪类型创建的列.为了避免依赖于此并感觉更干净,您可以使用pg_get_serial_sequence:

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval(pg_get_serial_sequence('persons','id'));
Run Code Online (Sandbox Code Playgroud)

警告: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;
Run Code Online (Sandbox Code Playgroud)

这是获得id的最干净,最有效和最安全的方式.它没有以前的任何风险.

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


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

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

  • LASTVAL()可能非常邪恶,以防您添加将行自身插入另一个表的触发器/规则. (22认同)
  • 如何使用`RETURNING id;`将其插入另一个表中的示例将受到欢迎! (7认同)
  • 如何在提供给`psql`命令行工具的SQL脚本中使用`RETURNING id`? (7认同)
  • 遗憾的是,SELECT max(id)`一旦开始删除行就不能完成工作. (3认同)
  • @leonbloy除非我错过了什么,如果你有ID为"1,2,3,4,5"的行并删除第4行和第5行,最后插入的ID仍然是5,但是`max()`返回3. (2认同)
  • LASTVAL() 不是邪恶的,它是邪恶的触发器 (2认同)
  • 我知道你有一个 tl;dr 但我不知道你为什么按照你的方式订购你的选项,你应该将选项 3 移到选项 1,因为它是最实用的解决方案,而且大多数用户无论如何都会采用。 (2认同)
  • 选项 3 是最简单方便的选项。谢谢 (2认同)

kwa*_*ord 73

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

  • 也许快速解释如何使用所述返回的id? (7认同)
  • @Andrew,如果你**从psql命令行运行**:`insert into names(firstname,lastname)values('john','smith')返回id;`,然后它只是输出id就像你跑了一样`直接从id = $ lastid`的名字中选择id.如果你想**将返回保存到变量**中,那么`插入名称(名字,姓氏)值('john','smith')将id返回到other_variable;`如果包含返回的语句是**函数**中的最后一个语句,然后整个函数返回id为"returns"的id. (6认同)
  • 从版本8.2开始工作,是最好和最快的解决方案. (4认同)

sna*_*erb 38

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

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

INSERT INTO tbl2 (other_id) 
VALUES ((select id from inserted_id));
Run Code Online (Sandbox Code Playgroud)

  • 我不认为 Postgres 对这种情况下的评估顺序提供任何保证(这是我对[本节]的阅读(https://www.postgresql.org/docs/13/queries-with.html#QUERIES-WITH-修改)来自文档。 (2认同)

wgz*_*hao 31

您可以在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 
Run Code Online (Sandbox Code Playgroud)


Kra*_*uss 24

Leonbloy的答案非常完整.我只会添加一个特殊情况,其中需要从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
);
Run Code Online (Sandbox Code Playgroud)

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

INSERT INTO person(lastname, firstname) 
VALUES (lastn, firstn) 
RETURNING id INTO [new_variable];
Run Code Online (Sandbox Code Playgroud)

请注意,有两个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;
Run Code Online (Sandbox Code Playgroud)

现在我们可以使用以下方法插入新数据:

SELECT new_client('Smith', 'John');
Run Code Online (Sandbox Code Playgroud)

要么

SELECT * FROM new_client('Smith', 'John');
Run Code Online (Sandbox Code Playgroud)

我们得到了新创建的id.

new_client
integer
----------
         1
Run Code Online (Sandbox Code Playgroud)

  • 这个答案非常有帮助。您在 Postrgres 官方文档中找不到任何具体的“RETURNING id INTO [new_variable]”示例。 (2认同)

Moh*_*lah 15

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

INSERT INTO distributors (id, name) VALUES (DEFAULT, 'ALI') RETURNING id;
Run Code Online (Sandbox Code Playgroud)

和结果:

 id 
----
  1

Run Code Online (Sandbox Code Playgroud)

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


Sun*_*arg 10

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

insert into my_table(name) returning id into _my_id;
Run Code Online (Sandbox Code Playgroud)


小智 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);
Run Code Online (Sandbox Code Playgroud)

然后为了获得最后插入的id,请使用此表"user"seq列名"id"

SELECT currval(pg_get_serial_sequence('users', 'id'));
Run Code Online (Sandbox Code Playgroud)


jis*_*shi 7

SELECT CURRVAL(pg_get_serial_sequence('my_tbl_name','id_col_name'))
Run Code Online (Sandbox Code Playgroud)

您需要提供表名和列名.

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


emr*_*tem 7

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

returning *
Run Code Online (Sandbox Code Playgroud)

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