postgresの主キーシーケンスが同期しなくなったときにリセットする方法は?


523

主キーシーケンスがテーブルの行と同期していないという問題に遭遇しました。

つまり、新しい行を挿入すると、シリアルデータ型に含まれるシーケンスが既に存在する番号を返すため、重複キーエラーが発生します。

インポート/復元がシーケンスを適切に維持していないことが原因のようです。


私は興味があります..復元を行う前にデータベースを削除していますか?私はこの出来事のかすかな思い出を持っていますが、私は間違っている可能性があります:P
アーサートーマス

25
PostgreSQL wikiには、シーケンスの修正に関するページがあります。
Brad Koch

14
グーグルらしさを助けるために、ここでスローされるエラーメッセージは次のとおりです:「重複するキー値は一意の制約に違反します...」
スーパー

4
これは、Djangoのsqlsequenceresetが行う方法です:SELECT setval(pg_get_serial_sequence( "<table_name>"、 'id')、coalesce(max( "id")、1)、max( "id")IS NOT null)FROM "< table_name> ";
ユーザーの

pg_get_serioal_sequence関数が機能するためには、<テーブル名>の最初のインスタンスを一重引用符で囲む必要があります。 、max( "id")IS null)FROM "<table_name>"
nclu

回答:


715
-- Login to psql and run the following

-- What is the result?
SELECT MAX(id) FROM your_table;

-- Then run...
-- This should be higher than the last result.
SELECT nextval('your_table_id_seq');

-- If it's not higher... run this set the sequence last to your highest id. 
-- (wise to run a quick pg_dump first...)

BEGIN;
-- protect against concurrent inserts while you update the counter
LOCK TABLE your_table IN EXCLUSIVE MODE;
-- Update the sequence
SELECT setval('your_table_id_seq', COALESCE((SELECT MAX(id)+1 FROM your_table), 1), false);
COMMIT;

ソース-Rubyフォーラム


12
いずれにせよ、MAX(id)に1を追加すると、IDに1つの数値のギャップが残ります。これは、setvalが設定するのはシーケンスの最後の値であり、次の値ではないためです。
mikl 2009

6
テーブルに行がない場合、この例は機能しません。したがって、以下のSQLの方が安全です。SELECT setval( 'your_table_id_seq'、coalesce((select max(id)+1 from your_table)、1)、true);
Valery Viktorovsky、2012

10
@Valery:しかし、あなたが必要とする以上の@mikl 2つのコメントで言及避けるギャップに順にSELECT setval('your_table_id_seq', coalesce((select max(id)+1 from your_table), 1), false);
アントニーHatchkins

20
すべての問題が解決し、単一のクエリにまとめる:SELECT setval('your_seq',(SELECT GREATEST(MAX(your_id)+1,nextval('your_seq'))-1 FROM your_table))
Frunsi

15
アプリケーションがシーケンスのギャップを考慮している場合、アプリケーションは壊れています。シーケンスのギャップはなど、エラーの後に正常であり、そして予定外のデータベースの停止が原因発生する可能性があり、トランザクションのロールバック
クレイグリンガー

202

pg_get_serial_sequenceシーケンス名に関する誤った仮定を回避するために使用できます。これにより、シーケンスがワンショットでリセットされます。

SELECT pg_catalog.setval(pg_get_serial_sequence('table_name', 'id'), (SELECT MAX(id) FROM table_name)+1);

またはもっと簡潔に:

SELECT pg_catalog.setval(pg_get_serial_sequence('table_name', 'id'), MAX(id)) FROM table_name;

ただし、max(id)がnullであるため、このフォームは空のテーブルを正しく処理できません。また、シーケンスの範囲外であるため、val 0も設定できません。このための1つの回避策は、ALTER SEQUENCE構文に頼ることです。

ALTER SEQUENCE table_name_id_seq RESTART WITH 1;
ALTER SEQUENCE table_name_id_seq RESTART; -- 8.4 or higher

ただしALTER SEQUENCE、シーケンス名とリスタート値を式にすることはできないため、用途は限られています。

最良の万能ソリューションはsetval、3番目のパラメーターとしてfalseを指定して呼び出し、「使用する次の値」を指定できるようにすることです。

SELECT setval(pg_get_serial_sequence('t1', 'id'), coalesce(max(id),0) + 1, false) FROM t1;

これは私のすべてのボックスを刻みます:

  1. 実際のシーケンス名をハードコーディングすることを避けます
  2. 空のテーブルを正しく処理する
  3. 既存のデータを持つテーブルを処理し、シーケンスに穴を残さない

最後に、pg_get_serial_sequenceシーケンスが列によって所有されている場合にのみ機能することに注意してください。これは、インクリメントする列がserialタイプとして定義されている場合に当てはまりますが、シーケンスが手動で追加された場合は、必ずALTER SEQUENCE .. OWNED BY実行されるようにする必要があります。

