จะรีเซ็ตลำดับใน postgres และเติมคอลัมน์ id ด้วยข้อมูลใหม่ได้อย่างไร


126

ฉันมีโต๊ะที่มีมากกว่าล้านแถว ฉันต้องการรีเซ็ตลำดับและกำหนดคอลัมน์รหัสใหม่ด้วยค่าใหม่ (1, 2, 3, 4 ... ฯลฯ ... ) มีวิธีง่ายๆในการทำเช่นนั้นหรือไม่?


6
คำถามที่แท้จริง: ทำไมคุณถึงอยากทำเช่นนั้นบนโลกนี้? สันนิษฐานว่า ID เป็นคีย์หลักดังนั้นจึงไม่มีประโยชน์ใด ๆ ในการเปลี่ยนคีย์หลัก คีย์หลักคือค่าที่ไม่มีความหมาย (ในกรณีของคุณเป็นสิ่งประดิษฐ์) "การเปลี่ยนหมายเลข" ไม่ได้ตอบสนองวัตถุประสงค์ที่สมเหตุสมผลในฐานข้อมูลเชิงสัมพันธ์
a_horse_with_no_name

2
ตอนแรกฉันมีแอปที่ทำงานในเครื่องจากนั้นฉันก็คัดลอกข้อมูลไปยังการผลิต แต่idไม่มีการเริ่มต้นจาก 1 ดังนั้นการเรียงลำดับจึงเป็นดังนี้ 150, 151 ... , 300, 1, 2 ... และมันจะทำให้เกิดข้อผิดพลาด id ซ้ำในที่สุดฉันก็คิดว่าถ้าฉันไม่ได้เรียงลำดับใหม่ รหัส นอกจากนี้การสั่งซื้อโดยทั่วไปดีขึ้นกว่าที่สั่งซื้อโดยid created_atและนี่คือสิ่งที่ทำงานสำหรับฉัน
x-yuri

จุดสำคัญของการทำเช่นนี้คือคุณสามารถใช้ int ปกติต่อไปแทน bigint สำหรับคีย์หลักในฐานข้อมูลที่ยังคงเพิ่มคีย์ลำดับต่อไป แต่ได้รับข้อมูลใหม่อย่างต่อเนื่อง คุณจะพบกับขีด จำกัด จำนวนเต็มที่ลงนามอย่างรวดเร็วและหากการรักษา ID ที่ยังหลงเหลืออยู่ไม่สำคัญกระบวนการนี้จะนำคุณกลับไปสู่หมายเลขประจำตัวที่จัดการได้
Ben Wilson

การใช้งานอื่นสำหรับสิ่งนี้คือการทดสอบ คุณต้องการรีเซ็ตตารางให้อยู่ในสถานะที่ทราบก่อนเริ่มการทดสอบแต่ละครั้งและต้องรีเซ็ตรหัส
Safa Alai

คำตอบ:


204

หากคุณไม่ต้องการคงลำดับของรหัสไว้คุณสามารถทำได้

ALTER SEQUENCE seq RESTART WITH 1;
UPDATE t SET idcolumn=nextval('seq');

ฉันสงสัยว่ามีวิธีง่ายๆในการทำตามลำดับที่คุณเลือกโดยไม่ต้องสร้างใหม่ทั้งตาราง


4
ไม่ควรอย่างนั้นALTER SEQUENCE seq RESTART WITH 1;หรือ?
Lars Haugseth

5
ซึ่งอาจทำให้เกิดรหัสซ้ำกัน เพื่อป้องกันปัญหานี้ก่อนอื่นคุณสามารถตั้งค่าทั้งหมดเป็นค่าที่สูงมาก: UPDATE t SET idcolumn = 1000000 + nextval ('seq'); จากนั้นเรียกใช้สคริปต์ด้านบน
tahagh

5
SELECT setval('seq', 1, FALSE)ควรทำเช่นเดียวกัน (ที่นี่อาร์กิวเมนต์ที่สาม FALSE ทำเวทมนตร์ตามที่แสดงว่าnextvalต้องเป็น 1 แทนที่จะเป็น 2)
Vasilen Donchev

@VassilenDontchev อย่างแน่นอน
Michael Krelin - แฮ็กเกอร์

55

ด้วย PostgreSQL 8.4 หรือใหม่กว่าไม่จำเป็นต้องระบุไฟล์WITH 1. ค่าเริ่มต้นที่บันทึกโดยCREATE SEQUENCEหรือตั้งค่าล่าสุดโดยALTER SEQUENCE START WITHจะถูกใช้ (ส่วนใหญ่อาจเป็น 1)

