Bagaimana cara mereset urutan di postgres dan mengisi kolom id dengan data baru?

126

Saya memiliki meja dengan lebih dari jutaan baris. Saya perlu mengatur ulang urutan dan menetapkan kembali kolom id dengan nilai baru (1, 2, 3, 4 ... dll ...). Apakah ada cara mudah untuk melakukan itu?

sennin
sumber
6
Pertanyaan sebenarnya: mengapa Anda ingin melakukan itu? Agaknya ID tersebut adalah primary key, jadi tidak ada manfaat apapun dalam merubah primary key tersebut. Kunci utama adalah nilai yang tidak berarti (dalam kasus Anda buatan). "Penomoran ulang" itu tidak melayani tujuan yang masuk akal dalam database relasional.
a_horse_with_no_name
2
Awalnya saya menjalankan aplikasi secara lokal, lalu saya menyalin data ke produksi. Tapi idada tidak mulai dari 1. Jadi urutannya ternyata sebagai berikut: 150, 151 ..., 300, 1, 2 ... Dan itu akan menyebabkan kesalahan id duplikat pada akhirnya saya kira, jika saya tidak menomori ulang id. Selain itu, pesan berdasarkan idumumnya lebih baik daripada memesan berdasarkan created_at. Dan inilah yang berhasil untuk saya .
x-yuri
Tujuan dari melakukan ini adalah agar Anda dapat terus menggunakan int reguler, bukan bigint untuk kunci utama dalam database yang terus menaikkan kunci sekuensial tetapi terus-menerus menerima data baru. Anda akan segera mencapai batas integer yang ditandatangani, dan jika mempertahankan id yang masih ada tidak penting, proses ini akan membawa Anda kembali ke nomor id yang dapat dikelola.
Ben Wilson
Kegunaan lain untuk ini adalah pengujian. Anda ingin mengatur ulang tabel ke keadaan yang diketahui sebelum memulai setiap pengujian dan membutuhkan id untuk diatur ulang.
Safa Alai

Jawaban:

204

Jika Anda tidak ingin mempertahankan urutan id, Anda bisa

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

Saya ragu ada cara mudah untuk melakukannya dalam urutan pilihan Anda tanpa membuat ulang seluruh tabel.

Michael Krelin - peretas
sumber
4
Bukankah begitu ALTER SEQUENCE seq RESTART WITH 1;?
Lars Haugseth
5
Ini mungkin menyebabkan id duplikat. Untuk mencegah hal ini, pertama-tama Anda dapat mengatur semua ke nilai yang sangat tinggi: UPDATE t SET idcolumn = 1000000 + nextval ('seq'); lalu jalankan script diatas.
tahagh
5
SELECT setval('seq', 1, FALSE)harus melakukan hal yang sama (di sini, argumen ketiga, SALAH, melakukan keajaiban, karena itu menunjukkan bahwa nextvalharus 1 bukan 2)
Vasilen Donchev
@VassilenDontchev, tentu saja.
Michael Krelin - peretas
55

Dengan PostgreSQL 8.4 atau yang lebih baru, tidak perlu WITH 1lagi menentukan . Nilai awal yang direkam oleh CREATE SEQUENCEatau set terakhir oleh ALTER SEQUENCE START WITHakan digunakan (kemungkinan besar ini adalah 1).

Setel ulang urutan:

ALTER SEQUENCE seq RESTART;

Kemudian perbarui kolom ID tabel:

UPDATE foo SET id = DEFAULT;

Sumber: Dokumen PostgreSQL

Oliver
sumber
3
Ini sepertinya jawaban terbaik karena menghindari membuat asumsi tentang nilai awal urutan.
anjing gembala
Jawaban terbaik untuk kasus saya juga. Saya menggabungkan jawaban ini dengan yang ini , yang menjelaskan perintah ALTER SEQUENCE ... jadi saya mengubah 'seq' dengan mytable_id_seq di mana 'mytable' adalah nama tabel saya dan 'id' adalah nama kolom serial saya
Javi
42

Setel ulang urutan:

SELECT setval('sequence_name', 0);

Memperbarui catatan saat ini:

UPDATE foo SET id = DEFAULT;
Frank Heikens
sumber
3
Urutan dapat memiliki nilai minimum lebih besar dari 0. (Dan nilai minimum default yang digunakan oleh jenis serialdan CREATE SEQUENCEadalah 1!)
brk
18

Kedua solusi yang diberikan tidak berhasil untuk saya;

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

setval('seq', 1)memulai penomoran dengan 2, dan ALTER SEQUENCE seq START 1memulai penomoran dengan 2 juga, karena seq.is_called adalah true (Postgres versi 9.0.4)

Solusi yang berhasil untuk saya adalah:

> ALTER SEQUENCE seq RESTART WITH 1;
> UPDATE foo SET id = DEFAULT;
ivy
sumber
1
Masalah yang sama disini. Solusi Anda juga berfungsi untuk PostgreSQL 8.3.10.
PeqNP
17

Hanya untuk menyederhanakan dan memperjelas penggunaan ALTER SEQUENCE dan SELECT setval untuk mengatur ulang urutan:

ALTER SEQUENCE sequence_name RESTART WITH 1;