つまり、serialタイプがテーブルの作成に使用された場合、これはすべて機能するはずです。

CREATE TABLE t1 (
  id serial,
  name varchar(20)
);

SELECT pg_get_serial_sequence('t1', 'id'); -- returns 't1_id_seq'

-- reset the sequence, regardless whether table has rows or not:
SELECT setval(pg_get_serial_sequence('t1', 'id'), coalesce(max(id),0) + 1, false) FROM t1;

しかし、シーケンスが手動で追加された場合:

CREATE TABLE t2 (
  id integer NOT NULL,
  name varchar(20)
);

CREATE SEQUENCE t2_custom_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;

ALTER TABLE t2 ALTER COLUMN id SET DEFAULT nextval('t2_custom_id_seq'::regclass);

ALTER SEQUENCE t2_custom_id_seq OWNED BY t2.id; -- required for pg_get_serial_sequence

SELECT pg_get_serial_sequence('t2', 'id'); -- returns 't2_custom_id_seq'

-- reset the sequence, regardless whether table has rows or not:
SELECT setval(pg_get_serial_sequence('t2', 'id'), coalesce(max(id),0) + 1, false) FROM t1;

11
クエリの「+1」は不要であり、setval()現在の値を設定し、nextval()すでに現在の値+1を返します。
アントニーハッチキンス

1
1つのパラメーター(table_name)を使用するこのメソッドをラップする関数は、以下の私の答えにあります:stackoverflow.com/a/13308052/237105
Antony Hatchkins

@AntonyHatchkins乾杯。ただ、これは最終的にはその優れたIの希望のためにswatted 1バグの別の繰り返しを見た
tardate

99

最短かつ最速の方法:

SELECT setval('tbl_tbl_id_seq', max(tbl_id)) FROM tbl;

tbl_idシーケンス(デフォルトの自動名)から描画されるserialtable の列です。tbltbl_tbl_id_seq

アタッチされたシーケンスの名前がわからない場合(デフォルトの形式である必要はありません)、次を使用しますpg_get_serial_sequence()

SELECT setval(pg_get_serial_sequence('tbl', 'tbl_id'), max(tbl_id)) FROM tbl;

ここには、1つずれたエラーはありません。ドキュメントごと:

2パラメータ形式は、シーケンスのlast_valueフィールドを指定された値に設定し、そのis_calledフィールドをtrueに設定します。これは、nextvalがシーケンスを進めてから値を返すことを意味し ます。

大胆な強調鉱山。

このテーブルが空、実際に1から始まる場合:

SELECT setval(pg_get_serial_sequence('tbl', 'tbl_id')
            , COALESCE(max(tbl_id) + 1, 1)
            , false)
FROM tbl;

0シーケンスの下限がデフォルトで1であるため(カスタマイズされていない限り)、2パラメータ形式を使用して開始することはできません。

並行性

上記のクエリでは、同時シーケンスアクティビティやテーブルへの書き込みに対する防御策はまだありません。それが適切であれば、テーブルを排他モードでロックすることができます。これは、同期をとろうとしている間、同時トランザクションがより高い数を書き込むのを防ぎます。(また、最大数を乱さない無害な書き込みを一時的にブロックします。)

ただし、メインテーブルをロックせずにシーケンス番号を事前にフェッチしている可能性があるクライアントは考慮されていません(これは発生する可能性があります)。それも可能にするために、シーケンスの現在の値のみを増やし、決して減らしません。それは偏執的なように見えるかもしれませんが、それはシーケンスの性質と並行性の問題に対する防御と一致しています。

BEGIN;

LOCK TABLE tbl IN EXCLUSIVE MODE;

SELECT setval('tbl_tbl_id_seq', max(tbl_id))
FROM   tbl
HAVING max(tbl_id) > (SELECT last_value FROM tbl_tbl_id_seq);

COMMIT;

「標準機能の重要な機能のコミュニティライブラリ」はどこにありますか?EXECUTE format()(@EB。のような)この回答の2番目の選択節は、必須関数です!PostgreSQLのこの標準ライブラリの欠如を修正する方法????
Peter Krauss 2017年

オフバイワンがあってもかまいません。シーケンスのギャップは正常です。あなたのアプリが対応できない場合は、あなたのアプリが壊れている、ギャップはまた、トランザクションのロールバック、計画外のサーバーのシャットダウンなどに発生する可能性があるため
クレイグリンガー

1
@Craig:私が対処した(そしてそこにはない)1つずれたエラーは重要です。あなたの考慮の反対方向; 誤解のようです。
Erwin Brandstetter 2017

ああ、理にかなっています。
クレイグリンガー

これは私のために働く
hectk

54

これにより、すべてのシーケンスがパブリックからリセットされ、テーブルまたは列の名前についての仮定がなくなります。バージョン8.4でテスト済み

CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text, columnname text, sequence_name text) RETURNS "pg_catalog"."void" AS 

    $body$  
      DECLARE 
      BEGIN 

      EXECUTE 'SELECT setval( ''' || sequence_name  || ''', ' || '(SELECT MAX(' || columnname || ') FROM ' || tablename || ')' || '+1)';



      END;  

    $body$  LANGUAGE 'plpgsql';


    select table_name || '_' || column_name || '_seq', reset_sequence(table_name, column_name, table_name || '_' || column_name || '_seq') from information_schema.columns where column_default like 'nextval%';

1
+1の便利な機能!シーケンス名はテーブル名と正確に一致しなかったため、のsubstring(column_default, '''(.*)''')代わりに使用しましたtable_name || '_' || column_name || '_seq'。完璧に動作します。
Chris Lercher、2012年

4
これは、単一引用符を含むシーケンス名、または名前に大文字、スペースなどを含むテーブル名で失敗することに注意してください。quote_literalおよびquote_ident機能、または好ましくはformat機能、本当にここに使用されるべきです。
クレイグリンガー2013

2
これを2票以上投票できればいいのですが... 少なくとも私にとっては、Postgres 9.1でもうまく動作します。
ピールマン2014年

1
これは素晴らしい。以前substring(column_default from 'nextval\(''(.+)''::regclass\)')は明示的にシーケンス名を取得していました。魅力のように働いた。
マシューマクドナルド

私はこの解決策を1日以上探していました。テキストを置き換えるために@ChrisLercherによって提案された方法を使用していても、ありがとうございましたsubstring(column_default, '''(.*)''') instead of table_name || '_' || column_name || '_seq'
Pv

43

ALTER SEQUENCE sequence_name RESTART WITH(SELECT max(id)FROM table_name); 動作しません。

@tardateの回答からコピー:

SELECT setval(pg_get_serial_sequence('table_name', 'id'), MAX(id)) FROM table_name;

8
これは8.4の構文エラーです(^(SELECT ...で)。RESTARTWITHは序数値しか受け入れないようです。ただし、これは機能します:SELECT setval(pg_get_serial_sequence( 'table_name'、 'id')、(SELECT MAX( id)FROM table_name)+1);
tardate

1
Murugesのソリューションも9.4では機能しません。なぜこの回答に多くの賛成票が投じられるのか理解できません。ALTER SEQUENCEはサブクエリを許可しません。@tardateによるソリューションは完全に機能します。不正なデータを削除するために回答を編集しました。
Vladislav Rastrusny

ALTER SEQUENCEは私にとって完璧に機能しました。COPYを使用して一部のデータを取り込みましたが、主キーにギャップがあり、INSERTは重複キーの例外をスローしていました。シーケンスを設定することでうまくいきました。9.4
user542319 2015年

22

このコマンドは、postgresqlで自動生成されたキーシーケンス値のみを変更します

ALTER SEQUENCE "your_sequence_name" RESTART WITH 0;

ゼロの代わりに、シーケンスを再開する任意の番号を指定できます。

デフォルトのシーケンス名はになり"TableName_FieldName_seq"ます。たとえば、テーブル名が"MyTable"でフィールド名がの"MyID"場合、シーケンス名はになります"MyTable_MyID_seq"

これは@murugesanponappanの回答と同じですが、彼の解決策には構文エラーがあります。コマンドでサブクエリ(select max()...)を使用することはできませんalter。そのため、固定数値を使用する必要があるか、サブクエリの代わりに変数を使用する必要があります。


これは完璧な解決策ですありがとうございました しかし、私の場合はエラーが発生したため、ALTER SEQUENCE "your_sequence_name" RESTART WITH 1に変更する必要がありました。
Deunz 16

18

すべてのシーケンスをリセットします。各テーブルの主キーが「id」であることを除いて、名前についての仮定はありません。

CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text, columnname text)
RETURNS "pg_catalog"."void" AS
$body$
DECLARE
BEGIN
    EXECUTE 'SELECT setval( pg_get_serial_sequence(''' || tablename || ''', ''' || columnname || '''),
    (SELECT COALESCE(MAX(id)+1,1) FROM ' || tablename || '), false)';
END;
$body$  LANGUAGE 'plpgsql';

select table_name || '_' || column_name || '_seq', reset_sequence(table_name, column_name) from information_schema.columns where column_default like 'nextval%';

私の9.1バージョンで完全に動作しました
Valentin Vasilyev 2013

:テーブルには大文字が含まれている場合は、引用符を追加する必要がありますpg_get_serial_sequence(''"' || tablename || '"''
マヌエルDarveau

これが最高の機能です!次のような形式を使用すると、引用の問題を回避(およびエレガンスを向上)できます EXECUTE format( 'SELECT setval(pg_get_serial_sequence(%L, %L), coalesce(max(id),0) + 1, false) FROM %I;', $1,$2,$1 );
Peter Krauss

13

シーケンス名、列名、テーブル名、またはスキーマ名にスペース、句読点などの変な文字が含まれている場合、これらの関数は危険に満ちています。私はこれを書きました:

CREATE OR REPLACE FUNCTION sequence_max_value(oid) RETURNS bigint
VOLATILE STRICT LANGUAGE plpgsql AS  $$
DECLARE
 tabrelid oid;
 colname name;
 r record;
 newmax bigint;
BEGIN
 FOR tabrelid, colname IN SELECT attrelid, attname
               FROM pg_attribute
              WHERE (attrelid, attnum) IN (
                      SELECT adrelid::regclass,adnum
                        FROM pg_attrdef
                       WHERE oid IN (SELECT objid
                                       FROM pg_depend
                                      WHERE refobjid = $1
                                            AND classid = 'pg_attrdef'::regclass
                                    )
          ) LOOP
      FOR r IN EXECUTE 'SELECT max(' || quote_ident(colname) || ') FROM ' || tabrelid::regclass LOOP
          IF newmax IS NULL OR r.max > newmax THEN
              newmax := r.max;
          END IF;
      END LOOP;
  END LOOP;
  RETURN newmax;
END; $$ ;

OIDを渡すことにより、単一のシーケンスに対してそれを呼び出すことができ、デフォルトでシーケンスを持つすべてのテーブルで使用される最大数を返します。または、次のようなクエリで実行して、データベース内のすべてのシーケンスをリセットできます。

 select relname, setval(oid, sequence_max_value(oid))
   from pg_class
  where relkind = 'S';

別のqualを使用すると、特定のスキーマのシーケンスのみをリセットできます。たとえば、「パブリック」スキーマのシーケンスを調整する場合は、次のようにします。

select relname, setval(pg_class.oid, sequence_max_value(pg_class.oid))
  from pg_class, pg_namespace
 where pg_class.relnamespace = pg_namespace.oid and
       nspname = 'public' and
       relkind = 'S';

setval()の仕組みにより、結果に1を追加する必要がないことに注意してください。

最後に、一部のデータベースには、システムカタログにそれらの完全な情報を持たせない方法でシーケンスにリンクするデフォルトがあるように見えることを警告する必要があります。これは、psqlの\ dに次のようなものがある場合に発生します。

alvherre=# \d baz
                     Tabla «public.baz»
 Columna |  Tipo   |                 Modificadores                  
---------+---------+------------------------------------------------
 a       | integer | default nextval(('foo_a_seq'::text)::regclass)

このデフォルト句のnextval()呼び出しには、:: regclassキャストに加えて:: textキャストがあることに注意してください。私が考えて、これは古いPostgreSQLのバージョンからpg_dump'edされたデータベースによるものです。何が起こるかは、上記の関数sequence_max_value()がそのようなテーブルを無視することです。この問題を修正するには、DEFAULT句を再定義して、キャストせずにシーケンスを直接参照します。

alvherre=# alter table baz alter a set default nextval('foo_a_seq');
ALTER TABLE

次に、psqlはそれを正しく表示します。

alvherre=# \d baz
                     Tabla «public.baz»
 Columna |  Tipo   |             Modificadores              
---------+---------+----------------------------------------
 a       | integer | default nextval('foo_a_seq'::regclass)

これを修正するとすぐに、関数はこのテーブルだけでなく、同じシーケンスを使用する他のすべてのテーブルでも正しく機能します。


これは素晴らしい感謝です!次のように、割り当て(関数コードの21行目)でキャストを追加する必要があることに注意しnewmax := r.max::bigint;てください。
Tommy Bravo

これも変更する必要がありました:'SELECT max(' || quote_ident(colname) || ') FROM ' => 動的に構築されたクエリ内に'SELECT max(' || quote_ident(colname) || '::bigint) FROM ' 追加された::bigintキャストに注意してください。
Tommy Bravo

9

さらに別のplpgsql-次の場合にのみリセット max(att) > then lastval

do --check seq not in sync
$$
declare
 _r record;
 _i bigint;
 _m bigint;
begin
  for _r in (
    SELECT relname,nspname,d.refobjid::regclass, a.attname, refobjid
    FROM   pg_depend    d
    JOIN   pg_attribute a ON a.attrelid = d.refobjid AND a.attnum = d.refobjsubid
    JOIN pg_class r on r.oid = objid
    JOIN pg_namespace n on n.oid = relnamespace
    WHERE  d.refobjsubid > 0 and  relkind = 'S'
   ) loop
    execute format('select last_value from %I.%I',_r.nspname,_r.relname) into _i;
    execute format('select max(%I) from %s',_r.attname,_r.refobjid) into _m;
    if coalesce(_m,0) > _i then
      raise info '%',concat('changed: ',_r.nspname,'.',_r.relname,' from:',_i,' to:',_m);
      execute format('alter sequence %I.%I restart with %s',_r.nspname,_r.relname,_m+1);
    end if;
  end loop;

end;
$$
;

また、行--execute format('alter sequenceをコメント化するとリストが表示されますが、実際には値がリセットされません


8

すべてのシーケンスを公開からリセット

CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text) RETURNS "pg_catalog"."void" AS 
$body$  
  DECLARE 
  BEGIN 
  EXECUTE 'SELECT setval( ''' 
  || tablename  
  || '_id_seq'', ' 
  || '(SELECT id + 1 FROM "' 
  || tablename  
  || '" ORDER BY id DESC LIMIT 1), false)';  
  END;  
