最後に挿入されたIDのPostgreSQL関数


321

PostgreSQLで、テーブルに挿入された最後のIDを取得するにはどうすればよいですか?

MS SQLにはSCOPE_IDENTITY()があります。

このようなものを使用するように私に助言しないでください:

select max(id) from table

なぜあなたはmax関数が嫌いですか?とても簡単だと思います。セキュリティなどの問題はありますか?
jeongmin.cha 2016年

9
@ jeongmin.cha他の操作とより多くの挿入(同時操作)の間に問題がある場合、ロックを明示的に取得して解放しない限り、最大IDが変更されたことを意味します
rohanagarwal

使用目的が、最後に挿入されたIDを後続の挿入の値の一部として使用することである場合は、この質問を参照してください。
フラックス

回答:


606

tl;dr:gotoオプション3:RETURNINGを指定したINSERT)

postgresqlにはテーブルの「id」の概念はなく、シーケンスのみであることを思い出してください(これは通常、SERIAL疑似タイプのサロゲート主キーのデフォルト値として必ずしも使用されるわけではありません)。

新しく挿入された行のIDを取得したい場合は、いくつかの方法があります。


オプション1: CURRVAL(<sequence name>);

例えば:

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

シーケンスの名前はわかっている必要があります。これは実際には任意です。この例では、疑似タイプで作成されpersonsid列がテーブルにあると想定していますSERIAL。これに依存することを避け、より清潔に感じるために、代わりに使用することができますpg_get_serial_sequence

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

警告:(実行された)のcurrval()後でのみ、同じセッションで機能します。INSERTnextval()


オプション2: LASTVAL();

これは前と同様ですが、シーケンス名を指定する必要はありません。最新の変更されたシーケンスを探します(常にセッション内で、上記と同じ警告)。


両方CURRVALLASTVAL完全に同時安全です。PGのシーケンスの動作は、異なるセッションが干渉しないように設計されているため、競合状態のリスクはありません(別のセッションがINSERTとSELECTの間に別の行を挿入した場合でも、正しい値が得られます)。

しかし、彼らには微妙な潜在的な問題があります。データベースにTRIGGER(またはRULE)があり、personsテーブルへの挿入時に他のテーブルに追加の挿入LASTVALが行われる場合、おそらく誤った値が返されます。CURRVAL同じpersonsテーブルに対して追加の挿入が行われる場合、問題はでさえ発生する可能性があります(これはあまり一般的ではありませんが、リスクは依然として存在します)。


オプション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をグローバルに(必ずしもセッションによってではなく)取得する場合は、これらすべてのメソッドは役に立ちません。このためには、手段を講じる必要がありますSELECT max(id) FROM table(もちろん、これは他のトランザクションからのコミットされていない挿入を読み取りません)。

逆に、あなたがすべき決して使用しないでSELECT max(id) FROM table自分で生成されたIDを取得するために、代わりに上記の3つのオプションのいずれかをINSERT(離れてパフォーマンスから)これは同時安全ではないので、文を:あなたの間INSERTとあなたのSELECT別のセッション別のレコードを挿入している場合があります。


25
LASTVAL()は、それ自体の行を別のテーブルに挿入するトリガー/ルールを追加する場合、非常に悪質な場合があります。
Kouber Saparev 2012年

3
SELECT max(id)残念ながら、行の削除を開始してもすぐにはジョブは実行されません。
Simon A. Eugster

2
@leonbloy何かを見落とした場合を除き、IDのある行が1,2,3,4,5あり、行4と5を削除した場合、最後に挿入されたIDは5のままですが、max()3を返します
Simon A. Eugster

9
RETURNING id;これを別のテーブルに挿入する方法のサンプルは 大歓迎です!
Olivier Pons、2015

8
コマンドラインツールにRETURNING id供給されるSQLスクリプト内でどのように使用できpsqlますか?
amoe

82

INSERTステートメントのRETURNING句を参照してください。基本的に、INSERTはクエリとしても機能し、挿入された値を返します。


4
バージョン8.2以降で動作し、最良かつ最速のソリューションです。
フランク・ヘイケンズ

9
上記の返されたIDの使用方法の簡単な説明?
Andrew

@Andrew質問が理解できるかわかりません。クエリから結果を取得する方法がわかりませんか?これは言語/ライブラリに依存しており、選択挿入と戻り挿入のどちらを実行しているかに関係なく、同じように機能します。私が思い付くことができる他の唯一の解釈は、呼び出しからIDを正常に取得し、それが何のためにあるかわからないということです...その場合、なぜそれを取得したのですか?
kwatford

8
@Andrew、あなたがいる場合にpsqlコマンドラインから実行この:insert into names (firstname, lastname) values ('john', 'smith') returning id;、あなたが走ったかのように、それは単に出力のID select id from names where id=$lastidを直接。あなたがしたい場合はAA変数に戻り値を保存し、その後、insert into names (firstname, lastname) values ('john', 'smith') returning id into other_variable;帰国を含む文がある場合は関数内の最後の文は、その後、idがあることreturning「edは、全体としての関数によって返されます。
Alexander Bird

32

次のように、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 

28

レオンブロイの答えは完全です。オプション3が正確に適合しないPL / pgSQL関数内から最後に挿入された値を取得する必要がある特別な場合のみを追加します。

たとえば、次のテーブルがあるとします。

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関数を考案したいとしましょう。そのためには、レオンブロイのオプション3のわずかなバリエーションを使用する必要があります。

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

2つの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

9

すべてのデータレコードを取得する必要がある人のために、あなたは追加することができます

returning *

クエリの最後に移動して、IDを含むすべてのオブジェクトを取得します。


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


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」は探しているシーケンス値を返します。


1

Postgresには組み込みのメカニズムがあり、同じクエリで、IDまたはクエリが返すものを返します。ここに例があります。column1とcolumn2の2つの列を持つテーブルを作成し、挿入ごとに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)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.