web-dev-qa-db-ja.com

postgresのシーケンスをリセットし、id列に新しいデータを入力する方法は?

100万行を超えるテーブルがあります。シーケンスをリセットし、新しい値(1、2、3、4 ...など)でid列を再割り当てする必要があります。それを行う簡単な方法はありますか?

96
sennin

IDの順序を保持したくない場合は、次のことができます。

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

テーブル全体を再作成せずに、選択した順序でそれを行う簡単な方法があるとは思いません。

シーケンスをリセットします。

SELECT setval('sequence_name', 0);

現在のレコードの更新:

UPDATE foo SET id = DEFAULT;
37
Frank Heikens

PostgreSQL 8.4以降では、WITH 1を指定する必要がなくなりました。 CREATE SEQUENCEによって記録された開始値またはALTER SEQUENCE START WITHによって最後に設定された開始値が使用されます(ほとんどの場合、これは1になります)。

シーケンスをリセットします。

ALTER SEQUENCE seq RESTART;

次に、テーブルのID列を更新します。

UPDATE foo SET id = DEFAULT;

ソース: PostgreSQL Docs

32
Oliver

提供されたソリューションはどちらも私にとってはうまくいきませんでした。

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

setval('seq', 1)は2から番号付けを開始し、ALTER SEQUENCE seq START 1は2から番号付けを開始します。これは、seq.is_calledが真であるためです(Postgresバージョン9.0.4)

私のために働いた解決策は次のとおりです。

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

ALTER SEQUENCEおよびSELECT setvalの適切な使用法を単純化および明確化するためだけにシーケンスをリセットするため:

ALTER SEQUENCE sequence_name RESTART WITH 1;

に等しい

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

いずれかのステートメントを使用してシーケンスをリセットでき、次のようにnextval( 'sequence_name')で次の値を取得できます here また:

nextval('sequence_name')
12

シーケンスをリセットして番号1から開始する最良の方法は、次を実行することです。

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

したがって、たとえばユーザーテーブルの場合は次のようになります。

ALTER SEQUENCE users_id_seq RESTART WITH 1
8
jahmed31

参考:IDの範囲(たとえば256〜10000000)の間に新しい開始値を指定する必要がある場合:

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

行の順序を保持するには:

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
alexkovelsky

シーケンスをリセットしてすべての行を更新するだけで、IDの重複エラーが発生する場合があります。多くの場合、すべての行を2回更新する必要があります。最初に重複を避けるために高いIDを使用し、次に実際に必要なIDを使用します。

(他のコメントで推奨されているように)すべてのIDに固定金額を追加することは避けてください。この固定量よりも多くの行がある場合はどうなりますか?シーケンスの次の値が既存の行のすべてのID(ギャップを埋めるだけ)よりも高いと仮定すると、次のようになります。

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

私の場合、次の方法でこれを達成しました。

ALTER SEQUENCE table_tabl_id_seq RESTART WITH 6;

私のテーブルの名前はtableです

自動インクリメント列でもPKではありません(この例ではseq-別名sequenceと呼ばれます)。トリガーでそれを実現できます。

存在する場合はドロップテーブル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

ここの他の回答に触発されて、シーケンス移行を行うためのSQL関数を作成しました。この関数は、主キーシーケンスを、既存のシーケンス範囲の内側または外側のいずれかの値(> = 1)で始まる新しい連続したシーケンスに移動します。

こちら 同じスキーマで異なる値を持つ2つのデータベースを1つのデータベースに移行する際にこの関数をどのように使用したかを説明します。

最初に、関数(生成されたSQLコマンドを出力し、実際に何が起こっているのかを明確にします):

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;

関数migrate_pkey_sequenceは次の引数を取ります。

  1. arg_table:テーブル名(例:'example'
  2. arg_column:主キー列名(例:'id'
  3. arg_sequence:シーケンス名(例:'example_id_seq'
  4. arg_next_value:移行後の列の次の値

次の操作を実行します。

  1. 主キー値を自由な範囲に移動します。 nextval('example_id_seq')max(id)に続き、シーケンスが1で始まると仮定します。これは、arg_next_value > max(id)の場合も処理します。
  2. arg_next_valueで始まる連続した範囲に主キー値を移動します。キー値の順序は保持されますが、範囲内の穴は保持されません。
  3. シーケンスに続く次の値を出力します。これは、別のテーブルの列を移行して、このテーブルとマージする場合に便利です。

実証するために、次のように定義されたシーケンスとテーブルを使用します(例: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)
  );

次に、いくつかの値を挿入します(たとえば、3から始まります)。

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

最後に、example.id値を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)

結果:

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