$body$  LANGUAGE 'plpgsql';

select sequence_name, reset_sequence(split_part(sequence_name, '_id_seq',1)) from information_schema.sequences
        where sequence_schema='public';

このアプローチは列とテーブルの名前を想定しているため、私にとっては機能しなかったようです
djsnowsill

データベース内のデータに損傷を与えませんか?
Zennin

8

この解決策はpostgres wikiで見つけることをお勧めします。テーブルのすべてのシーケンスを更新します。

SELECT 'SELECT SETVAL(' ||
       quote_literal(quote_ident(PGT.schemaname) || '.' || quote_ident(S.relname)) ||
       ', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' ||
       quote_ident(PGT.schemaname)|| '.'||quote_ident(T.relname)|| ';'
FROM pg_class AS S,
     pg_depend AS D,
     pg_class AS T,
     pg_attribute AS C,
     pg_tables AS PGT
WHERE S.relkind = 'S'
    AND S.oid = D.objid
    AND D.refobjid = T.oid
    AND D.refobjid = C.attrelid
    AND D.refobjsubid = C.attnum
    AND T.relname = PGT.tablename
ORDER BY S.relname;

使用方法(postgres wikiから):

  • これを「reset.sql」などのファイルに保存します。
  • ファイルを実行し、通常のヘッダーを含まない方法でその出力を保存してから、その出力を実行します。例:

