web-dev-qa-db-ja.com

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

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

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

このようなものを使うように私に忠告しないでください。

select max(id) from table
277
Anton

tl;dr:オプション3に進む:RETURNINGを使ってINSERT)

Postgresqlにはテーブルのための "id"の概念はなく、単にsequence(代理の主キーのためのデフォルト値として使われますが、必ずしもそうとは限りません、 _ 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()INSERT(これはnextval()を実行した)、同じセッション内の後でのみ機能します。


オプション2: LASTVAL();

これは前のものと似ていますが、シーケンス名を指定する必要がないということだけです。それは最新の修正されたシーケンスを探す(常にあなたのセッションの中、上記と同じ警告)。


CURRVALLASTVALはどちらも完全に同時安全です。 PGでのsequenceの振る舞いは、異なるセッションが干渉しないように設計されているので、競合状態の危険性はありません(別のセッションが私の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に頼らなければなりません(もちろん、これは他のトランザクションからのコミットされていない挿入を読み込みません)。

514
leonbloy

INSERT 文のRETURNING句を参照してください。基本的に、INSERTはクエリを兼ねており、挿入された値を返します。

71
kwatford

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

Leonbloy's answer かなり完成しました。最後の挿入値をPL/pgSQL関数内から取得する必要があるという特別な場合だけを追加します。ここでは、オプション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];

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
20
Krauss

下記の例をご覧ください

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'));
8
RINSON KE
SELECT CURRVAL(pg_get_serial_sequence('my_tbl_name','id_col_name'))

もちろんテーブル名とカラム名を指定する必要があります。

これは現在のセッション/接続用です http://www.postgresql.org/docs/8.3/static/functions-sequence.html

7
jishi

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

returning *

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

4
emreoktem

これを試して:

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
Oozman

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)
0
Sandip Debnath