it-swarm.com.de

Wie setzt man die Sequenz in Postgres zurück und füllt die ID-Spalte mit neuen Daten?

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

77
sennin

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 eine einfache Möglichkeit gibt, dies in der Reihenfolge Ihrer Wahl zu tun, ohne den gesamten Tisch neu erstellen zu müssen.

Setzen Sie die Sequenz zurück:

SELECT setval('sequence_name', 0);

Aktuelle Datensätze aktualisieren:

UPDATE foo SET id = DEFAULT;
34
Frank Heikens

Bei PostgreSQL 8.4 oder neuer muss der WITH 1 nicht mehr angegeben werden. Der Startwert, der mit CREATE SEQUENCE oder dem zuletzt von ALTER SEQUENCE START WITH festgelegten Wert aufgezeichnet wurde, wird verwendet (höchstwahrscheinlich 1).

Setzen Sie die Sequenz zurück:

ALTER SEQUENCE seq RESTART;

Aktualisieren Sie anschließend die ID-Spalte der Tabelle:

UPDATE foo SET id = DEFAULT;

Quelle: PostgreSQL-Dokumente

29
Oliver

Beide zur Verfügung gestellten Lösungen haben bei mir nicht funktioniert;

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

setval('seq', 1) beginnt die Nummerierung mit 2 und ALTER SEQUENCE seq START 1 beginnt 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;
15
ivy

Nur zur Vereinfachung und Verdeutlichung der korrekten 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 von nextval ('sequence_name') wie folgt erhalten: here also:

nextval('sequence_name')
11

Um eine Sequenz zurückzusetzen und mit Nummer 1 zu beginnen, führen Sie die folgenden Schritte aus:

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

Zum Beispiel für die Benutzertabelle wäre dies:

ALTER SEQUENCE users_id_seq RESTART WITH 1
7
jahmed31

Zu Ihrer Information: Wenn Sie einen neuen Startwert zwischen einem ID-Bereich 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
       ); 
4
Stone

Reihenfolge der Zeilen beibehalten:

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;
3
alexkovelsky

Wenn Sie einfach die Reihenfolge zurücksetzen und alle Zeilen aktualisieren, kann dies 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 möchten. 

Bitte vermeiden Sie, einen festen Betrag zu allen IDs 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
Frank

In meinem Fall habe ich dies erreicht mit:

ALTER SEQUENCE table_tabl_id_seq RESTART WITH 6;

Wo mein Tisch heißt Tisch

Selbst die Auto-Increment-Spalte ist nicht PK (in diesem Beispiel heißt sie seq - aka sequence). Dies könnte man mit einem Trigger erreichen: 

DROP TABLE WENN 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();
0
Yordan Georgiev

Inspiriert von den anderen Antworten hier, habe ich eine SQL-Funktion für eine Sequenzmigration erstellt. Die Funktion verschiebt eine Primärschlüsselsequenz in eine neue zusammenhängende Sequenz, 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.

Erstens, die Funktion (die die generierten SQL-Befehle so ausgibt, dass sie Klar macht, 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_sequence akzeptiert die folgenden Argumente:

  1. arg_table: Tabellenname (z. B. 'example')
  2. arg_column: Name der Primärschlüsselspalte (z. B. 'id')
  3. arg_sequence: Sequenzname (z. B. '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 nextval('example_id_seq') auf max(id) folgt 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 in den zusammenhängenden Bereich, beginnend mit arg_next_value. Die Reihenfolge der Schlüsselwerte wird beibehalten, aber Löcher im Bereich Werden nicht beibehalten.
  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 Veranschaulichung verwenden wir eine wie folgt definierte Sequenz und Tabelle (z. B. mit 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 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.id-Werte, 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
Sean Leather