마지막으로 삽입 된 ID에 대한 PostgreSQL 함수


321

PostgreSQL에서 마지막 id를 테이블에 삽입하려면 어떻게합니까?

MS SQL에는 SCOPE_IDENTITY ()가 있습니다.

다음과 같은 것을 사용하도록 조언하지 마십시오.

select max(id) from table

왜 최대 기능을 싫어합니까? 매우 간단하다고 생각합니다. 보안과 같은 문제가 있습니까?
jeongmin.cha

9
@ jeongmin.cha 다른 연산이 있고 더 많은 삽입 (동시 연산)이있는 경우 문제가 있습니다. 명시 적으로 잠금을 해제하고 해제하지 않으면 max id가 변경되었음을 의미합니다.
rohanagarwal

의도 된 사용 사례가 마지막 삽입 ID를 후속 삽입 값의 일부로 사용하는 경우이 질문을 참조하십시오 .
플럭스

답변:


606

( tl;dr: goto 옵션 3 : RETURNING과 함께 삽입)

postgresql에는 테이블에 대한 "id"개념이 없으며 시퀀스 (일반적으로 SERIAL 의사 유형 의 대리 기본 키의 기본값으로 사용되는 것은 아님 ) 만 있습니다.

새로 삽입 된 행의 ID를 얻는 데 관심이있는 경우 몇 가지 방법이 있습니다.


옵션 1 : CURRVAL(<sequence name>);.

예를 들면 다음과 같습니다.

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval('persons_id_seq');

시퀀스의 이름을 알고 있어야합니다. 실제로는 임의적입니다. 이 예에서는 테이블 에 의사 유형으로 작성된 열 persons이 있다고 가정합니다 . 이것에 의존하지 않고 더 깨끗하게 느끼려면 대신 다음을 사용할 수 있습니다 .idSERIALpg_get_serial_sequence

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval(pg_get_serial_sequence('persons','id'));

주의 사항 : 같은 세션에서 ( 실행 한 ) currval()후에 만 작동합니다 .INSERTnextval()


옵션 2 : LASTVAL();

이것은 이전과 비슷하지만 시퀀스 이름을 지정할 필요가 없습니다. 가장 최근에 수정 된 시퀀스를 찾습니다 (세션 내에서 항상 위와 동일한 경고).


모두 CURRVALLASTVAL완전히 동시 안전합니다. PG의 시퀀스 동작은 다른 세션이 방해하지 않도록 설계되어 경쟁 조건의 위험이 없습니다 (다른 세션이 내 INSERT와 내 SELECT 사이에 다른 행을 삽입하면 여전히 올바른 값을 얻습니다).

그러나 미묘한 잠재적 인 문제가 있습니다. 데이터베이스에 TRIGGER (또는 RULE)가 있으면 persons테이블 에 삽입 할 때 다른 테이블에 추가로 삽입 LASTVAL하는 경우 잘못된 값을 줄 수 있습니다. CURRVAL여분의 삽입이 동일한 persons테이블에서 수행되면 문제는 심지어 발생할 수 있습니다 (이는 훨씬 덜 일반적이지만 위험은 여전히 ​​존재합니다).


옵션 3 : INSERTRETURNING

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(물론 다른 거래에서 커밋되지 않은 삽입물을 읽지 않습니다).

반대로, 당신이해야 결코 사용하지 SELECT max(id) FROM table당신의에 의해 생성 된 ID를 얻기 위해, 대신 위의 3 개 가지 옵션 중 하나를 INSERT(분리 성능에서)이 동시 안전이 아니기 때문에, 문을 : 당신의 사이 INSERTSELECT다른 세션 다른 레코드를 삽입 할 수도 있습니다.


25
LASTVAL ()은 행을 다른 테이블에 삽입하는 트리거 / 규칙을 추가하는 경우 매우 악할 수 있습니다.
Kouber Saparev

3
SELECT max(id)불행히도 행 삭제를 시작하자마자 작업을 수행하지 않습니다.
Simon A. Eugster

2
@leonbloy 당신이 ID를 가진 행이있는 경우, 뭔가를 놓친하지 않는 1,2,3,4,5및 행 4와 5를 삭제하고, 마지막으로 삽입 된 ID는 여전히 5하지만, max()반환 3.
사이먼 A. Eugster

9
RETURNING id;이것을 다른 테이블에 삽입 하는 방법에 대한 샘플을 환영합니다!
Olivier Pons

8
명령 행 도구 RETURNING id에 제공된 SQL 스크립트 내에서 어떻게 사용할 수 psql있습니까?
amoe

82

INSERT 문의 RETURNING 절을 참조하십시오 . 기본적으로 INSERT는 쿼리로 두 배가되고 삽입 된 값을 반환합니다.


4
버전 8.2부터 작동하며 가장 빠르고 빠른 솔루션입니다.
Frank Heikens

9
아마도 반환 된 ID를 사용하는 방법에 대한 간단한 설명입니까?
Andrew

@Andrew 질문을 이해하지 못했습니다. 쿼리에서 결과를 검색하는 방법을 모르십니까? 언어 / 라이브러리에 따라 다르며 선택 또는 반환 삽입에 관계없이 동일하게 작동해야합니다. 내가 다룰 수있는 유일한 다른 해석은 전화에서 ID를 성공적으로 검색했으며 ID가 무엇인지 알지 못한다는 것입니다. 어떤 경우에, 당신은 왜 그것을 검색 했습니까?
kwatford

8
@Andrew, psql 명령 줄에서 this : insert into names (firstname, lastname) values ('john', 'smith') returning id;을 실행하면 마치 select id from names where id=$lastid직접 실행 한 것처럼 단순히 id를 출력 합니다. 당신이 할 경우 AA 변수로 수익을 저장 한 후 insert into names (firstname, lastname) values ('john', 'smith') returning id into other_variable;복귀를 포함하는 문이 경우 함수의 마지막 문장이 다음 ID가되는 returning'에드는 전체 함수에 의해 반환됩니다.
Alexander Bird

32

다음과 같이 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 

28

Leonbloy의 답변 은 매우 완전합니다. OPTION 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의 OPTION 3의 약간의 변형을 사용해야합니다.

INSERT INTO person(lastname, firstname) 
VALUES (lastn, firstn) 
RETURNING id INTO [new_variable];

두 개의 INTO 절이 있습니다. 따라서 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

9

모든 데이터 레코드를 가져와야하는 사람들을 위해

returning *

ID를 포함한 모든 객체를 얻으려면 쿼리 끝까지.


8

아래 예를 참조하십시오

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


1

이 시도:

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"이 원하는 시퀀스 값과 함께 반환됩니다.


1

Postgres에는 동일한 메커니즘에 내장 메커니즘이 있으며, 동일한 쿼리에서 ID 또는 쿼리가 반환하려는 항목을 반환합니다. 여기 예가 있습니다. column1과 column2라는 2 개의 열이있는 테이블을 작성하고 모든 삽입 후에 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)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.