Wie setze ich die Sequenz in postgres zurück und fülle die ID-Spalte mit neuen Daten?


126

Ich habe eine Tabelle mit über Millionen Zeilen. Ich muss die Sequenz zurücksetzen und die ID-Spalte mit neuen Werten (1, 2, 3, 4 ... usw.) neu zuweisen. Ist das einfach?


6
Die eigentliche Frage: Warum um alles in der Welt möchten Sie das tun? Vermutlich ist die ID der Primärschlüssel, daher hat das Ändern des Primärschlüssels keinerlei Vorteile. Ein Primärschlüssel ist ein bedeutungsloser (in Ihrem Fall künstlicher) Wert. "Umnummerieren" dient in einer relationalen Datenbank keinem sinnvollen Zweck.
a_horse_with_no_name

2
Anfangs lief die App lokal, dann kopierte ich die Daten in die Produktion. Aber ids begann nicht bei 1. Die Reihenfolge stellte sich also wie folgt heraus: 150, 151 ..., 300, 1, 2 ... Und es würde schließlich zu doppelten ID-Fehlern führen, wenn ich nicht neu nummeriert hätte die IDs. Außerdem ist die Bestellung nach idim Allgemeinen besser als die Bestellung nach created_at. Und hier ist, was für mich funktioniert hat .
X-Yuri

Der Grund dafür ist, dass Sie weiterhin ein reguläres int anstelle von bigint für einen Primärschlüssel in einer Datenbank verwenden können, die den sequentiellen Schlüssel weiter erhöht, aber ständig neue Daten empfängt. Sie werden schnell auf das Limit für vorzeichenbehaftete Ganzzahlen stoßen. Wenn das Beibehalten der vorhandenen ID nicht wichtig ist, werden Sie durch diesen Vorgang zu verwaltbaren ID-Nummern zurückgeführt.
Ben Wilson

Eine andere Verwendung hierfür ist das Testen. Sie möchten eine Tabelle vor dem Starten jedes Tests auf einen bekannten Status zurücksetzen. Dazu müssen die IDs zurückgesetzt werden.
Safa Alai

Antworten:


203

Wenn Sie die Reihenfolge der IDs nicht beibehalten möchten, können Sie dies tun

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

Ich bezweifle, dass es einen einfachen Weg gibt, dies in der Reihenfolge Ihrer Wahl zu tun, ohne die gesamte Tabelle neu zu erstellen.


4
Sollte das nicht sein ALTER SEQUENCE seq RESTART WITH 1;?
Lars Haugseth

5
Dies kann zu doppelten IDs führen. Um dies zu verhindern, können Sie zunächst alle auf sehr hohe Werte setzen: UPDATE t SET idcolumn = 1000000 + nextval ('seq'); Führen Sie dann das obige Skript aus.
Tahagh

5
SELECT setval('seq', 1, FALSE)sollte das gleiche tun (hier macht das dritte Argument, FALSE, die Magie, da es zeigt, nextvaldass 1 statt 2 sein muss)
Vasilen Donchev

@VassilenDontchev, absolut.
Michael Krelin - Hacker

55

Mit PostgreSQL 8.4 oder neuer muss das WITH 1nicht mehr angegeben werden. Der Startwert, der von aufgezeichnet CREATE SEQUENCEoder zuletzt festgelegt wurde, ALTER SEQUENCE START WITHwird verwendet (höchstwahrscheinlich ist dies 1).

Setzen Sie die Sequenz zurück:

ALTER SEQUENCE seq RESTART;

Aktualisieren Sie dann die ID-Spalte der Tabelle:

UPDATE foo SET id = DEFAULT;

Quelle: PostgreSQL Docs


3
Dies scheint die beste Antwort zu sein, da keine Annahmen über den Startwert der Sequenz getroffen werden müssen.
Schäferhund

Beste Antwort auch für meinen Fall. Ich kombiniere diese Antwort mit dieser , die den Befehl ALTER SEQUENCE erklärt ... also habe ich 'seq' durch mytable_id_seq geändert, wobei 'mytable' mein Tabellenname und 'id' der Name meiner seriellen Spalte ist
Javi

42

Setzen Sie die Sequenz zurück:

SELECT setval('sequence_name', 0);

Aktuelle Datensätze aktualisieren:

UPDATE foo SET id = DEFAULT;

3
Sequenz könnte einen Minimalwert größer als 0 haben. (Und der Standard-Minimalwert, der vom Typ verwendet wird serialund CREATE SEQUENCE1 ist!)
brk

18

Beide bereitgestellten Lösungen funktionierten bei mir nicht.

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

setval('seq', 1)startet die Nummerierung mit 2 und ALTER SEQUENCE seq START 1startet die Nummerierung auch mit 2, da seq.is_called wahr ist (Postgres Version 9.0.4)

Die Lösung, die für mich funktioniert hat, ist:

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

1
Selbes Problem hier. Ihre Lösung funktioniert auch für PostgreSQL 8.3.10.
PeqNP

17

Nur zur Vereinfachung und Verdeutlichung der ordnungsgemäßen Verwendung von ALTER SEQUENCE und SELECT setval zum Zurücksetzen der Sequenz:

