มันขึ้นอยู่กับรายละเอียดความต้องการของคุณเป็นอย่างมาก
หากคุณมีพื้นที่ว่างเพียงพอ (อย่างน้อย 110% ของpg_size_pretty((pg_total_relation_size(tbl))
) บนดิสก์และสามารถจ่ายได้ล็อคหุ้นสำหรับบางเวลาและล็อคพิเศษสำหรับเวลาที่สั้นมากแล้วสร้างตารางใหม่รวมทั้งคอลัมน์โดยใช้uuid
CREATE TABLE AS
ทำไม?
ด้านล่างรหัสใช้ฟังก์ชั่นจากเพิ่มเติมuuid-oss
โมดูล
ล็อคตารางจากการเปลี่ยนแปลงที่เกิดขึ้นพร้อมกันในSHARE
โหมด (ยังคงอนุญาตให้อ่านพร้อมกัน) ความพยายามที่จะเขียนลงในตารางจะรอและล้มเหลวในที่สุด ดูด้านล่าง
คัดลอกตารางทั้งหมดในขณะที่เติมคอลัมน์ใหม่ในทันที - อาจเป็นไปได้ในการสั่งซื้อแถวในขณะที่อยู่ที่มัน
หากคุณจะเรียงลำดับแถวใหม่ให้แน่ใจว่าได้ตั้งค่าไว้work_mem
สูงที่สุดเท่าที่จะทำได้ (เฉพาะเซสชันของคุณไม่ใช่ทั่วโลก)
จากนั้นเพิ่มข้อ จำกัด คีย์ต่างประเทศดัชนีทริกเกอร์ ฯลฯ ลงในตารางใหม่ เมื่อปรับปรุงส่วนใหญ่ของตารางมันจะมากได้เร็วขึ้นในการสร้างดัชนีจากรอยขีดข่วนมากกว่าที่จะเพิ่มแถวซ้ำ
เมื่อตารางใหม่พร้อมวางแบบเก่าแล้วเปลี่ยนชื่อใหม่เพื่อให้แทนที่แบบดรอปอิน เฉพาะขั้นตอนสุดท้ายนี้เท่านั้นที่จะได้รับการล็อคแบบเอกสิทธิ์เฉพาะบุคคลบนตารางเก่าสำหรับส่วนที่เหลือของการทำธุรกรรม - ซึ่งควรจะสั้นมากในขณะนี้
นอกจากนี้ยังต้องการให้คุณลบวัตถุใด ๆ ขึ้นอยู่กับประเภทของตาราง (มุมมองฟังก์ชั่นการใช้ประเภทตารางในลายเซ็น, ... ) และสร้างพวกเขาในภายหลัง
ทำทุกอย่างในหนึ่งธุรกรรมเพื่อหลีกเลี่ยงสถานะที่ไม่สมบูรณ์
BEGIN;
LOCK TABLE tbl IN SHARE MODE;
SET LOCAL work_mem = '???? MB'; -- just for this transaction
CREATE TABLE tbl_new AS
SELECT uuid_generate_v1() AS tbl_uuid, <list of all columns in order>
FROM tbl
ORDER BY ??; -- optionally order rows favorably while being at it.
ALTER TABLE tbl_new
ALTER COLUMN tbl_uuid SET NOT NULL
, ALTER COLUMN tbl_uuid SET DEFAULT uuid_generate_v1()
, ADD CONSTRAINT tbl_uuid_uni UNIQUE(tbl_uuid);
-- more constraints, indices, triggers?
DROP TABLE tbl;
ALTER TABLE tbl_new RENAME tbl;
-- recreate views etc. if any
COMMIT;
นี่ควรจะเร็วที่สุด วิธีการอัปเดตอื่น ๆ จะต้องเขียนทั้งตารางอีกครั้งด้วยวิธีที่แพงกว่า คุณจะไปเส้นทางนั้นถ้าคุณมีพื้นที่ว่างไม่เพียงพอบนดิสก์หรือไม่สามารถล็อคตารางทั้งหมดหรือสร้างข้อผิดพลาดสำหรับความพยายามในการเขียนพร้อมกัน
เกิดอะไรขึ้นกับการเขียนพร้อมกัน?
ธุรกรรมอื่น ๆ (ในเซสชันอื่น) พยายามINSERT
/ UPDATE
/ DELETE
ในตารางเดียวกันหลังจากที่การทำธุรกรรมของคุณได้ทำการSHARE
ล็อคแล้วจะรอจนกว่าการปลดล็อคจะเริ่มขึ้นหรือการหมดเวลาใช้งานจะเริ่มขึ้น พวกเขาจะล้มเหลวอย่างใดอย่างหนึ่งเนื่องจากตารางที่พวกเขาพยายามที่จะเขียนถูกลบออกจากใต้พวกเขา
ตารางใหม่มี OID ของตารางใหม่ แต่ธุรกรรมที่เกิดขึ้นพร้อมกันได้แก้ไขชื่อของตารางไปเป็น OID ของตารางก่อนหน้านี้แล้ว เมื่อล็อคถูกปล่อยออกมาในที่สุดพวกเขาพยายามที่จะล็อคโต๊ะตัวเองก่อนที่จะเขียนลงไปและพบว่ามันหายไป Postgres จะตอบ:
ERROR: could not open relation with OID 123456
123456
OID ของตารางเก่าอยู่ที่ไหน คุณต้องตรวจสอบข้อยกเว้นนั้นและลองค้นหาในรหัสแอปของคุณอีกครั้งเพื่อหลีกเลี่ยง
หากคุณไม่สามารถซื้อสิ่งนั้นได้คุณต้องเก็บตารางดั้งเดิมไว้
สองทางเลือกในการรักษาตารางที่มีอยู่
อัปเดตในสถานที่ (อาจเรียกใช้การอัปเดตในส่วนเล็ก ๆ ในเวลา) ก่อนที่คุณจะเพิ่มNOT NULL
ข้อ จำกัด การเพิ่มคอลัมน์ใหม่ด้วยค่า NULL และไม่มีNOT NULL
ข้อ จำกัด นั้นราคาถูก
ตั้งแต่ Postgres 9.2คุณสามารถสร้างCHECK
ข้อ จำกัด ด้วยNOT VALID
:
ข้อ จำกัด จะยังคงมีผลบังคับใช้กับส่วนแทรกหรือการปรับปรุงที่ตามมา
ที่ช่วยให้คุณปรับปรุงแถวpeu à peu - ในการทำธุรกรรมหลายแยก วิธีนี้จะช่วยหลีกเลี่ยงการล็อกแถวเป็นเวลานานเกินไปและยังอนุญาตให้นำแถวที่ตายแล้วกลับมาใช้ซ้ำได้ (คุณจะต้องเรียกใช้VACUUM
ด้วยตนเองหากไม่มีเวลาเพียงพอในการที่จะเปิดเครื่องอัตโนมัติ) ในที่สุดเพิ่มNOT NULL
ข้อ จำกัด และลบNOT VALID CHECK
ข้อ จำกัด :
ALTER TABLE tbl ADD CONSTRAINT tbl_no_null CHECK (tbl_uuid IS NOT NULL) NOT VALID;
-- update rows in multiple batches in separate transactions
-- possibly run VACUUM between transactions
ALTER TABLE tbl ALTER COLUMN tbl_uuid SET NOT NULL;
ALTER TABLE tbl ALTER DROP CONSTRAINT tbl_no_null;
คำตอบที่เกี่ยวข้องพูดคุยNOT VALID
ในรายละเอียดเพิ่มเติม:
เตรียมรัฐใหม่ในตารางชั่วคราว , TRUNCATE
ต้นฉบับและเติมเงินจากตาราง temp ทั้งหมดในการทำธุรกรรม คุณยังต้องใช้การSHARE
ล็อค ก่อนจัดทำตารางใหม่เพื่อป้องกันการสูญเสียการเขียนพร้อมกัน
รายละเอียดในคำตอบที่เกี่ยวข้องเหล่านี้ใน SO:
ALTER TABLE .. ADD COLUMN ...
หรือมีส่วนที่จะตอบด้วยหรือไม่?