PostgreSQLでcurrval()を使用して最後に挿入されたIDを取得するにはどうすればよいですか?


64

私はテーブルを持っています:

CREATE TABLE names (id serial, name varchar(20))

挿入時に使用せずに、そのテーブルから「最後に挿入されたID」が必要RETURNING idです。関数があるようですが、CURRVAL()使用方法がわかりません。

私は試しました:

SELECT CURRVAL() AS id FROM names_id_seq
SELECT CURRVAL('names_id_seq')
SELECT CURRVAL('names_id_seq'::regclass)

しかし、どれも機能しません。currval()最後に挿入されたIDを取得するにはどうすればよいですか?


2
この問題/解決策の読者は、RETURNING句が追加クエリのオーバーヘッドなしで識別子を提供し、間違った値を返す可能性がないため(currvalがいくつかのユースケース。)
チャンダー

1
@chander:その主張について何か参考になりますか?の使用currval() は絶対に推奨されません。
a_horse_with_no_name

おそらく、currvalの使用が推奨されていないかどうかについての意見の問題ですが、場合によっては、ユーザーが期待するものとは異なる値を提供する可能性があることに注意する必要があります(したがって、サポートされている場合はRETURNINGがより良い選択になります)。シーケンスa_seqを使用するテーブルAと、a_seq(PK列にnextval( 'a_seq')を呼び出す)を使用するテーブルBがあります。テーブルAにINSERTを挿入してテーブルBに挿入するトリガー(a_trg)もあるとします。その場合(表Aに挿入した後)CURRVAL()関数は、テーブルBではなく、テーブルAの挿入のために生成数戻ります
のChander

回答:


51

serialPostgreSQL が列を作成する場合、PostgreSQLはそのためのシーケンスを自動的に作成します。

シーケンスの名前は自動生成され、常にtablename_columnname_seq names_id_seqです。この場合、シーケンスはnamesになります。

テーブルに挿入した後currval()、そのシーケンス名で呼び出すことができます:

postgres=> CREATE TABLE names in schema_name (id serial, name varchar(20));
CREATE TABLE
postgres=> insert into names (name) values ('Arthur Dent');
INSERT 0 1
postgres=> select currval('names_id_seq');
 currval
---------
       1
(1 row)
postgres=>

シーケンス名をハードコーディングする代わりに、代わりに使用することもできpg_get_serial_sequence()ます:

select currval(pg_get_serial_sequence('names', 'id'));

そうすれば、Postgresが使用する命名戦略に依存する必要がなくなります。

または、シーケンス名をまったく使用したくない場合は、使用します lastval()


currval()マルチユーザー設定で使用するのは良くないと思います。たとえば、ウェブサーバー上。
ジョナス

9
いいえ、あなたは間違っています。currval()現在の接続に対して「ローカル」です。したがって、マルチユーザー環境で使用しても問題はありません。それがシーケンスの全体的な目的です。
a_horse_with_no_name

1
これは、同じテーブル内で挿入がより多くの挿入をトリガーする(非常にまれな)ケースで破損する可能性がありますか?
レオンブロ14年

1
@a_horse_with_no_name:複数の挿入(それは簡単に見つけられます)ではなく、テーブルで定義された(おそらく不明な)トリガーについて考えていました。たとえば、上記でこれを試してください:gist.github.com/anonymous/9784814
leonbloy

1
常にテーブル名と列名ではありません。その名前のシーケンスが既に存在する場合、最後に数字を連結またはインクリメントすることで新しいシーケンスを生成します。制限(62文字)を超える場合は、名前を短くする必要があります。
PhilHibbs

47

これは、スタックオーバーフローから直接です

@a_horse_with_no_nameと@Jack Douglasが指摘したように、currvalは現在のセッションでのみ機能します。そのため、結果が別のセッションのコミットされていないトランザクションの影響を受ける可能性があるという事実に問題がなく、セッション間で機能するものが必要な場合は、これを使用できます:

SELECT last_value FROM your_sequence_name;

詳細については、SOへのリンクを使用してください。

ただし、Postgresのドキュメントからは、

nextvalが現在のセッションでまだ呼び出されていない場合、lastvalを呼び出すとエラーになります。

ですから、厳密に言えば、セッション全体でシーケンスにcurrvalまたはlast_valueを適切に使用するには、そのようなことをする必要がありますか?

SELECT setval('serial_id_seq',nextval('serial_id_seq')-1);

もちろん、現在のセッションで挿入またはシリアルフィールドを使用する他の方法がないと仮定します。


3
これが役立つ状況を考えることはできません。
ypercubeᵀᴹ

nextvalが現在のセッションで呼び出されていない場合、これがcurrvalを取得する方法かどうか疑問に思っていました。何か提案はありますか?
スラック

生成されたフィクスチャデータのプライマリキーをハードコーディングしていたときに、クライアントのテストを容易にするために、通常は増分的に生成されるPK値を事前に決定する必要がありました。通常、デフォルト値によって管理される列で挿入を行うときに挿入をサポートするにnextval()は、ハードコーディングされたIDで挿入したフィクスチャレコードの数と一致するようにシーケンスを手動で設定する必要があります。また、pre-nextvalが利用できないcurrval()/ lastval()の問題を解決する方法はSELECT、シーケンスを直接実行することです。
ピーターM.エリアス

