web-dev-qa-db-ja.com

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

テーブルがあります:

_CREATE TABLE names (id serial, name varchar(20))
_

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

私は試しました:

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

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

70
Jonas

serialとして列を作成すると、PostgreSQLはそのシーケンスを自動的に作成します。

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

テーブルに挿入した後、そのシーケンス名で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()を使用します

56

これは Stack Overflow から直接です

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

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

53
Slak

あなた 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)

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

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

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

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

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

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

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

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

    テーブルAの更新X = 'y' blah = 'blech'が返す*

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

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

3
chander

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

GRANT USAGE ON SCHEMA schema_name to user;

そして

GRANT ALL PRIVILEGES ON schema_name.sequence_name TO user;
1
AMML

nextval()を呼び出さずに、シーケンスを変更せずにシーケンスの値を取得したい場合は、PL/pgSQL関数を一緒にスローして処理します: すべての値を検索データベースシーケンス

1
Christophe

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

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

これはpython flask + postgresqlプロジェクトです。

1
Jalal

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であることを意味します。

0
scifisamurai