web-dev-qa-db-ja.com

postgresシーケンスの問題、手動で作成されたPID、適切なシーケンスのリセット

開発者は、過去1年間にpostgres 9.6データベースでシーケンスの問題に遭遇し、そこで動作を停止したため、最後に挿入されたレコードのpidに基づいて手動で作成されたpidを使用して行を挿入し、このコードを使用してシーケンスをリセットするなどの手段を講じています。

SELECT pg_catalog.setval(pg_get_serial_sequence('mytable', 'pid'), 
       (SELECT MAX(pid)+1 FROM mytable) );

私はデータベースを使用してIDを作成できること、そして最後の最大値に基づいて自分のPIDを作成することは「ベストプラクティス」ではなく、すべてのシナリオにとって安全ではないことを知っている十分な経験がありますが、私たちの日常的な使用。

彼らが初めて作業を停止した理由は、たとえばテーブルが復元されたというよく知られた原因です。ただし、この時点では、手動のコードとシーケンスが相互に干渉しており、シーケンスをリセットするコードがしっかりしていないと思います。最後の最大値を挿入するとシーケンスが弱くなることは明らかで、単純に独自の番号を増やします。

私が読んでいる間、誰かが今日私に正しい方向に向けてシーケンスを元に戻し、既存のテーブルで問題なく動作するかどうかを知りたい-そして、時々挿入されるコードが配置されているにもかかわらずそれを機能させる最後の最大値に基づくコードからのpid。 (もちろん、長期的には、そのようなコードがすべて削除されるのが最善であることは十分に承知していますが、今のところそのコードを回避する方法はありますか?)

ソリューションに含まれているのは、postgreSQL 9.6で、競合がある場合にシーケンス自体をリセットする方法です-可能かどうかは不明ですが、経験豊富な講師を受け入れています-それが私がここにいる理由です!

最後に、これは私をここに導いた1つの厄介な事実です-pg adminでシーケンスをリセットした後、pg admin 3および4のテーブルに2つのpidが表示されます。これは、createスクリプトにも表示されます。 「2番目」のPID列は、psqlで\ dを実行すると表示されません。これは適切ですが、関連があると考えました。

UPDATE-その最後のポイントで、pg adminのテーブルのゴースト重複PIDは、同じ列/テーブルのコンボに対して2つのシーケンスがあったために発生しました。2番目のシーケンスは、ある時点で壊れたシーケンスを試行して修正するために作成されました(例:mytable_pid_seqおよびmytable_pid_seq1)。データベースでこれが発生した理由/方法がわかりません。

6
csdev

ソリューションに含まれているのは、postgreSQL 9.6で、競合がある場合にシーケンス自体をリセットする方法です-可能かどうかは不明ですが、経験豊富な講師を受け入れています-それが私がここにいる理由です!

まず、実際の修正は、コードがselect 1+max(pk)nextval('seqname')の一貫性のない組み合わせの代わりに常にシーケンスを使用するようにすることです。

とはいえ、実際の修正前の応急処置として、常にシーケンスでnextvalを呼び出す各行のINSERTにトリガーを設定することができるため、シーケンスが遅れることはありません。 、INSERT自体にnextvalコールがない場合でも。

列にDEFAULT nextval('seqname')(手動またはSERIAL宣言によって設定)があり、トリガーがnextvalに加えてnextvalを呼び出す場合、 DEFAULT、これはシーケンスを1つではなく2つ進めるだけです。いずれにしても、ロールバックまたはキャッシングのためにシーケンスに穴ができる可能性があるため、これはアプリにとって問題にはなりません。

setvalSELECT 1+max(pk) from tableで呼び出すこともできますが、シーケンスを同時に使用しているトランザクションが他にない場合のみです。したがって、トリガーでそれを行うのは良い考えのようには聞こえません。通常、この種の調整は、一括読み込み後にのみ行われます(実際、これがpg_dumpは、シーケンスがテーブルによって所有されている場合に生成されます)。

1
Daniel Vérité

彼らが初めて作業を停止した理由は、たとえばテーブルが復元されたというよく知られた原因です。ただし、この時点では、手動のコードとシーケンスが相互に干渉しており、シーケンスをリセットするコードがしっかりしていないと思います。最後の最大値を挿入するとシーケンスが弱くなることは明らかで、単純に独自の番号を増やします。

pg_dumpから復元している場合、問題になる可能性はありません。たとえば、

CREATE TABLE foo (id serial PRIMARY KEY);
INSERT INTO foo DEFAULT VALUES;

これはこのようなテーブルを生成し、

                         Table "public.foo"
 Column |  Type   |                    Modifiers                     
--------+---------+--------------------------------------------------
 id     | integer | not null default nextval('foo_id_seq'::regclass)
Indexes:
    "foo_pkey" PRIMARY KEY, btree (id)

ここで、IDのタイプはintであり、すべてのシーケンスがデフォルトでnextvalであることがわかります。 pg_dumpを使用すると、このようなダンプが生成されます(コメントと権限を削除します)

CREATE TABLE foo (
    id integer NOT NULL
);

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

ALTER TABLE ONLY foo ALTER COLUMN id SET DEFAULT nextval('foo_id_seq'::regclass);

COPY foo (id) FROM stdin;
1
\.

SELECT pg_catalog.setval('foo_id_seq', 1, true);

ALTER TABLE ONLY foo
    ADD CONSTRAINT foo_pkey PRIMARY KEY (id);

これはたくさんのことですが、単純なテーブルを復元するのに最も安全な方法です。したがって、ここで pg_catalog.setval への呼び出しに注意してください。

SELECT pg_catalog.setval('foo_id_seq', 1, true);

潜在的に高い他のpkidをマージした後、それを新しい最高値で呼び出す必要があります

SELECT pg_catalog.setval(
  'foo_id_seq',
  (SELECT max(id) FROM foo),
  true
);

その後、ビオラ、あなたは仕事に戻ります。彼らはまったく壊れていません。

重要な注意、これはserialタイプ用です。 Identity Columns を使用している場合、ページ10はこのインターフェイスを変更し、代わりに現在の値を変更するための標準化されたALTER TABLEコマンドになります。

2
Evan Carroll