รีเซ็ตลำดับ:

ALTER SEQUENCE seq RESTART;

จากนั้นอัปเดตคอลัมน์ ID ของตาราง:

UPDATE foo SET id = DEFAULT;

ที่มา: PostgreSQL Docs


3
นี่ดูเหมือนจะเป็นคำตอบที่ดีที่สุดเนื่องจากหลีกเลี่ยงการตั้งสมมติฐานเกี่ยวกับค่าเริ่มต้นของลำดับ
sheepdog

คำตอบที่ดีที่สุดสำหรับกรณีของฉันด้วย ฉันรวมคำตอบนี้เข้ากับคำตอบนี้ซึ่งอธิบายคำสั่ง ALTER SEQUENCE ... ดังนั้นฉันจึงเปลี่ยน 'seq' โดยmytable_id_seqโดย ที่ 'mytable' เป็นชื่อตารางของฉันและ 'id' คือชื่อของคอลัมน์อนุกรมของฉัน
Javi

42

รีเซ็ตลำดับ:

SELECT setval('sequence_name', 0);

การอัปเดตบันทึกปัจจุบัน:

UPDATE foo SET id = DEFAULT;

3
ลำดับอาจมีค่าต่ำสุดมากกว่า 0 (และค่าต่ำสุดเริ่มต้นที่ใช้ตามประเภทserialและCREATE SEQUENCEคือ 1!)
brk

18

วิธีแก้ปัญหาที่ให้มาทั้งสองไม่ได้ผลสำหรับฉัน

> SELECT setval('seq', 0);
ERROR:  setval: value 0 is out of bounds for sequence "seq" (1..9223372036854775807)

setval('seq', 1)เริ่มการกำหนดหมายเลขด้วย 2 และALTER SEQUENCE seq START 1เริ่มการกำหนดหมายเลขด้วย 2 เช่นกันเนื่องจาก seq.is_called เป็นจริง (Postgres เวอร์ชัน 9.0.4)

วิธีแก้ปัญหาที่เหมาะกับฉันคือ:

> ALTER SEQUENCE seq RESTART WITH 1;
> UPDATE foo SET id = DEFAULT;

1
ปัญหาเดียวกันที่นี่ โซลูชันของคุณใช้ได้กับ PostgreSQL 8.3.10 เช่นกัน
PeqNP

17

เพียงเพื่อลดความซับซ้อนและชี้แจงการใช้ALTER SEQUENCEและSELECT setval ที่เหมาะสมสำหรับการรีเซ็ตลำดับ:

ALTER SEQUENCE sequence_name RESTART WITH 1;

เทียบเท่ากับ

SELECT setval('sequence_name', 1, FALSE);

อาจใช้คำสั่งอย่างใดอย่างหนึ่งเพื่อรีเซ็ตลำดับและคุณจะได้รับค่าถัดไปตามค่า nextval ('Sequence_name') ตามที่ระบุไว้ที่นี่ด้วย:

nextval('sequence_name')

ขอบคุณอาลี ฉันเพิ่งสังเกตเห็นว่าการตั้งค่าพารามิเตอร์ที่ 3 นั้นเป็นเท็จด้วยฟังก์ชัน
setval

14

วิธีที่ดีที่สุดในการรีเซ็ตลำดับเพื่อเริ่มต้นด้วยหมายเลข 1 คือดำเนินการดังต่อไปนี้:

ALTER SEQUENCE <tablename>_<id>_seq RESTART WITH 1

ตัวอย่างเช่นสำหรับตารางผู้ใช้จะเป็น:

ALTER SEQUENCE users_id_seq RESTART WITH 1

6

ในการรักษาลำดับของแถว:

UPDATE thetable SET rowid=col_serial FROM 
(SELECT rowid, row_number() OVER ( ORDER BY lngid) AS col_serial FROM thetable ORDER BY lngid) AS t1 
WHERE thetable.rowid=t1.rowid;

4

FYI: หากคุณต้องการระบุค่าเริ่มต้นใหม่ระหว่างช่วงของ ID (เช่น 256 - 10,000000):

SELECT setval('"Sequence_Name"', 
       (SELECT coalesce(MAX("ID"),255) 
           FROM "Table_Name" 
           WHERE "ID" < 10000000 and "ID" >= 256)+1
       ); 

2

