2010-05-31 110 views
216

如何获取插入到表中的最后一个ID?在MS SQL中有SCOPE_IDENTITY()。postgreSQL函数为最后插入的ID

请,不建议使用这样的事情:

select max(id) from table 
+0

你为什么讨厌最大功能?我认为这很简单。有没有安全问题? – 2016-11-25 07:41:06

+3

@ jeongmin.cha如果存在其他操作和更多插入操作(并发操作)之间存在问题,则表示max id已更改,除非并且直到明确地取锁并且不释放它 – rohanagarwal 2017-07-14 14:00:51

回答

409

tl;dr:GOTO选项3:与返回的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可能会给我们带来错误的值。如果额外的插入是在同一个persons表中完成的,这个问题甚至可能发生在CURRVAL(这通常不太常见,但风险仍然存在)。


方案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(当然,这不会读取来自其他事务的未提交插入)。

+15

LASTVAL()可能非常邪恶,以防将触发器/规则插入到其他表中。 – 2012-10-25 15:17:41

+2

'SELECT max(id)'不幸的是,只要开始删除行,就不会完成这项工作。 – 2012-11-02 16:44:30

+0

@ SimonA.Eugster为什么不呢? – leonbloy 2012-11-02 17:11:22

7

参见下面的例子

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用这个表“用户”以次列名“ID”

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

请参阅INSERT声明的RETURNING子句。基本上,INSERT加倍作为查询,并返回插入的值。

+4

从版本8.2并且是最好和最快的解决方案。 – 2010-05-31 15:04:11

+2

也许快速解释如何使用所述返回的ID? – Andrew 2016-04-12 20:02:39

+0

@Andrew我不确定我是否理解这个问题。你不知道如何从查询中检索结果吗?这是依赖于语言/库的,不管你是在做一个select还是一个返回插入,它都应该是一样的。我唯一可以想到的其他解释是,您已成功从通话中检索到身份证,并且不知道它的用途......在这种情况下,您为什么要取回身份证? – kwatford 2016-04-20 22:39:10

22

您可以使用INSERT语句,返回条款,就像下面

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 
1

试试这个:

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

如果返回1(或任何对你的序列START_VALUE),然后将序列重置回原始值,通过假标志:

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

否则,

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

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

-2

如果你只需要检索没有插入任何你可以用下面的例子

SELECT id FROM users ORDER BY id DESC LIMIT 1; 

希望能帮最后插入的ID。

+0

Op不要求这种类型的解决方案 – ModChowdhury 2016-05-21 15:13:24

11

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的OPTION 3的微小的变化:

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

注意,有两成的条款。

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; 

现在我们可以使用插入新的数据:因此,PL/pgSQL函数想被定义

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

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

而我们得到新创建的ID 。

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

对于谁需要获得所有数据记录的,你可以添加

returning * 

到您的查询的末尾以获得包括ID的所有对象。