Postgres'te sıra nasıl sıfırlanır ve kimlik sütunu yeni verilerle nasıl doldurulur?


126

Milyondan fazla sıralı bir masam var. Sırayı sıfırlamam ve id sütununu yeni değerlerle yeniden atamam gerekiyor (1, 2, 3, 4 ... vb.). Bunu yapmanın kolay bir yolu var mı?


6
Gerçek soru: neden bunu yapmak istersiniz? Muhtemelen kimlik birincil anahtardır, bu nedenle birincil anahtarı değiştirmenin hiçbir faydası yoktur. Birincil anahtar, anlamsız (sizin durumunuzda yapay) bir değerdir. "Yeniden numaralandırmak", ilişkisel bir veritabanında herhangi bir mantıklı amaca hizmet etmez.
a_horse_with_no_name

2
Başlangıçta uygulamayı yerel olarak çalıştırdım, ardından verileri üretime kopyaladım. Ama id1'den başlamadı. Yani sıralama şu şekilde oldu: 150, 151 ..., 300, 1, 2 ... Ve sanırım, yeniden numaralandırmasaydım, sonunda yinelenen kimlik hatalarına neden olacaktı. kimlikler. Ek olarak, sıralama idölçütü genellikle sıralama yerine göre daha iyidir created_at. Ve işte benim için işe yarayan şey .
x-yuri

Bunu yapmanın amacı, sıralı anahtarı artırmaya devam eden ancak sürekli olarak yeni veriler alan bir veritabanında birincil anahtar için bigint yerine normal int kullanmaya devam edebilmenizdir. Hızlı bir şekilde imzalı tamsayı sınırına koşarsınız ve mevcut kimliği korumak önemli değilse, bu işlem sizi yönetilebilir kimlik numaralarına geri getirecektir.
Ben Wilson

Bunun başka bir kullanımı test etmektir. Her testi başlatmadan önce tabloyu bilinen bir duruma sıfırlamak istiyorsunuz ve bu, kimliklerin sıfırlanmasını gerektirir.
Safa Alai

Yanıtlar:


204

Kimliklerin sırasını korumak istemiyorsanız, şunları yapabilirsiniz:

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

Bunu tüm tabloyu yeniden oluşturmadan seçtiğiniz sırayla yapmanın kolay bir yolu olduğundan şüpheliyim.


4
Olması gerekmiyor ALTER SEQUENCE seq RESTART WITH 1;mu?
Lars Haugseth

5
Bu, yinelenen kimliklere neden olabilir. Bunu önlemek için, önce hepsini çok yüksek değerlere ayarlayabilirsiniz: UPDATE t SET idcolumn = 1000000 + nextval ('seq'); ardından yukarıdaki komut dosyasını çalıştırın.
tahagh

5
SELECT setval('seq', 1, FALSE)aynısını yapmalı (burada, üçüncü argüman, YANLIŞ, sihir yapar, çünkü nextval2 yerine 1 olması gerektiğini gösterir )
Vasilen Donchev

@VassilenDontchev, kesinlikle.
Michael Krelin - hacker

55

PostgreSQL 8.4 veya daha yenisi ile WITH 1artık belirtmeye gerek yok. Tarafından kaydedilen CREATE SEQUENCEveya son ayarlanan başlangıç ​​değeri ALTER SEQUENCE START WITHkullanılacaktır (büyük olasılıkla bu 1 olacaktır).

Sırayı sıfırlayın:

ALTER SEQUENCE seq RESTART;

Ardından tablonun kimlik sütununu güncelleyin:

UPDATE foo SET id = DEFAULT;

Kaynak: PostgreSQL Docs


3
Bu, dizinin başlangıç ​​değeri hakkında varsayımlarda bulunmaktan kaçındığı için en iyi cevap gibi görünüyor.
çoban köpeği

Benim durumum için de en iyi cevap. Ben bu cevabı birleştirmek bu bir ben tarafından 'seq' değişti yani ... ALTER SIRASI komutunu açıklar mytable_id_seq 'Tablom' benim tablo adı ve 'id' benim seri sütunun adıdır
Javi

42

Sırayı sıfırlayın:

SELECT setval('sequence_name', 0);

Mevcut kayıtları güncelleme:

UPDATE foo SET id = DEFAULT;

3
Sıra 0'dan minimum değerine sahip (tip kullandığı Ve varsayılan minimum değeri olabilir serialve CREATE SEQUENCE1'dir!)
brk

18

Sağlanan her iki çözüm de benim için işe yaramadı;

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

setval('seq', 1)numaralandırmaya 2 ile başlar ve numaralandırmaya 2 ile ALTER SEQUENCE seq START 1başlar, çünkü seq.is_called true (Postgres sürüm 9.0.4)

Benim için işe yarayan çözüm şudur:

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

1
Burada da aynı problem. Çözümünüz PostgreSQL 8.3.10 için de çalışıyor.
PeqNP

17