เพียงแค่รีเซ็ตลำดับและอัปเดตแถวทั้งหมดอาจทำให้เกิดข้อผิดพลาด id ซ้ำกัน ในหลาย ๆ กรณีคุณต้องอัปเดตแถวทั้งหมดสองครั้ง อันดับแรกด้วยรหัสที่สูงกว่าเพื่อหลีกเลี่ยงการซ้ำกันจากนั้นจึงใช้รหัสที่คุณต้องการจริงๆ

โปรดหลีกเลี่ยงการเพิ่มจำนวนคงที่ให้กับรหัสทั้งหมด (ตามที่แนะนำในความคิดเห็นอื่น ๆ ) จะเกิดอะไรขึ้นถ้าคุณมีแถวมากกว่าจำนวนที่กำหนดนี้? สมมติว่าค่าถัดไปของลำดับสูงกว่ารหัสทั้งหมดของแถวที่มีอยู่ (คุณแค่ต้องการเติมช่องว่าง) ฉันจะทำดังนี้:

UPDATE table SET id = DEFAULT;
ALTER SEQUENCE seq RESTART;
UPDATE table SET id = DEFAULT;

1

ในกรณีของฉันฉันทำได้ด้วย:

ALTER SEQUENCE table_tabl_id_seq RESTART WITH 6;

ตารางของฉันชื่อตาราง


ขอขอบคุณที่รวมตัวอย่างที่เป็นรูปธรรมพร้อมชื่อตารางของคุณ คำตอบอื่น ๆ ค่อนข้างคลุมเครือเกินไป
Brylie Christopher Oxley

0

แรงบันดาลใจจากคำตอบอื่น ๆ ที่นี่ฉันสร้างฟังก์ชัน SQL เพื่อทำการโยกย้ายลำดับ ฟังก์ชันจะย้ายลำดับคีย์หลักไปยังลำดับที่ต่อเนื่องกันใหม่โดยเริ่มต้นด้วยค่าใด ๆ (> = 1) ทั้งภายในหรือภายนอกช่วงลำดับที่มีอยู่

ฉันอธิบายที่นี่ว่าฉันใช้ฟังก์ชันนี้อย่างไรในการย้ายฐานข้อมูลสองฐานข้อมูลที่มีสคีมาเดียวกัน แต่มีค่าต่างกันในฐานข้อมูลเดียว

ขั้นแรกฟังก์ชั่น (ซึ่งพิมพ์คำสั่ง SQL ที่สร้างขึ้นเพื่อให้ชัดเจนว่าเกิดอะไรขึ้น):

CREATE OR REPLACE FUNCTION migrate_pkey_sequence
  ( arg_table      text
  , arg_column     text
  , arg_sequence   text
  , arg_next_value bigint  -- Must be >= 1
  )
RETURNS int AS $$
DECLARE
  result int;
  curr_value bigint = arg_next_value - 1;
  update_column1 text := format
    ( 'UPDATE %I SET %I = nextval(%L) + %s'
    , arg_table
    , arg_column
    , arg_sequence
    , curr_value
    );
  alter_sequence text := format
    ( 'ALTER SEQUENCE %I RESTART WITH %s'
    , arg_sequence
    , arg_next_value
    );
  update_column2 text := format
    ( 'UPDATE %I SET %I = DEFAULT'
    , arg_table
    , arg_column
    );
  select_max_column text := format
    ( 'SELECT coalesce(max(%I), %s) + 1 AS nextval FROM %I'
    , arg_column
    , curr_value
    , arg_table
    );
BEGIN
  -- Print the SQL command before executing it.
  RAISE INFO '%', update_column1;
  EXECUTE update_column1;
  RAISE INFO '%', alter_sequence;
  EXECUTE alter_sequence;
  RAISE INFO '%', update_column2;
  EXECUTE update_column2;
  EXECUTE select_max_column INTO result;
  RETURN result;
END $$ LANGUAGE plpgsql;

ฟังก์ชันmigrate_pkey_sequenceรับอาร์กิวเมนต์ต่อไปนี้:

  1. arg_table: ชื่อตาราง (เช่น'example')
  2. arg_column: ชื่อคอลัมน์คีย์หลัก (เช่น'id')
  3. arg_sequence: ชื่อลำดับ (เช่น'example_id_seq')
  4. arg_next_value: ค่าถัดไปสำหรับคอลัมน์หลังการโอนย้าย

