[postgresql] Postgres의 기존 열에 ‘직렬’추가

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

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



답변

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

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;


답변

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


답변

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


답변