Sıralamayı sıfırlamak için ALTER SEQUENCE ve SELECT ayar değerinin doğru kullanımını basitleştirmek ve açıklığa kavuşturmak için :

ALTER SEQUENCE sequence_name RESTART WITH 1;

eşdeğerdir

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

İfadelerden herhangi biri sekansı sıfırlamak için kullanılabilir ve bir sonraki değeri, burada da belirtildiği gibi nextval ('sıra_adı') ile elde edebilirsiniz :

nextval('sequence_name')

Teşekkürler Ali.
Setval

14

1 numaradan başlamak için bir diziyi sıfırlamanın en iyi yolu, aşağıdakileri yürütmektir:

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

Dolayısıyla, örneğin kullanıcılar tablosu için şöyle olacaktır:

ALTER SEQUENCE users_id_seq RESTART WITH 1

6

Satırların sırasını korumak için:

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

Bilginize: Bir dizi kimlik arasında yeni bir başlangıç ​​değeri belirtmeniz gerekiyorsa (örneğin 256 - 10000000):

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

2

Sadece diziyi sıfırlamak ve tüm satırları güncellemek, yinelenen kimlik hatalarına neden olabilir. Çoğu durumda, tüm satırları iki kez güncellemeniz gerekir. Öncelikle kopyaları önlemek için daha yüksek kimliklerle, ardından gerçekten istediğiniz kimliklerle.

Lütfen tüm kimliklere sabit bir miktar eklemekten kaçının (diğer yorumlarda önerildiği gibi). Bu sabit miktardan daha fazla satırınız varsa ne olur? Sıranın bir sonraki değerinin mevcut satırların tüm kimliklerinden daha yüksek olduğunu varsayarsak (sadece boşlukları doldurmak istersiniz), şunu yapacağım:

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

1

Benim durumumda bunu şu şekilde başardım:

ALTER SEQUENCE table_tabl_id_seq RESTART WITH 6;

Masamın adı tablo nerede


Tablo adınızla somut bir örnek eklediğiniz için teşekkür ederiz. Diğer cevaplar biraz fazla belirsizdi.
Brylie Christopher Oxley

0

Buradaki diğer cevaplardan esinlenerek, bir dizi geçişi yapmak için bir SQL işlevi oluşturdum. İşlev, birincil tuş dizisini, mevcut sıra aralığı içinde veya dışında herhangi bir değerle (> = 1) başlayan yeni bir bitişik diziye taşır.

Burada , aynı şemaya sahip ancak farklı değerlere sahip iki veritabanının bir veritabanına geçişinde bu işlevi nasıl kullandığımı açıklıyorum .

İlk olarak, işlev (oluşturulan SQL komutlarını yazdırır, böylece gerçekte ne olduğu nettir):

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;

İşlev migrate_pkey_sequenceaşağıdaki bağımsız değişkenleri alır:

  1. arg_table: tablo adı (örneğin 'example')
  2. arg_column: birincil anahtar sütun adı (örneğin 'id')
  3. arg_sequence: sıra adı (örneğin 'example_id_seq')
  4. arg_next_value: geçişten sonraki sütun için sonraki değer

Aşağıdaki işlemleri gerçekleştirir:

  1. Birincil anahtar değerlerini boş bir aralığa taşıyın. Bunu nextval('example_id_seq')takip max(id)ettiğini ve sıranın 1 ile başladığını varsayıyorum . Bu aynı zamanda nerede olduğunu da ele alıyor arg_next_value > max(id).
  2. Birincil anahtar değerlerini ile başlayan bitişik aralığa taşıyın arg_next_value. Anahtar değerlerin sırası korunur, ancak aralıktaki delikler korunmaz.
  3. Sırayla izlenecek bir sonraki değeri yazdırın. Başka bir tablonun sütunlarını taşımak ve bununla birleştirmek istiyorsanız bu yararlıdır.

Göstermek için, aşağıdaki gibi tanımlanan bir dizi ve tablo kullanıyoruz (örneğin kullanarak 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)
  );

Ardından, bazı değerler ekleriz (örneğin, 3'ten başlayarak):

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

Son olarak, example.iddeğerleri 1 ile başlayacak şekilde taşıyoruz.

# 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)

Sonuç:

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

0

Otomatik artış sütunu bile PK değildir (bu örnekte buna seq - aka dizi denir) bunu bir tetikleyici ile elde edebilirsiniz:

TABLO VARSA 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 kullanıyorsanız, 'Sıralar'ı genişletin, bir diziye sağ tıklayın,' Özellikler'e gidin ve 'Tanım' sekmesinde 'Geçerli değer'i istediğiniz değere değiştirin. Sorgulamaya gerek yoktur.


3
En azından bize hangi aracı kullandığınızı söylemezseniz, cevabınız hiçbir değer katmaz.
11101101b

3
Bu en kolay yol, açıkçası sanırım PG admin 3
MvcCmsJ
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.