ดำเนินการดังต่อไปนี้:

  1. ย้ายค่าคีย์หลักไปยังช่วงว่าง ฉันคิดว่า nextval('example_id_seq')ต่อไปนี้max(id)และว่าลำดับเริ่มต้นด้วย 1 arg_next_value > max(id)นี้ยังจัดการกรณีที่
  2. arg_next_valueย้ายค่าคีย์หลักในช่วงที่ติดกันเริ่มต้นด้วย ลำดับของค่าคีย์จะถูกรักษาไว้ แต่ช่องว่างในช่วงจะไม่ถูกรักษา
  3. พิมพ์ค่าถัดไปที่จะตามมาในลำดับ สิ่งนี้มีประโยชน์หากคุณต้องการย้ายคอลัมน์ของตารางอื่นและรวมเข้ากับตารางนี้

เพื่อแสดงให้เห็นเราใช้ลำดับและตารางที่กำหนดไว้ดังต่อไปนี้ (เช่นใช้psql):

# CREATE SEQUENCE example_id_seq
  START WITH 1
  INCREMENT BY 1
  NO MINVALUE
  NO MAXVALUE
  CACHE 1;
# CREATE TABLE example
  ( id bigint NOT NULL DEFAULT nextval('example_id_seq'::regclass)
  );

จากนั้นเราแทรกค่าบางอย่าง (เริ่มต้นเช่นที่ 3):

# ALTER SEQUENCE example_id_seq RESTART WITH 3;
# INSERT INTO example VALUES (DEFAULT), (DEFAULT), (DEFAULT);
-- id: 3, 4, 5

สุดท้ายเราย้ายexample.idค่าเริ่มต้นด้วย 1

# SELECT migrate_pkey_sequence('example', 'id', 'example_id_seq', 1);
INFO:  00000: UPDATE example SET id = nextval('example_id_seq') + 0
INFO:  00000: ALTER SEQUENCE example_id_seq RESTART WITH 1
INFO:  00000: UPDATE example SET id = DEFAULT
 migrate_pkey_sequence
-----------------------
                     4
(1 row)

ผลลัพธ์:

# SELECT * FROM example;
 id
----
  1
  2
  3
(3 rows)

0

แม้แต่คอลัมน์การเพิ่มอัตโนมัติก็ไม่ใช่ PK (ในตัวอย่างนี้เรียกว่าลำดับ seq - aka) คุณสามารถทำได้ด้วยทริกเกอร์:

วางตารางหากมีอยู่ devops_guide CASCADE;

SELECT 'create the "devops_guide" table'
;
   CREATE TABLE devops_guide (
      guid           UUID NOT NULL DEFAULT gen_random_uuid()
    , level          integer NULL
    , seq            integer NOT NULL DEFAULT 1
    , name           varchar (200) NOT NULL DEFAULT 'name ...'
    , description    text NULL
    , CONSTRAINT pk_devops_guide_guid PRIMARY KEY (guid)
    ) WITH (
      OIDS=FALSE
    );

-- START trg_devops_guide_set_all_seq
CREATE OR REPLACE FUNCTION fnc_devops_guide_set_all_seq()
    RETURNS TRIGGER
    AS $$
       BEGIN
         UPDATE devops_guide SET seq=col_serial FROM
         (SELECT guid, row_number() OVER ( ORDER BY seq) AS col_serial FROM devops_guide ORDER BY seq) AS tmp_devops_guide
         WHERE devops_guide.guid=tmp_devops_guide.guid;

         RETURN NEW;
       END;
    $$ LANGUAGE plpgsql;

 CREATE TRIGGER trg_devops_guide_set_all_seq
  AFTER UPDATE OR DELETE ON devops_guide
  FOR EACH ROW
  WHEN (pg_trigger_depth() < 1)
  EXECUTE PROCEDURE fnc_devops_guide_set_all_seq();

-1

หากคุณใช้ pgAdmin3 ขยาย 'ลำดับ' คลิกขวาที่ลำดับไปที่ 'คุณสมบัติ' และในแท็บ 'คำจำกัดความ' ให้เปลี่ยน 'ค่าปัจจุบัน' เป็นค่าใดก็ได้ที่คุณต้องการ ไม่จำเป็นต้องมีการสอบถาม


3
คำตอบของคุณไม่มีค่าอะไรเลยถ้าอย่างน้อยคุณก็ไม่บอกเราว่าคุณกำลังใช้เครื่องมืออะไร
11101101b

3
นี่เป็นวิธีที่ง่ายที่สุดเห็นได้ชัดว่าฉันคิดว่าเขากำลังพูดว่า pg admin 3
MvcCmsJon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.