Come ripristinare la sequenza in postgres e riempire la colonna ID con nuovi dati?


126

Ho una tabella con oltre milioni di righe. Devo reimpostare la sequenza e riassegnare la colonna ID con nuovi valori (1, 2, 3, 4 ... ecc ...). C'è un modo semplice per farlo?


6
La vera domanda: perché diavolo vorresti farlo? Presumibilmente l'ID è la chiave primaria, quindi non c'è alcun vantaggio nel cambiare la chiave primaria. Una chiave primaria è un valore privo di significato (nel tuo caso artificiale). "Rinumerare" non serve a nessuno scopo sensato in un database relazionale.
a_horse_with_no_name

2
Inizialmente avevo l'app in esecuzione in locale, poi ho copiato i dati in produzione. Ma idnon è iniziato da 1. Quindi l'ordinamento è risultato come segue: 150, 151 ..., 300, 1, 2 ... E suppongo che alla fine causerebbe errori di identificazione duplicati, se non avessi rinumerato gli id. Inoltre, ordinare per idè generalmente migliore di ordinare per created_at. Ed ecco cosa ha funzionato per me .
x-yuri

Lo scopo di farlo è che tu possa continuare a usare un int normale invece di bigint per una chiave primaria in un database che continua ad incrementare la chiave sequenziale ma riceve costantemente nuovi dati. Ti imbatterai rapidamente nel limite di numeri interi con segno e, se mantenere l'ID esistente non è importante, questo processo ti riporterà a numeri di ID gestibili.
Ben Wilson

Un altro uso per questo è il test. Si desidera ripristinare una tabella a uno stato noto prima di avviare ogni test e ciò richiede il ripristino degli ID.
Safa Alai

Risposte:


204

Se non vuoi mantenere l'ordine degli ID, puoi farlo

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

Dubito che ci sia un modo semplice per farlo nell'ordine che preferisci senza ricreare l'intera tabella.


4
Non dovrebbe essere quello ALTER SEQUENCE seq RESTART WITH 1;?
Lars Haugseth

5
Ciò potrebbe causare ID duplicati. Per evitare ciò, è possibile prima impostare tutto su valori molto alti: UPDATE t SET idcolumn = 1000000 + nextval ('seq'); quindi eseguire lo script precedente.
tahagh

5
SELECT setval('seq', 1, FALSE)dovrebbe fare lo stesso (qui, il terzo argomento, FALSO, fa la magia, poiché mostra che nextvaldeve essere 1 invece di 2)
Vasilen Donchev

@VassilenDontchev, assolutamente.
Michael Krelin - hacker

55

Con PostgreSQL 8.4 o successivo non è più necessario specificare il file WITH 1. Verrà utilizzato il valore iniziale registrato da CREATE SEQUENCEo l'ultimo impostato da ALTER SEQUENCE START WITH(molto probabilmente sarà 1).

Ripristina la sequenza:

ALTER SEQUENCE seq RESTART;

Quindi aggiorna la colonna ID della tabella:

UPDATE foo SET id = DEFAULT;

Fonte: Documenti PostgreSQL


3
Questa sembra la risposta migliore in quanto evita di fare supposizioni sul valore iniziale della sequenza.
cane da pastore

La migliore risposta anche per il mio caso. Combino questa risposta con questa , che spiega il comando ALTER SEQUENCE ... quindi ho cambiato 'seq' con mytable_id_seq dove 'mytable' è il nome della mia tabella e 'id' è il nome della mia colonna seriale
Javi

42

Ripristina la sequenza:

SELECT setval('sequence_name', 0);

Aggiornamento dei record correnti:

UPDATE foo SET id = DEFAULT;

3
La sequenza potrebbe avere un valore minimo maggiore di 0. (E il valore minimo predefinito utilizzato dal tipo serialed CREATE SEQUENCEè 1!)
brk

18

Entrambe le soluzioni fornite non hanno funzionato per me;

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

setval('seq', 1) inizia la numerazione con 2 e ALTER SEQUENCE seq START 1 inizia anche la numerazione con 2, perché seq.is_called è vero (Postgres versione 9.0.4)

La soluzione che ha funzionato per me è:

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

1
Stesso problema qui. La tua soluzione funziona anche per PostgreSQL 8.3.10.
PeqNP

17