例:

psql -Atq -f reset.sql -o temp
psql -f temp
rm temp

元の記事(シーケンスの所有権の修正も含む)はこちら


7

ここで本当にハードコアな回答がいくつかあります。ここでの回答の多くはバージョン9.3では機能しないため、これが要求された頃は本当に悪かったと思います。バージョン8.0以降のドキュメントでは、この質問に対する回答が提供されています。

SELECT setval('serial', max(id)) FROM distributors;

また、大文字と小文字を区別するシーケンス名を処理する必要がある場合は、次のようにします。

SELECT setval('"Serial"', max(id)) FROM distributors;

7

この問題は、エンティティフレームワークを使用してデータベースを作成し、データベースに初期データをシードするときに発生します。これにより、シーケンスが一致しなくなります。

データベースをシードした後に実行するスクリプトを作成することで解決しました。

DO
$do$
DECLARE tablename text;
BEGIN
    -- change the where statments to include or exclude whatever tables you need
    FOR tablename IN SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE' AND table_name != '__EFMigrationsHistory'
        LOOP
            EXECUTE format('SELECT setval(pg_get_serial_sequence(''"%s"'', ''Id''), (SELECT MAX("Id") + 1 from "%s"))', tablename, tablename);
    END LOOP;
END
$do$

1
MAX("Id") + 1シーケンスが=から最大であるときに、それが私にとって最適な理由
最終リンク2018

6

私のバージョンは最初のバージョンを使用していますが、いくつかのエラーチェックがあります...

