Postgres의 기존 열에 '직렬'추가


98

Postgres 9.0 데이터베이스에 현재 1부터 시작하는 고유 한 순차 정수가 포함되어 있지만 'serial'키워드를 사용하여 생성되지 않은 정수 ID 필드 (기본 키)가있는 작은 테이블 (~ 30 행)이 있습니다.

지금부터이 테이블에 삽입 할 때이 필드가 유형으로 'serial'을 사용하여 생성 된 것처럼 작동하도록이 테이블을 어떻게 변경할 수 있습니까?


5
참고로 SERIAL가상 유형은 이제 레거시 이며 Postgres 10 이상 에서 SQL : 2003에 정의 된 새로운 GENERATED … AS IDENTITY기능으로 대체되었습니다 . 설명을 참조하십시오 .
Basil Bourque

최신 Postgres 버전 (> = 10)의 경우 다음 질문을 참조하십시오. stackoverflow.com/questions/2944499
a_horse_with_no_name

답변:


135

다음 명령 (특히 주석이 달린 블록)을보십시오.

DROP TABLE foo;
DROP TABLE bar;

CREATE TABLE foo (a int, b text);
CREATE TABLE bar (a serial, b text);

INSERT INTO foo (a, b) SELECT i, 'foo ' || i::text FROM generate_series(1, 5) i;
INSERT INTO bar (b) SELECT 'bar ' || i::text FROM generate_series(1, 5) i;

-- blocks of commands to turn foo into bar
CREATE SEQUENCE foo_a_seq;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');
ALTER TABLE foo ALTER COLUMN a SET NOT NULL;
ALTER SEQUENCE foo_a_seq OWNED BY foo.a;    -- 8.2 or later

SELECT MAX(a) FROM foo;
SELECT setval('foo_a_seq', 5);  -- replace 5 by SELECT MAX result

INSERT INTO foo (b) VALUES('teste');
INSERT INTO bar (b) VALUES('teste');

SELECT * FROM foo;
SELECT * FROM bar;

OP에서 기본 키를 언급하고 있으므로 ALTER TABLE foo ADD PRIMARY KEY (a).
Skippy le Grand Gourou 2014 년

SERIAL은 구문 적 설탕이며 DB 메타 데이터에 저장되지 않으므로 위 코드는 100 % 동일합니다.
DKroot

다른 사용자가 대상 테이블을 생성 할 가능성이있는 경우 ALTER TABLE foo OWNER TO current_user;먼저 수행해야합니다 .
DKroot

2
MAX(a)+1setval에서 설정해야하지 않습니까? SELECT MAX(a)+1 FROM foo; SELECT setval('foo_a_seq', 6);
SunnyPro

49

START WITHsetval이 Euler의 대답에서와 같이 동일한 작업을 수행하더라도 특정 지점에서 시퀀스를 시작하는 데 사용할 수도 있습니다 .

SELECT MAX(a) + 1 FROM foo;
CREATE SEQUENCE foo_a_seq START WITH 12345; -- replace 12345 with max above
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');

31

TL; DR

다음은 값을 읽고 직접 입력하는 데 사람이 필요하지 않은 버전입니다.

CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq'); 

또 다른 옵션은 Function이 답변의 끝에 재사용 가능한 공유 를 사용하는 것입니다.


비대화 형 솔루션

예를 들어 라이브 DB를 패치하는 동안 비 대화 형 스크립트로Sequence 생성 해야하는 사람들을 위해 다른 두 답변에 추가하는 것입니다 .

즉, SELECT값을 수동으로 입력 하고 싶지 않을 때 다음 CREATE문에 직접 입력합니다 .

요컨대 다음을 할 수 없습니다 .

CREATE SEQUENCE foo_a_seq
    START WITH ( SELECT max(a) + 1 FROM foo );

...의 START [WITH]절이 하위 쿼리가 아닌 CREATE SEQUENCE기대하기 때문 입니다.

주 : (모든 비 CRUD에 적용 엄지 손가락의 규칙으로서, : 이외의 INSERT, SELECT, UPDATE, DELETE의 문) pgSQL의 AFAIK.

