자주 (자주 읽지 만 드물게 작성된) 대형 (20M 행) 열을 백필로 채우고 싶습니다. 다양한 articles 및 questions on SO에서이 작업을 수행하는 가장 좋은 방법은 구조가 동일한 테이블을 만들고 백필 데이터에로드 한 다음 라이브 스왑 (이름 바꾸기가 매우 빠르기 때문에)과 같은 것입니다. 좋은 소리!Postgres에서 테이블 스와핑이 너무 장황한 이유는 무엇입니까?
그러나 실제로이 작업을 수행하는 스크립트를 작성하면 이 마침내입니다. 여기에 맛이 있습니다 :
BEGIN;
CREATE TABLE foo_new (LIKE foo);
-- I don't use INCLUDING ALL, because that produces Indexes/Constraints with different names
-- This is the only part of the script that is specific to my case.
-- Everything else is standard for any table swap
INSERT INTO foo_new (id, first_name, last_name, email, full_name)
(SELECT id, first_name, last_name, email, first_name || last_name) FROM foo);
CREATE SEQUENCE foo_new_id_seq
START 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
SELECT setval('foo_new_id_seq', COALESCE((SELECT MAX(id)+1 FROM foo_new), 1), false);
ALTER SEQUENCE foo_new_id_seq OWNED BY foo_new.id;
ALTER TABLE ONLY foo_new ALTER COLUMN id SET DEFAULT nextval('foo_new_id_seq'::regclass);
ALTER TABLE foo_new
ADD CONSTRAINT foo_new_pkey
PRIMARY KEY (id);
COMMIT;
-- Indexes are made concurrently, otherwise they would block reads for
-- a long time. Concurrent index creation cannot occur within a transaction.
CREATE INDEX CONCURRENTLY foo_new_on_first_name ON foo_new USING btree (first_name);
CREATE INDEX CONCURRENTLY foo_new_on_last_name ON foo_new USING btree (last_name);
CREATE INDEX CONCURRENTLY foo_new_on_email ON foo_new USING btree (email);
-- One more line for each index
BEGIN;
ALTER TABLE foo RENAME TO foo_old;
ALTER TABLE foo_new RENAME TO foo;
ALTER SEQUENCE foo_id_seq RENAME TO foo_old_id_seq;
ALTER SEQUENCE foo_new_id_seq RENAME TO foo_id_seq;
ALTER TABLE foo_old RENAME CONSTRAINT foo_pkey TO foo_old_pkey;
ALTER TABLE foo RENAME CONSTRAINT foo_new_pkey TO foo_pkey;
ALTER INDEX foo_on_first_name RENAME TO foo_old_on_first_name;
ALTER INDEX foo_on_last_name RENAME TO foo_old_on_last_name;
ALTER INDEX foo_on_email RENAME TO foo_old_on_email;
-- One more line for each index
ALTER INDEX foo_new_on_first_name RENAME TO foo_on_first_name;
ALTER INDEX foo_new_on_last_name RENAME TO foo_on_last_name;
ALTER INDEX foo_new_on_email RENAME TO foo_on_email;
-- One more line for each index
COMMIT;
-- TODO: drop old table (CASCADE)
그리고 이것은 외래 키나 다른 제약을 포함하지 않습니다! INSERT INTO
비트의 내 사례에만 해당되는 부분이 있기 때문에 이러한 스와핑 작업을 수행하는 내장 된 Postgres 함수가 없다는 사실에 놀랐습니다. 이 작업이 내가하는 것보다 덜 일반적입니까? 이것이 달성 될 수있는 다양한 방법을 과소 평가하고 있습니까? 이름을 일관되게 비정규 적으로 유지하겠습니까?
올바른 정의로 두 테이블을 유지 한 다음 뷰를 사용하여 "활성"테이블에서 데이터를 가져옵니다. 끊임없이 테이블을 만들고 삭제할 필요가 없습니다. 간단한 'truncate ... restart identity'는 목표 테이블을 지우기에 충분합니다. –
나는이 아이디어가 마음에 든다. 그리고 내가 자주 한 테이블을 대량 업데이트한다면 분명히 도움이된다. 그러나 우리는 [보기에 외래 키를 정의 할 수 없습니다] (https://www.postgresql.org/message-id/[email protected]), 그래서 우리는 이들을 모두 바꿀 필요가 있습니다. 시각. 그리고 매번 다른 테이블을 대량 업데이트한다면이 점이 도움이되지 않습니다. –
FK 제한이 문제입니다. 동의합니다. 자주해야 할 일이 있다면, 동적 SQL을 사용하는 모든 기능을 만들 것입니다. –