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 절이 추가 쿼리의 오버 헤드없이 식별자를 제공하고 WRONG VALUE (커 런스가 수행 될 수있는)를 리턴 할 가능성이 없기 때문에 currval ()의 사용은 일반적으로 권장되지 않습니다. 일부 사용 사례.)
— chander

1
@ chander : 그 주장에 대한 언급이 있습니까? 사용을 currval() 권장하지 않습니다.
— a_horse_with_no_name

아마도 currval의 사용이 권장되지 않는지에 대한 의견의 문제 일 수도 있지만, 어떤 경우에는 사용자가 예상치 않은 값을 제공 할 수 있음을 알고 있어야합니다 (따라서 지원되는 경우 RETURNING을 더 나은 선택으로 설정). 시퀀스 a_seq를 사용하는 테이블 A와 a_seq도 사용하는 테이블 B (PK 열에 대해 nextval ( 'a_seq'호출)를가집니다.) 테이블 B에 INSERT를 테이블 A에 삽입하는 트리거 (a_trg)도 있다고 가정하십시오. 이 경우 currval () 함수 (테이블 A에 삽입 후)는 테이블 A가 아니라 테이블 B에 삽입에 대해 생성 된 숫자를 반환합니다.
— chander

답변:


51

serialPostgreSQL 로 열을 생성하면 해당 시퀀스가 ​​자동으로 생성됩니다.

시퀀스의 이름은 자동 생성되며 항상 tablename_columnname_seq이며,이 경우 시퀀스는 names 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()


currval()다중 사용자 설정에서 사용 하는 것이 좋지 않은 것 같습니다 . 예를 들어 웹 서버에서.
— Jonas

9
아니 당신은 착각이야 currval()현재 연결에 "로컬"입니다. 따라서 다중 사용자 환경에서 사용하는 데 아무런 문제가 없습니다. 이것이 시퀀스의 전체 목적입니다.
— a_horse_with_no_name

1
삽입물이 동일한 테이블에서 더 많은 삽입물을 트리거하는 (아주 아주 드문) 경우에 문제가 발생할 수 있습니다.
— leonbloy

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을 얻는 방법인지 궁금합니다. 그럼 어떤 제안?
— Slak

클라이언트 테스트를 쉽게하기 위해 점진적으로 생성되는 PK 값을 미리 결정하기를 원했던 픽스쳐 데이터를 생성하기 위해 기본 키를 하드 코딩 할 때이 작업을 수행해야했습니다. 일반적으로 기본값이 적용되는 열에서 삽입을 수행 할 때 삽입을 지원 nextval()하려면 하드 코드 된 ID로 삽입 한 조명기 레코드 수와 일치하도록 시퀀스를 수동으로 설정해야합니다. 또한 currval () / lastval ()이 pre-nextval에서 사용할 수없는 문제를 해결하는 방법 SELECT은 시퀀스에서 직접 수행하는 것입니다.
— Peter M. Elias

@ ypercubeᵀᴹ 반대로, 선택된 "올바른"답변을 사용해야 할 이유가 없다고 생각합니다. 이 답변에는 테이블에 레코드를 삽입 할 필요가 없습니다. 이것은 또한 질문에 대한 답변입니다. 다시 한 번, 선택한 답변에 대해이 답변을 사용하지 않을 이유가 없다고 생각할 수 있습니다.
— Henley Chiu

1
이 답변에는 테이블 수정이 전혀 필요하지 않습니다. 확인한 사람은 않습니다. 이상적으로, 당신은 전혀 변경하지 않고 마지막 ID를 알고 싶어합니다. 이것이 프로덕션 DB 인 경우 어떻게합니까? 타악기없이 임의의 행을 삽입 할 수는 없습니다. 따라서이 답변은 안전하고 정확합니다.
— Henley Chiu

14

당신은 호출 할 필요가 nextval이 세션 전에이 시퀀스currval :

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

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

insert동일한 세션에서 수행 하지 않으면 시퀀스에서 '마지막으로 삽입 된 ID'를 찾을 수 없습니다 (트랜잭션은 롤백 할 수 있지만 시퀀스는 그렇지 않습니다)

a_horse 's answer에서 지적했듯이 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'여기서 blah = 'blech'반환 *

  • 업데이트 삭제 결과를 사용하십시오.

    WITH A as (ID를 리턴 할 때 표 A에서 delete *) update B set deleted = true 여기서 id는 (A에서 id 선택);


물론 OP는 명시 적으로RETURNING 조항 을 사용하고 싶지 않다고 말 했지만 다른 사람에게 명확하게 사용하면 이점이 없습니다.
— RDFozz

나는 실제로 여러 가지 다른 방법의 함정을 지적하려고 노력했습니다.
— chander

1

currval을 성공적으로 사용하지 못했기 때문에 SQLALchemy를 사용해도 쿼리를 실행해야했습니다.

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

이것은 파이썬 플라스크 + 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()스레드와 좀 더 관련성을 갖도록 함수를 호출 할 때 권한 실패와 관련된 몇 가지 사항을 포함하도록 답변을 확장 하는 것이 좋습니다.
— Peter Vandivier

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.