setara dengan

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

Salah satu pernyataan dapat digunakan untuk mengatur ulang urutan dan Anda bisa mendapatkan nilai berikutnya dengan nextval ('sequence_name') seperti yang dinyatakan di sini juga:

nextval('sequence_name')
Ali Raza Bhayani
sumber
Terima kasih Ali. Saya baru saja menyadari bahwa sangat penting untuk mengatur parameter ke-3 itu menjadi false dengan fungsi
setval
14

Cara terbaik untuk menyetel ulang urutan untuk memulai kembali dengan nomor 1 adalah dengan menjalankan yang berikut ini:

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

Jadi, misalnya untuk tabel users akan menjadi:

ALTER SEQUENCE users_id_seq RESTART WITH 1
jahmed31
sumber
6

Untuk mempertahankan urutan baris:

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;
alexkovelsky
sumber
4

FYI: Jika Anda perlu menentukan nilai awal baru antara rentang ID (256 - 10000000 misalnya):

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

Hanya mengatur ulang urutan dan memperbarui semua baris dapat menyebabkan kesalahan id duplikat. Dalam banyak kasus, Anda harus memperbarui semua baris dua kali. Pertama dengan id yang lebih tinggi untuk menghindari duplikat, lalu dengan id yang Anda inginkan.

Harap hindari untuk menambahkan jumlah tetap ke semua id (seperti yang direkomendasikan di komentar lain). Apa yang terjadi jika Anda memiliki lebih banyak baris daripada jumlah tetap ini? Dengan asumsi nilai berikutnya dari urutan lebih tinggi dari semua id dari baris yang ada (Anda hanya ingin mengisi celah), saya akan melakukannya seperti:

UPDATE table SET id = DEFAULT;
ALTER SEQUENCE seq RESTART;
UPDATE table SET id = DEFAULT;
jujur
sumber
1

Dalam kasus saya, saya mencapai ini dengan:

ALTER SEQUENCE table_tabl_id_seq RESTART WITH 6;

Dimana meja saya diberi nama table

Diego Santa Cruz MendezĂș
sumber
Terima kasih telah menyertakan contoh konkret dengan nama tabel Anda. Jawaban lainnya agak terlalu ambigu.
Brylie Christopher Oxley
0

Terinspirasi oleh jawaban lain di sini, saya membuat fungsi SQL untuk melakukan migrasi urutan. Fungsi ini memindahkan urutan kunci primer ke urutan baru yang berdekatan yang dimulai dengan nilai apa pun (> = 1) baik di dalam maupun di luar rentang urutan yang ada.

Saya menjelaskan di sini bagaimana saya menggunakan fungsi ini dalam migrasi dua database dengan skema yang sama tetapi nilai berbeda ke dalam satu database.

Pertama, fungsinya (yang mencetak perintah SQL yang dihasilkan sehingga jelas apa yang sebenarnya terjadi):

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;

Fungsi tersebut migrate_pkey_sequencemengambil argumen berikut:

  1. arg_table: nama tabel (mis. 'example')
  2. arg_column: nama kolom kunci utama (mis 'id' )
  3. arg_sequence: nama urutan (misalnya 'example_id_seq')
  4. arg_next_value: nilai berikutnya untuk kolom setelah migrasi

Itu melakukan operasi berikut:

  1. Pindahkan nilai kunci utama ke rentang bebas. Saya berasumsi bahwa nextval('example_id_seq')berikut max(id)dan urutannya dimulai dengan 1. Ini juga menangani kasus di mana arg_next_value > max(id).
  2. Pindahkan nilai kunci utama ke rentang bersebelahan yang dimulai dengan arg_next_value. Urutan nilai kunci dipertahankan tetapi lubang pada kisaran tidak dipertahankan.
  3. Cetak nilai berikutnya yang akan mengikuti urutan. Ini berguna jika Anda ingin memindahkan kolom dari tabel lain dan menggabungkannya dengan yang satu ini.

Untuk mendemonstrasikan, kami menggunakan urutan dan tabel yang ditentukan sebagai berikut (misalnya menggunakan 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)
  );

Kemudian, kami memasukkan beberapa nilai (mulai, misalnya, pada 3):

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

Terakhir, kami memigrasi example.idnilai untuk memulai dengan 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)

Hasil:

# SELECT * FROM example;
 id
----
  1
  2
  3
(3 rows)
Kulit Sean
sumber
0

Bahkan kolom auto-increment bukan PK (dalam contoh ini disebut seq - aka sequence) Anda dapat mencapainya dengan pemicu:

DROP TABLE JIKA ADA 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();
Yordan Georgiev
sumber
-1

Jika Anda menggunakan pgAdmin3, perluas 'Sequences', klik kanan pada urutan, buka 'Properties,' dan di tab 'Definition' ubah 'Nilai saat ini' ke nilai apa pun yang Anda inginkan. Tidak perlu kueri.

Dinesh Patil
sumber
3
Jawaban Anda tidak menambah nilai jika Anda setidaknya tidak memberi tahu kami alat apa yang Anda gunakan.
11101101b
3
Ini cara termudah, jelas saya pikir dia mengatakan pg admin 3
MvcCmsJon