BEGIN;
CREATE OR REPLACE FUNCTION reset_sequence(_table_schema text, _tablename text, _columnname text, _sequence_name text)
RETURNS pg_catalog.void AS
$BODY$
DECLARE
BEGIN
 PERFORM 1
 FROM information_schema.sequences
 WHERE
  sequence_schema = _table_schema AND
  sequence_name = _sequence_name;
 IF FOUND THEN
  EXECUTE 'SELECT setval( ''' || _table_schema || '.' || _sequence_name  || ''', ' || '(SELECT MAX(' || _columnname || ') FROM ' || _table_schema || '.' || _tablename || ')' || '+1)';
 ELSE
  RAISE WARNING 'SEQUENCE NOT UPDATED ON %.%', _tablename, _columnname;
 END IF;
END; 
$BODY$
 LANGUAGE 'plpgsql';

SELECT reset_sequence(table_schema, table_name, column_name, table_name || '_' || column_name || '_seq')
FROM information_schema.columns
WHERE column_default LIKE 'nextval%';

DROP FUNCTION reset_sequence(_table_schema text, _tablename text, _columnname text, _sequence_name text) ;
COMMIT;

エラーチェックありがとうございます!テーブル/列の名前が長すぎると切り捨てられるため、非常にありがたいRAISE WARNINGです。
ニコラスライリー

5

すべてを一緒に入れて

CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text) 
RETURNS "pg_catalog"."void" AS
$body$
DECLARE
BEGIN
  EXECUTE 'SELECT setval( pg_get_serial_sequence(''' || tablename || ''', ''id''),
  (SELECT COALESCE(MAX(id)+1,1) FROM ' || tablename || '), false)';
END;
$body$  LANGUAGE 'plpgsql';

id'与えられたテーブルの' シーケンスを修正します(通常、例えばdjangoで必要です)。


4

私がまだコードを試していない前に、以下のように、私のクラウスと私のバージョンのいくつかの小さな調整だけで、私のPC [Postgres 8.3]で機能するクラウスとuser457226ソリューションの両方のsql-codeのバージョンを投稿します。 user457226 1つの。

クラウスのソリューション:

drop function IF EXISTS rebuilt_sequences() RESTRICT;
CREATE OR REPLACE FUNCTION  rebuilt_sequences() RETURNS integer as
$body$
  DECLARE sequencedefs RECORD; c integer ;
  BEGIN
    FOR sequencedefs IN Select
      constraint_column_usage.table_name as tablename,
      constraint_column_usage.table_name as tablename, 
      constraint_column_usage.column_name as columnname,
      replace(replace(columns.column_default,'''::regclass)',''),'nextval(''','') as sequencename
      from information_schema.constraint_column_usage, information_schema.columns
      where constraint_column_usage.table_schema ='public' AND 
      columns.table_schema = 'public' AND columns.table_name=constraint_column_usage.table_name
      AND constraint_column_usage.column_name = columns.column_name
      AND columns.column_default is not null
   LOOP    
      EXECUTE 'select max('||sequencedefs.columnname||') from ' || sequencedefs.tablename INTO c;
      IF c is null THEN c = 0; END IF;
      IF c is not null THEN c = c+ 1; END IF;
      EXECUTE 'alter sequence ' || sequencedefs.sequencename ||' restart  with ' || c;
   END LOOP;

   RETURN 1; END;
$body$ LANGUAGE plpgsql;

select rebuilt_sequences();

user457226ソリューション:

--drop function IF EXISTS reset_sequence (text,text) RESTRICT;
CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text,columnname text) RETURNS bigint --"pg_catalog"."void"
AS
$body$
  DECLARE seqname character varying;
          c integer;
  BEGIN
    select tablename || '_' || columnname || '_seq' into seqname;
    EXECUTE 'SELECT max("' || columnname || '") FROM "' || tablename || '"' into c;
    if c is null then c = 0; end if;
    c = c+1; --because of substitution of setval with "alter sequence"
    --EXECUTE 'SELECT setval( "' || seqname || '", ' || cast(c as character varying) || ', false)'; DOES NOT WORK!!!
    EXECUTE 'alter sequence ' || seqname ||' restart with ' || cast(c as character varying);
    RETURN nextval(seqname)-1;
  END;
$body$ LANGUAGE 'plpgsql';

select sequence_name, PG_CLASS.relname, PG_ATTRIBUTE.attname,
       reset_sequence(PG_CLASS.relname,PG_ATTRIBUTE.attname)
from PG_CLASS
join PG_ATTRIBUTE on PG_ATTRIBUTE.attrelid = PG_CLASS.oid
join information_schema.sequences
     on information_schema.sequences.sequence_name = PG_CLASS.relname || '_' || PG_ATTRIBUTE.attname || '_seq'
where sequence_schema='public';

4

パブリックスキーマ関数のすべてのシーケンスを再確認します

CREATE OR REPLACE FUNCTION public.recheck_sequence (
)
RETURNS void AS
$body$
DECLARE
  _table_name VARCHAR;
  _column_name VARCHAR;  
  _sequence_name VARCHAR;
BEGIN
  FOR _table_name IN SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = 'public' LOOP
    FOR _column_name IN SELECT column_name FROM information_schema.columns WHERE table_name = _table_name LOOP
        SELECT pg_get_serial_sequence(_table_name, _column_name) INTO _sequence_name;
        IF _sequence_name IS NOT NULL THEN 
            EXECUTE 'SELECT setval('''||_sequence_name||''', COALESCE((SELECT MAX('||quote_ident(_column_name)||')+1 FROM '||quote_ident(_table_name)||'), 1), FALSE);';
        END IF;
    END LOOP;   
  END LOOP;
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;

3

すべてのシーケンスを1に再開するには、次のコマンドを使用します。

-- Create Function
CREATE OR REPLACE FUNCTION "sy_restart_seq_to_1" (
    relname TEXT
)
RETURNS "pg_catalog"."void" AS
$BODY$

DECLARE

BEGIN
    EXECUTE 'ALTER SEQUENCE '||relname||' RESTART WITH 1;';
END;
$BODY$

LANGUAGE 'plpgsql';

-- Use Function
SELECT 
    relname
    ,sy_restart_seq_to_1(relname)
FROM pg_class
WHERE relkind = 'S';

2

クラウスの答えが最も便利で、少し失敗した場合に備えて、selectステートメントにDISTINCTを追加する必要があります。

ただし、2つの異なるテーブルで同等のテーブル名と列名を使用できないことが確実な場合は、次のものも使用できます。

select sequence_name, --PG_CLASS.relname, PG_ATTRIBUTE.attname
       reset_sequence(split_part(sequence_name, '_id_seq',1))
from PG_CLASS
join PG_ATTRIBUTE on PG_ATTRIBUTE.attrelid = PG_CLASS.oid
join information_schema.sequences
     on information_schema.sequences.sequence_name = PG_CLASS.relname || '_' || PG_ATTRIBUTE.attname
where sequence_schema='public';

これは、関心のある列名が 'ID'ではない場合のuser457226ソリューションの拡張です。


...もちろん、「reset_sequence」の変更も必要です。つまり、「id」の代わりに使用する「columnname」パラメーターを追加します。
mauro

2

初期化のためにカスタムSQLデータをロードしているときにこのエラーが表示される場合、これを回避する別の方法は次のとおりです。

書く代わりに:

INSERT INTO book (id, name, price) VALUES (1 , 'Alchemist' , 10),

id(主キー)を初期データから削除する

INSERT INTO book (name, price) VALUES ('Alchemist' , 10),

これにより、Postgresシーケンスが同期されます。


2

この答えはマウロからのコピーです。

drop function IF EXISTS rebuilt_sequences() RESTRICT;
CREATE OR REPLACE FUNCTION  rebuilt_sequences() RETURNS integer as
$body$
  DECLARE sequencedefs RECORD; c integer ;
  BEGIN
    FOR sequencedefs IN Select
      DISTINCT(constraint_column_usage.table_name) as tablename,
      constraint_column_usage.column_name as columnname,
      replace(replace(columns.column_default,'''::regclass)',''),'nextval(''','') as sequencename
      from information_schema.constraint_column_usage, information_schema.columns
      where constraint_column_usage.table_schema ='public' AND 
      columns.table_schema = 'public' AND columns.table_name=constraint_column_usage.table_name
      AND constraint_column_usage.column_name = columns.column_name
      AND columns.column_default is not null 
      ORDER BY sequencename
   LOOP    
      EXECUTE 'select max('||sequencedefs.columnname||') from ' || sequencedefs.tablename INTO c;
      IF c is null THEN c = 0; END IF;
      IF c is not null THEN c = c+ 1; END IF;
      EXECUTE 'alter sequence ' || sequencedefs.sequencename ||' minvalue '||c ||' start ' || c ||' restart  with ' || c;
   END LOOP;

   RETURN 1; END;
$body$ LANGUAGE plpgsql;

select rebuilt_sequences();

2

Mixed Caseのテーブルと列を使用してデータベースを操作するためにdjsnowsillの回答を取得するために1時間費やし、ついにManuel Darveauからのコメントのおかげで解決策に出くわしましたが、誰にとっても少し明確にできると思いました:

CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text, columnname text)
RETURNS "pg_catalog"."void" AS
$body$
DECLARE
BEGIN
EXECUTE format('SELECT setval(pg_get_serial_sequence(''%1$I'', %2$L),
        (SELECT COALESCE(MAX(%2$I)+1,1) FROM %1$I), false)',tablename,columnname);
END;
$body$  LANGUAGE 'plpgsql';

SELECT format('%s_%s_seq',table_name,column_name), reset_sequence(table_name,column_name) 
FROM information_schema.columns WHERE column_default like 'nextval%';

これには次の利点があります。

  • ID列のスペルが特定の方法であるとは想定していません。
  • すべてのテーブルにシーケンスがあるとは限りません。
  • 大文字と小文字が混在するテーブル/列名で機能します。
  • より簡潔にするためにフォーマットを使用します。

説明すると、問題は、pg_get_serial_sequenceあなたが参照しているものを理解するために文字列をとることです。

"TableName" --it thinks it's a table or column
'TableName' --it thinks it's a string, but makes it lower case
'"TableName"' --it works!

これは''%1$I''、フォーマット文字列で使用することで達成され、''アポストロフィ1$は最初の引数をI意味し、引用符で囲みます


2
select 'SELECT SETVAL(' || seq [ 1] || ', COALESCE(MAX('||column_name||')+1, 1) ) FROM '||table_name||';'
from (
       SELECT table_name, column_name, column_default, regexp_match(column_default, '''.*''') as seq
       from information_schema.columns
       where column_default ilike 'nextval%'
     ) as sequense_query

4
このコードは質問に答えることがありますが、このコードが質問に答える理由や方法に関する追加のコンテキストを提供すると、長期的な価値が向上します。
イェヤ

1

シェルマジックを使用して修正する醜いハックです。優れたソリューションではありませんが、同様の問題を抱えている他の人を刺激するかもしれません:)

pg_dump -s <DATABASE> | grep 'CREATE TABLE' | awk '{print "SELECT setval(#" $3 "_id_seq#, (SELECT MAX(id) FROM " $3 "));"}' | sed "s/#/'/g" | psql <DATABASE> -f -

0

インデックスを再作成してみてください。

更新:コメントで指摘されているように、これは元の質問に対する回答でした。


reindexは機能しませんでした。インデックスを1だけインクリメントするようです
meleyal

3
reindexは、シーケンスではなくデータベースインデックスに関する元の質問に答えていたため機能しませんでした
Vinko Vrsalovic '28

0

SELECT setval... JDBCが動作しなくなるため、Java互換の方法でこれを実行します。

-- work around JDBC 'A result was returned when none was expected.'
-- fix broken nextval due to poorly written 20140320100000_CreateAdminUserRoleTables.sql
DO 'BEGIN PERFORM setval(pg_get_serial_sequence(''admin_user_role_groups'', ''id''), 1 + COALESCE(MAX(id), 0), FALSE) FROM admin_user_role_groups; END;';

0

IDとして使用されるスキーマ内のすべてのシーケンスを更新する方法:

DO $$ DECLARE
  r RECORD;
BEGIN
FOR r IN (SELECT tablename, pg_get_serial_sequence(tablename, 'id') as sequencename
          FROM pg_catalog.pg_tables
          WHERE schemaname='YOUR_SCHEMA'
          AND tablename IN (SELECT table_name 
                            FROM information_schema.columns 
                            WHERE table_name=tablename and column_name='id')
          order by tablename)
LOOP
EXECUTE
        'SELECT setval(''' || r.sequencename || ''', COALESCE(MAX(id), 1), MAX(id) IS NOT null)
         FROM ' || r.tablename || ';';
END LOOP;
END $$;

0

以下のコマンドを実行してください:

SELECT setval('my_table_seq', (SELECT max(id) FROM my_table));

0

ここには良い答えがたくさんあります。Djangoデータベースをリロードした後も同じ必要がありました。

しかし、私は必要でした:

  • オールインワン機能
  • 一度に1つ以上のスキーマを修正できます
  • 一度にすべてまたは1つのテーブルを修正できます
  • また、何が変更されたか、または変更されなかったかを正確に確認するための優れた方法も必要でした

これは、元の要求が求めていたものと非常によく似ています。
BaldiryとおかげでMauroは正しい軌道に乗ってくれました。

drop function IF EXISTS reset_sequences(text[], text) RESTRICT;
CREATE OR REPLACE FUNCTION reset_sequences(
    in_schema_name_list text[] = '{"django", "dbaas", "metrics", "monitor", "runner", "db_counts"}',
    in_table_name text = '%') RETURNS text[] as
$body$
  DECLARE changed_seqs text[];
  DECLARE sequence_defs RECORD; c integer ;
  BEGIN
    FOR sequence_defs IN
        select
          DISTINCT(ccu.table_name) as table_name,
          ccu.column_name as column_name,
          replace(replace(c.column_default,'''::regclass)',''),'nextval(''','') as sequence_name
          from information_schema.constraint_column_usage ccu,
               information_schema.columns c
          where ccu.table_schema = ANY(in_schema_name_list)
            and ccu.table_schema = c.table_schema
            AND c.table_name = ccu.table_name
            and c.table_name like in_table_name
            AND ccu.column_name = c.column_name
            AND c.column_default is not null
          ORDER BY sequence_name
   LOOP
      EXECUTE 'select max(' || sequence_defs.column_name || ') from ' || sequence_defs.table_name INTO c;
      IF c is null THEN c = 1; else c = c + 1; END IF;
      EXECUTE 'alter sequence ' || sequence_defs.sequence_name || ' restart  with ' || c;
      changed_seqs = array_append(changed_seqs, 'alter sequence ' || sequence_defs.sequence_name || ' restart with ' || c);
   END LOOP;
   changed_seqs = array_append(changed_seqs, 'Done');

   RETURN changed_seqs;
END
$body$ LANGUAGE plpgsql;

次に、実行して変更の実行を確認するには:

select *
from unnest(reset_sequences('{"django", "dbaas", "metrics", "monitor", "runner", "db_counts"}'));

戻り値

activity_id_seq                          restart at 22
api_connection_info_id_seq               restart at 4
api_user_id_seq                          restart at 1
application_contact_id_seq               restart at 20
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.