633

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

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

インポート/リストアが順序を適切に維持していないことが原因のようです。

4

32 に答える 32

857
-- 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 フォーラム

于 2008-10-28T18:14:13.750 に答える
263

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 であるため、このフォームは空のテーブルを正しく処理できません。また、シーケンスの範囲外であるため、setval 0 も設定できません。ALTER SEQUENCEこれに対する 1 つの回避策は、次の構文に頼ることです。

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;
于 2010-09-13T08:19:28.987 に答える
64

これにより、パブリックからのすべてのシーケンスがリセットされ、テーブルまたは列の名前に関する仮定がなくなります。バージョン 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%';
于 2010-11-04T21:03:13.957 に答える
47

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;
于 2008-12-10T08:29:11.830 に答える
25

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

ALTER SEQUENCE "your_sequence_name" RESTART WITH 0;

ゼロの代わりに、シーケンスを再開したい任意の数字を入れることができます。

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

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

于 2014-09-01T10:29:48.710 に答える
21

以下の例では、テーブル名はusersで、スキーマ名はpublic(デフォルトのスキーマ) です。必要に応じて置き換えてください。

1. 最大 ID を確認します。

SELECT MAX(id) FROM public.users;

2. 次の値を確認します。

SELECT nextval('public."users_id_seq"');

3.next valueが より低い場合はmax id、リセットします。

SELECT setval('public."users_id_seq"',
  (SELECT MAX(id) FROM public.users)
);

ノート:

nextval()here に記載されているように、現在の値を返す前にシーケンスをインクリメントしますが、現在の値をcurrval()返すだけです

于 2021-04-07T09:13:02.953 に答える
20

すべてのシーケンスをリセットします。各テーブルの主キーが「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%';
于 2013-01-31T19:00:43.067 に答える
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)

その default 句の nextval() 呼び出しには、::regclass キャストに加えて ::text キャストがあることに注意してください。これは、データベースが古い PostgreSQL バージョンから pg_dump されているためだと思います。上記の関数 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)

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

于 2011-05-09T22:20:43.500 に答える
9

パブリックからすべてのシーケンスをリセット

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';
于 2010-09-24T11:40:30.393 に答える
8

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

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

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$
于 2018-04-08T15:22:44.327 に答える
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;
于 2011-09-13T18:22:39.810 に答える
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 で必要になります)。

于 2012-11-09T12:26:09.877 に答える
5

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

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;
于 2015-06-02T10:15:43.347 に答える
4

コードをまだ試していない前に: 以下に、私の PC [Postgres 8.3] で動作する Klaus と user457226 の両方のソリューションの SQL コードのバージョンを投稿します。 user457226 の場合。

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

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';
于 2011-03-09T21:50:40.803 に答える
4

この回答は mauro からのコピーです。

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();
于 2016-06-16T19:05:14.787 に答える
3

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

書く代わりに:

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

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

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

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

于 2014-05-23T13:52:00.473 に答える
2

Klaus の回答が最も便利です。少しミスがあった場合は execpt です。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 ソリューションの拡張です。

于 2011-03-09T21:05:52.010 に答える
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 -
于 2013-05-27T22:38:33.237 に答える
1

reindex を試してください。

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

于 2008-10-28T18:10:55.397 に答える
1

したがって、このスレッドには十分な意見や車輪の再発明がないことがわかるので、物事にスパイスを加えることにしました.

以下は、次の手順です。

  • テーブルに関連付けられているシーケンスに焦点を当てている (影響するだけ)
  • SERIAL 列と GENERATED AS IDENTITY 列の両方で機能します
  • good_column_names および "BAD_column_123" の名前で機能します
  • テーブルが空の場合、それぞれのシーケンスの定義された開始値を自動的に割り当てます
  • 特定のシーケンスのみに影響を与えることができます (schema.table.column 表記で)
  • プレビューモードがあります
CREATE OR REPLACE PROCEDURE pg_reset_all_table_sequences(
    IN commit_mode BOOLEAN DEFAULT FALSE
,   IN mask_in TEXT DEFAULT NULL
) AS
$$
DECLARE
    sql_reset TEXT;
    each_sec RECORD;
    new_val TEXT;
BEGIN

sql_reset :=
$sql$
SELECT setval(pg_get_serial_sequence('%1$s.%2$s', '%3$s'), coalesce(max("%3$s"), %4$s), false) FROM %1$s.%2$s;
$sql$
;

FOR each_sec IN (

    SELECT
        quote_ident(table_schema) as table_schema
    ,   quote_ident(table_name) as table_name
    ,   column_name
    ,   coalesce(identity_start::INT, seqstart) as min_val
    FROM information_schema.columns
    JOIN pg_sequence ON seqrelid = pg_get_serial_sequence(quote_ident(table_schema)||'.'||quote_ident(table_name) , column_name)::regclass
    WHERE
        (is_identity::boolean OR column_default LIKE 'nextval%') -- catches both SERIAL and IDENTITY sequences

    -- mask on column address (schema.table.column) if supplied
    AND coalesce( table_schema||'.'||table_name||'.'||column_name = mask_in, TRUE )
)
LOOP

IF commit_mode THEN
    EXECUTE format(sql_reset, each_sec.table_schema, each_sec.table_name, each_sec.column_name, each_sec.min_val) INTO new_val;
    RAISE INFO 'Resetting sequence for: %.% (%) to %'
        ,   each_sec.table_schema
        ,   each_sec.table_name
        ,   each_sec.column_name
        ,   new_val
    ;
ELSE
    RAISE INFO 'Sequence found for resetting: %.% (%)'
        ,   each_sec.table_schema
        ,   each_sec.table_name
        ,   each_sec.column_name
    ;
END IF
;

END LOOP;

END
$$
LANGUAGE plpgsql
;

プレビューするには:

call pg_reset_all_table_sequences();

コミットする:

call pg_reset_all_table_sequences(true);

ターゲット テーブルのみを指定するには:

call pg_reset_all_table_sequences('schema.table.column');

于 2020-10-07T19:21:49.063 に答える
-1

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;';
于 2015-05-21T17:01:06.987 に答える