그러나 setval()그렇습니다! 따라서 다음은 절대적으로 좋습니다.

SELECT setval('foo_a_seq', max(a)) FROM foo;

데이터가없고 그것에 대해 알지 못하는 경우 (원하는)을 사용 coalesce()하여 기본값을 설정하십시오.

SELECT setval('foo_a_seq', coalesce(max(a), 0)) FROM foo;
--                         ^      ^         ^
--                       defaults to:       0

그러나 현재 시퀀스 값을로 설정하는 0것은 불법이 아니라면 어색합니다.
3 개 매개 변수 형식을 사용하는 setval것이 더 적절합니다.

--                                             vvv
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
--                                                  ^   ^
--                                                is_called

setvalto 의 선택적 세 번째 매개 변수를 설정하면 값을 반환하기 전에 false다음 매개 변수가 nextval시퀀스를 진행 하지 못하므로 다음 을 수행합니다.

다음 nextval은 지정된 값을 정확하게 반환하고 시퀀스 진행은 다음과 함께 시작됩니다 nextval.

문서의이 항목에서

관련되지 않은 메모에서를 사용하여 Sequence직접 소유하는 열을 지정할 수도 있으며 CREATE나중에 변경할 필요가 없습니다.

CREATE SEQUENCE foo_a_seq OWNED BY foo.a;

요약해서 말하자면:

CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq'); 

사용 Function

또는 여러 열에 대해이 작업을 계획하는 경우 실제 Function.

CREATE OR REPLACE FUNCTION make_into_serial(table_name TEXT, column_name TEXT) RETURNS INTEGER AS $$
DECLARE
    start_with INTEGER;
    sequence_name TEXT;
BEGIN
    sequence_name := table_name || '_' || column_name || '_seq';
    EXECUTE 'SELECT coalesce(max(' || column_name || '), 0) + 1 FROM ' || table_name
            INTO start_with;
    EXECUTE 'CREATE SEQUENCE ' || sequence_name ||
            ' START WITH ' || start_with ||
            ' OWNED BY ' || table_name || '.' || column_name;
    EXECUTE 'ALTER TABLE ' || table_name || ' ALTER COLUMN ' || column_name ||
            ' SET DEFAULT nextVal(''' || sequence_name || ''')';
    RETURN start_with;
END;
$$ LANGUAGE plpgsql VOLATILE;

다음과 같이 사용하십시오.

INSERT INTO foo (data) VALUES ('asdf');
-- ERROR: null value in column "a" violates not-null constraint

SELECT make_into_serial('foo', 'a');
INSERT INTO foo (data) VALUES ('asdf');
-- OK: 1 row(s) affected

좋은 대답이지만 coalesce(max(a), 0))대부분의 경우 Ids가 1부터 시작하므로 대부분의 경우 작동하지 않습니다. 더 정확한 방법은 다음과 같습니다coalesce(max(a), 1))
Amiko

1
댓글에 대해 @Amiko에게 감사드립니다! 이 setval함수는 실제로 시퀀스에 대해 현재 "최근 사용 된 값"만 설정합니다. 다음 사용 가능한 값 (실제로 사용되는 첫 번째 값)이 하나 더 있습니다! setval(..., coalesce(max(a), 1))빈 열에 사용 하면 문서에2 설명 된대로 (사용 가능한 다음 값) 으로 "시작"하도록 설정됩니다 .
ccjmne

1
@Amiko 그래도 내 코드에 문제가 있다고 말하는 것이 맞습니다 . 실제 데이터 세트에 반영되지 않더라도 currval는 절대해서는 안됩니다 0. 세 매개 변수 형식을 사용하는 setval것이 더 적절할 것 setval(..., coalesce(max(a), 0) + 1, false)입니다.. 그에 따라 답변이 업데이트되었습니다!
ccjmne

1
동의합니다. 저는 그것을 완전히 놓쳤습니다. 답변에 감사드립니다.
Amiko
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.