PostgreSQLで、最後のIDをテーブルに挿入するにはどうすればよいですか?
MS SQLには、SCOPE_IDENTITY()があります。
次のようなものを使用するように私にアドバイスしないでください:
select max(id) from table
PostgreSQLで、最後のIDをテーブルに挿入するにはどうすればよいですか?
MS SQLには、SCOPE_IDENTITY()があります。
次のようなものを使用するように私にアドバイスしないでください:
select max(id) from table
(tl;dr
:gotoオプション3:INSERT with RETURNING)
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();
これは前の例と似ていますが、シーケンス名を指定する必要がない点が異なります。最新の変更されたシーケンスを検索します(常にセッション内で、上記と同じ注意事項)。
CURRVAL
とは両方ともLASTVAL
完全に同時安全です。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
(もちろん、これは他のトランザクションからコミットされていない挿入を読み取ることはありません)。
逆に、ステートメントによって生成されたIDを取得するために、上記の3つのオプションのいずれかを使用しないでください。これは、(パフォーマンスは別として)同時に安全ではないためです。自分と別のセッションの間で別のレコードが挿入された可能性があります。SELECT max(id) FROM table
INSERT
INSERT
SELECT
INSERTステートメントのRETURNING句を参照してください。基本的に、INSERTはクエリとしても機能し、挿入された値を返します。
レオンブロイの答えは完全です。オプション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関数を考案したいとします。そのためには、leonbloyのオプション3のわずかなバリエーションを使用する必要があります。
INSERT INTO person(lastname, firstname)
VALUES (lastn, firstn)
RETURNING id INTO [new_variable];
INTO句が2つあることに注意してください。したがって、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
次のように、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
SELECT CURRVAL(pg_get_serial_sequence('my_tbl_name','id_col_name'))
もちろん、テーブル名と列名を指定する必要があります。
これは現在のセッション/接続用になり ますhttp://www.postgresql.org/docs/8.3/static/functions-sequence.html
以下の例を参照してください
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'));
すべてのデータレコードを取得する必要がある場合は、追加できます
returning *
クエリの最後に移動して、IDを含むすべてのオブジェクトを取得します。
他の回答は、によって返される値をどのように使用するかを示していませんRETURNING
。戻り値が別のテーブルに挿入される例を次に示します。
WITH inserted_id AS (
INSERT INTO tbl1 (col1)
VALUES ('foo') RETURNING id
)
INSERT INTO tbl2 (other_id)
VALUES ((select id from inserted_id));
クエリの挿入後にRETURNINGidを使用できます。
INSERT INTO distributors (id, name) VALUES (DEFAULT, 'ALI') RETURNING id;
結果:
id
----
1
上記の例では、idは自動インクリメントファイルです。
Postgresには同じメカニズムが組み込まれており、同じクエリでIDまたはクエリに返したいものを返します。ここに例があります。2つの列column1とcolumn2を持つテーブルが作成されており、挿入のたびに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)
これを試して:
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」が探しているシーケンス値で返されます。
より良い方法は、returningでInsertを使用することです。すでに同じ答えがありますが、追加したいのですが、これを変数に保存したい場合は、これを行うことができます
insert into my_table(name) returning id into _my_id;
この問題はJavaとPostgresで発生しました。新しいConnector-Jバージョンを更新して修正しました。
postgresql-9.2-1002.jdbc4.jar
https://jdbc.postgresql.org/download.html:バージョン42.2.12
上記の@ooZmanの回答に基づくと、これはPostgreSQL v12で機能するようです。これは、テーブルカウンターに何も追加せずに、「シーケンス」の次の値(auto_incrementに類似)でINSERTする必要がある場合です。(注:より複雑なDBクラスター構成ではテストしていません...)
$ insert_next_id = $ return_result-> query( " select (setval('"your_id_seq"', (select nextval('"your_id_seq"')) - 1, true)) +1;
")