ALTER SEQUENCE sequence_name RESTART WITH 1;

ist äquivalent zu

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

Jede der Anweisungen kann verwendet werden, um die Sequenz zurückzusetzen, und Sie können den nächsten Wert durch nextval ('sequence_name') erhalten, wie auch hier angegeben :

nextval('sequence_name')

Danke Ali. Ich habe gerade bemerkt, dass es wichtig ist, diesen dritten Parameter mit der Funktion
setval

13

Der beste Weg, eine Sequenz zurückzusetzen, um mit Nummer 1 zu beginnen, besteht darin, Folgendes auszuführen:

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

So wäre es beispielsweise für die Benutzertabelle:

ALTER SEQUENCE users_id_seq RESTART WITH 1

6

So behalten Sie die Reihenfolge der Zeilen bei:

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

Zu Ihrer Information: Wenn Sie einen neuen Startwert zwischen einer Reihe von IDs angeben müssen (z. B. 256 - 10000000):

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

2

Das Zurücksetzen der Sequenz und das Aktualisieren aller Zeilen kann zu doppelten ID-Fehlern führen. In vielen Fällen müssen Sie alle Zeilen zweimal aktualisieren. Zuerst mit höheren IDs, um die Duplikate zu vermeiden, dann mit den IDs, die Sie tatsächlich wollen.

Bitte vermeiden Sie es, allen IDs einen festen Betrag hinzuzufügen (wie in anderen Kommentaren empfohlen). Was passiert, wenn Sie mehr Zeilen als diesen festen Betrag haben? Angenommen, der nächste Wert der Sequenz ist höher als alle IDs der vorhandenen Zeilen (Sie möchten nur die Lücken füllen), würde ich Folgendes tun:

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

1

In meinem Fall habe ich dies erreicht mit:

ALTER SEQUENCE table_tabl_id_seq RESTART WITH 6;

Wo meine Tabelle Tabelle genannt wird


Vielen Dank, dass Sie Ihrem Tabellennamen ein konkretes Beispiel beigefügt haben. Die anderen Antworten waren etwas zu zweideutig.
Brylie Christopher Oxley

0

Inspiriert von den anderen Antworten hier habe ich eine SQL-Funktion erstellt, um eine Sequenzmigration durchzuführen. Die Funktion verschiebt eine Primärschlüsselfolge in eine neue zusammenhängende Folge, beginnend mit einem beliebigen Wert (> = 1) innerhalb oder außerhalb des vorhandenen Sequenzbereichs.

Ich erkläre hier, wie ich diese Funktion bei einer Migration von zwei Datenbanken mit demselben Schema, aber unterschiedlichen Werten in eine Datenbank verwendet habe.

Zunächst die Funktion (die die generierten SQL-Befehle druckt, damit klar ist, was tatsächlich passiert):

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;

Die Funktion migrate_pkey_sequenceakzeptiert die folgenden Argumente:

  1. arg_table: Tabellenname (zB 'example')
  2. arg_column: Primärschlüsselspalte Namen (z 'id')
  3. arg_sequence: Sequenzname (zB 'example_id_seq')
  4. arg_next_value: nächster Wert für die Spalte nach der Migration

Es führt die folgenden Operationen aus:

  1. Verschieben Sie die Primärschlüsselwerte in einen freien Bereich. Ich gehe davon aus, dass das nextval('example_id_seq')Folgende folgt max(id)und dass die Sequenz mit 1 beginnt. Dies behandelt auch den Fall, in dem arg_next_value > max(id).
  2. Verschieben Sie die Primärschlüsselwerte beginnend mit in den zusammenhängenden Bereich arg_next_value. Die Reihenfolge der Schlüsselwerte bleibt erhalten, Löcher im Bereich bleiben jedoch nicht erhalten.
  3. Drucken Sie den nächsten Wert, der in der Sequenz folgen würde. Dies ist nützlich, wenn Sie die Spalten einer anderen Tabelle migrieren und mit dieser zusammenführen möchten.

Zur Demonstration verwenden wir eine Sequenz und Tabelle, die wie folgt definiert sind (z. B. using 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)
  );

Dann fügen wir einige Werte ein (beginnend zum Beispiel bei 3):

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

Schließlich migrieren wir die example.idWerte, um mit 1 zu beginnen.

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

Das Ergebnis:

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

0

Selbst wenn die Auto-Inkrement-Spalte keine PK ist (in diesem Beispiel heißt sie seq - auch bekannt als Sequenz), können Sie dies mit einem Trigger erreichen:

DROP TABLE IF EXISTS 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

Wenn Sie pgAdmin3 verwenden, erweitern Sie 'Sequenzen', klicken Sie mit der rechten Maustaste auf eine Sequenz, gehen Sie zu 'Eigenschaften' und ändern Sie auf der Registerkarte 'Definition' 'Aktueller Wert' in den gewünschten Wert. Es ist keine Abfrage erforderlich.


3
Ihre Antwort bringt keinen Wert, wenn Sie uns nicht mindestens mitteilen, welches Tool Sie verwenden.
11101101b

3
Dies ist der einfachste Weg, offensichtlich denke ich, dass er pg admin 3
MvcCmsJon
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.