2010-05-31 107 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的所有對象。