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.