@ypercubeᵀᴹそれどころか、選択された「正しい」答えを使用する正当な理由はないと考えることができます。この回答では、テーブルにレコードを挿入する必要はありません。これも質問に答えます。繰り返しますが、選択した回答よりもこの回答を使用しない正当な理由は考えられません。
ヘンリーチウ

1
この答えには、テーブルの変更は一切含まれません。チェックされたものはそうします。理想的には、まったく変更せずに最後のIDを知りたいだけです。これが本番DBの場合はどうなりますか?パーカッションなしでランダムな行を挿入することはできません。したがって、この答えは正しいだけでなく、より安全です。
ヘンリーチウ

14

あなたは、呼び出す必要がありますnextval前に、このセッションでは、このシーケンスのためにcurrval

create sequence serial;
select nextval('serial');
 nextval
---------
       1
(1 row)

select currval('serial');
 currval
---------
       1
(1 row)

そのためinsert、同じセッションで実行されない限り、シーケンスから「最後に挿入されたID」を見つけることができません(トランザクションはロールバックするかもしれませんが、シーケンスはそうなりません)

a_horseの答えで指摘されているようにcreate table、タイプserialの列を使用すると、シーケンスが自動的に作成され、それを使用して列のデフォルト値が生成されるため、insert通常はnextval暗黙的にアクセスされます。

create table my_table(id serial);
NOTICE:  CREATE TABLE will create implicit sequence "my_table_id_seq" for 
         serial column "my_table.id"

\d my_table
                          Table "stack.my_table"
 Column |  Type   |                       Modifiers
--------+---------+-------------------------------------------------------
 id     | integer | not null default nextval('my_table_id_seq'::regclass)

insert into my_table default values;
select currval('my_table_id_seq');
 currval
---------
       1
(1 row)

3

そのため、これらのさまざまな方法にはいくつかの問題があります。

Currvalは、現在のセッションで生成された最後の値のみを取得します-値を生成するものが他にない場合は素晴らしいですが、現在のトランザクションでトリガーを呼び出したりシーケンスを複数回進めたりする場合は素晴らしいです正しい値を返しません。これは99%の人々にとっては問題ではありませんが、それは考慮に入れるべきものです。

挿入操作の後に割り当てられた一意の識別子を取得する最良の方法は、RETURNING句を使用することです。以下の例では、シーケンスに関連付けられた列が「id」と呼ばれることを想定しています。

insert into table A (cola,colb,colc) values ('val1','val2','val3') returning id;

RETURNING句の有用性は、シーケンスを取得するだけではありません。

  • 「最終挿入」に使用された戻り値(たとえば、BEFOREトリガーが挿入されているデータを変更した可能性があります。)
  • 削除されていた値を返します。

    id> 100を返すテーブルAから削除*

  • UPDATE後に変更された行を返します。

    更新テーブルAセットX = 'y' where blah = 'blech' return *

  • 更新の削除の結果を使用します。

    WITH A as(削除*テーブルAからidを返す)update B set deleted = true where id in(Aからidを選択);


もちろん、OP は句を使用したくないと明示的に述べていますが、それを使用するRETURNINGことの利点を他の人に明らかにすることには何の問題もありません。
RDFozz

私は本当にさまざまな他の方法の落とし穴を指摘しようとしていました(注意しない限り、期待される値以外の値を返すことができるという点で)-ベストプラクティスを明確にしました。
チャンダー

1

currvalを使用できなかったため、SQLALchemyを使用しているにもかかわらずクエリを実行する必要がありました。

nextId = db.session.execute("select last_value from <table>_seq").fetchone()[0] + 1

これはPythonフラスコ+ postgresqlプロジェクトでした。



1

次のGRANTように、スキーマで使用する必要があります。

GRANT USAGE ON SCHEMA schema_name to user;

そして

GRANT ALL PRIVILEGES ON schema_name.sequence_name TO user;

1
dba.seへようこそ!あなたの最初の投稿に乾杯!オリジナルの投稿が許可に関して具体的に言及しているようには見えません。currval()関数を呼び出してこのスレッドに少し関連性を持たせるときに、アクセス許可の失敗に関する詳細を含めるように答えを拡張することを検討してください。
ピーターヴァンディヴィエ

0

PostgreSQL 11.2では、シーケンスをテーブルのように扱うことができます:

「names_id_seq」という名前のシーケンスがある場合の例

select * from names_id_seq;
 last_value | log_cnt | is_called
------------+---------+-----------
          4 |      32 | t
(1 row)

これにより、最後に挿入されたID(この場合は4)が得られるはずです。つまり、現在の値(または次にIDに使用される値)は5になります。


-2

PostgreSQLのバージョンが異なると、現在または次のシーケンスIDを取得するための関数が異なる場合があります。

まず、Postgresのバージョンを知る必要があります。select version()を使用します。バージョンを取得します。

PostgreSQL 8.2.15では、を使用して現在のシーケンスIDを取得しselect last_value from schemaName.sequence_nameます。

上記のステートメントが機能しない場合は、使用できます select currval('schemaName.sequence_name');


2
異なるバージョンが異なる方法でそれをしている証拠はありますか?
-dezso
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.