Solo per semplificare e chiarire il corretto utilizzo di ALTER SEQUENCE e SELECT setval per il ripristino della sequenza:

ALTER SEQUENCE sequence_name RESTART WITH 1;

è equivalente a

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

Entrambe le istruzioni possono essere utilizzate per reimpostare la sequenza e puoi ottenere il valore successivo da nextval ('sequence_name') come indicato anche qui :

nextval('sequence_name')

Grazie Ali. Ho appena notato che è fondamentale impostare il terzo parametro su false con la funzione
setval

14

Il modo migliore per ripristinare una sequenza per ricominciare con il numero 1 è eseguire quanto segue:

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

Quindi, ad esempio per la tabella degli utenti, sarebbe:

ALTER SEQUENCE users_id_seq RESTART WITH 1

6

Per mantenere l'ordine delle righe:

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: se è necessario specificare un nuovo valore iniziale tra un intervallo di ID (256 - 10000000 ad esempio):

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

2

Il semplice ripristino della sequenza e l'aggiornamento di tutte le righe possono causare errori di identificazione duplicati. In molti casi è necessario aggiornare tutte le righe due volte. Prima con ID più alti per evitare i duplicati, poi con gli ID che desideri effettivamente.

Evita di aggiungere un importo fisso a tutti gli ID (come consigliato in altri commenti). Cosa succede se hai più righe di questo importo fisso? Supponendo che il valore successivo della sequenza sia maggiore di tutti gli ID delle righe esistenti (vuoi solo riempire gli spazi vuoti), lo farei come:

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

1

Nel mio caso, ho ottenuto questo risultato con:

ALTER SEQUENCE table_tabl_id_seq RESTART WITH 6;

Dove la mia tabella è denominata tabella


Grazie per aver incluso un esempio concreto con il nome del tuo tavolo. Le altre risposte erano un po 'troppo ambigue.
Brylie Christopher Oxley

0

Ispirato dalle altre risposte qui, ho creato una funzione SQL per eseguire una migrazione in sequenza. La funzione sposta una sequenza di tasti primari in una nuova sequenza contigua che inizia con qualsiasi valore (> = 1) all'interno o all'esterno dell'intervallo di sequenza esistente.

Spiego qui come ho utilizzato questa funzione in una migrazione di due database con lo stesso schema ma valori diversi in un database.

Innanzitutto, la funzione (che stampa i comandi SQL generati in modo che sia chiaro cosa sta effettivamente accadendo):

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;

La funzione migrate_pkey_sequenceaccetta i seguenti argomenti:

  1. arg_table: nome tabella (ad es. 'example')
  2. arg_column: nome della colonna della chiave primaria (ad es 'id' )
  3. arg_sequence: nome della sequenza (es. 'example_id_seq')
  4. arg_next_value: valore successivo per la colonna dopo la migrazione

Esegue le seguenti operazioni:

  1. Spostare i valori della chiave primaria in un intervallo libero. Presumo che nextval('example_id_seq')segua max(id)e che la sequenza inizi con 1. Questo gestisce anche il caso in cui arg_next_value > max(id).
  2. Spostare i valori della chiave primaria nell'intervallo contiguo a partire da arg_next_value. L'ordine dei valori chiave viene mantenuto ma i buchi nell'intervallo non vengono preservati.
  3. Stampa il valore successivo che seguirà nella sequenza. Questo è utile se vuoi migrare le colonne di un'altra tabella e unirle a questa.

Per dimostrare, usiamo una sequenza e una tabella definite come segue (ad esempio utilizzando 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)
  );

Quindi, inseriamo alcuni valori (iniziando, ad esempio, da 3):

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

Infine, migriamo i example.idvalori per iniziare con 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)

Il risultato:

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

0

Anche la colonna di incremento automatico non è PK (in questo esempio si chiama seq - aka sequenza) potresti ottenerla con un trigger:

DROP TABLE SE ESISTE 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

Se stai usando pgAdmin3, espandi "Sequenze", fai clic con il pulsante destro del mouse su una sequenza, vai a "Proprietà" e nella scheda "Definizione" cambia "Valore corrente" con il valore che desideri. Non è necessaria una query.


3
La tua risposta non aggiunge valore se non ci dici almeno quale strumento stai utilizzando.
11101101b

3
Questo è il modo più semplice, ovviamente penso che stia dicendo pg admin 3
MvcCmsJon
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.