13

長時間実行した後、id フィールドに穴がどんどん開いていきます。一部のテーブルの ID は int32 で、ID シーケンスが最大値に達しています。一部の Java ソースは読み取り専用であるため、単純に id 列の型を から に変更するint32long、API が壊れてしまいます。

それらすべてに番号を付け直したいと思います。これは良い習慣ではないかもしれませんが、この質問では良いか悪いかは関係ありません。特に、「61789238」、「548273826529524324」などの非常に長い ID の番号を付け直したいと考えています。なぜそんなに長いのかはわかりませんが、短い ID は手動で処理するのも簡単です。

しかし、参照と制約があるため、ID を手動で圧縮するのは簡単ではありません。

PostgreSQL 自体は ID の再番号付けをサポートしていますか? または、このジョブ用のプラグインまたはメンテナンス ユーティリティはありますか?

たぶん私はいくつかのストアドプロシージャを書くことができますか? 1年に1回スケジュールできるので、とてもいいですね。

4

5 に答える 5

17

質問は古いですが、ここで提案されていることを適用しようとした後、dba.SE の絶望的なユーザーから新しい質問を受けました。そこに詳細と説明がある回答を見つけてください:

現在受け入れられている回答 は、ほとんどの場合失敗します。

  • 通常、列にはPRIMARY KEYorUNIQUE制約がありますが、これはデフォルトです。(OPは言及しています。)このような制約は各行の後にチェックされるため、試行中に一意の違反エラーが発生する可能性が高くなります。詳細:idNOT DEFERRABLEreferences and constraints

  • 通常、ギャップを埋めながら、行の元の順序を維持したい場合があります。ただし、行が更新される順序は任意であり、任意の数になります。示された例は元の順序を保持しているように見えます。これは、物理ストレージが目的の順序 (直前に目的の順序で挿入された行) と一致しているためです。これは、実際のアプリケーションではほとんど当てはまらず、まったく信頼できません。

問題は、最初に思われるよりも複雑です。PK / UNIQUE 制約 (および関連する FK 制約) を一時的に削除する余裕がある場合の1 つの解決策 (とりわけ):

BEGIN;

LOCK tbl;

-- remove all FK constraints to the column

ALTER TABLE tbl DROP CONSTRAINT tbl_pkey;  -- remove PK

-- for the simple case without FK references - or see below:    
UPDATE tbl t  -- intermediate unique violations are ignored now
SET    id = t1.new_id
FROM  (SELECT id, row_number() OVER (ORDER BY id) AS new_id FROM tbl) t1
WHERE  t.id = t1.id;

-- Update referencing value in FK columns at the same time (if any)

SELECT setval('tbl_id_seq', max(id)) FROM tbl;  -- reset sequence

ALTER TABLE tbl ADD CONSTRAINT tbl_pkey PRIMARY KEY(id); -- add PK back

-- add all FK constraints to the column back

COMMIT;

これは、大きなテーブルの場合もはるかに高速です。行ごとに PK (および FK) 制約をチェックすると、制約を削除して追加し直すよりもはるかにコストがかかるためです。

を参照する他のテーブルに FK 列がある場合は、データ変更 CTEtbl.idを使用してそれらすべてを更新します。

テーブルfk_tblと FK 列の例fk_id:

WITH u1 AS (
   UPDATE tbl t
   SET    id = t1.new_id
   FROM  (SELECT id, row_number() OVER (ORDER BY id) AS new_id FROM tbl) t1
   WHERE  t.id = t1.id
   RETURNING t.id, t1.new_id  -- return old and new ID
   )
UPDATE fk_tbl f
SET    fk_id = u1.new_id      -- set to new ID
FROM   u1
WHERE  f.fk_id = u1.id;       -- match on old ID

dba.SEで参照されている回答の詳細。

于 2015-08-24T14:14:11.047 に答える
15

ID がシーケンスから生成されていると仮定すると、bignumシーケンスだけRESTARTでテーブルが更新されますidcolumn = DEFAULT

警告: このid列が他のテーブルで外部キーとして使用されている場合は、on update cascade修飾子がオンになっていることを確認してください。

例えば:

テーブルを作成し、いくつかのデータを入れて、中間値を削除します。

db=# create sequence xseq;
CREATE SEQUENCE
db=# create table foo ( id bigint default nextval('xseq') not null, data text );
CREATE TABLE
db=# insert into foo (data) values ('hello'), ('world'), ('how'), ('are'), ('you');
INSERT 0 5
db=# delete from foo where data = 'how';
DELETE 1
db=# select * from foo;
 id | data  
----+-------
  1 | hello
  2 | world
  4 | are
  5 | you
(4 rows)

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

db=# ALTER SEQUENCE xseq RESTART;
ALTER SEQUENCE

データを更新します。

db=# update foo set id = DEFAULT;
UPDATE 4
db=# select * from foo;
 id | data  
----+-------
  1 | hello
  2 | world
  3 | are
  4 | you
(4 rows)
于 2011-08-01T06:49:25.277 に答える
1

*このスクリプトは postgresql で動作します

これは、すべてのケースで機能する一般的なソリューションです

このクエリは、任意のデータベースのすべてのテーブルのフィールドの説明を見つけます。

WITH description_bd AS (select colum.schemaname,coalesce(table_name,relname) as table_name , column_name, ordinal_position, column_default, data_type, is_nullable, character_maximum_length, is_updatable,description from 
 ( SELECT columns.table_schema as schemaname,columns.table_name, columns.column_name, columns.ordinal_position, columns.column_default, columns.data_type, columns.is_nullable, columns.character_maximum_length, columns.character_octet_length, columns.is_updatable, columns.udt_name
  FROM information_schema.columns 
 ) colum

 full join (SELECT schemaname, relid, relname,objoid,  objsubid, description
 FROM pg_statio_all_tables ,pg_description where pg_statio_all_tables.relid= pg_description.objoid  ) descre
  on descre.relname = colum.table_name and  descre.objsubid=colum.ordinal_position   and  descre.schemaname=colum.schemaname )

このクエリは、すべてのデータベース テーブルの順序を修正するソリューションを提案します (これにより、さまざまなテーブルの順序を修正する req フィールドにクエリが生成されます)。

テーブルのレコード数を見つけて、この数を 1 増やします。

SELECT  table_name, column_name, ordinal_position,column_default, 
   data_type, is_nullable, character_maximum_length, is_updatable, 
   description,'SELECT setval('''||schemaname||'.'|| replace(replace(column_default,'''::regclass)',''),'nextval(''','')||''',    (select max( '||column_name ||')+1  from '|| table_name ||' ), true);' as req
  FROM description_bd where column_default  like '%nextva%' 
于 2016-01-19T09:10:55.620 に答える
1

私は答えが気に入らなかったので、PL/pgSQL で関数を書き、その仕事をしました。次のように呼び出されます。

=> SELECT resequence('port','id','port_id_seq');
 resequence   
--------------
 5090 -> 3919

3つのパラメータを取ります

  1. テーブルの名前
  2. SERIAL である列の名前
  3. SERIAL が使用するシーケンスの名前

この関数は、シーケンスの以前の値と新しい値を使用して、実行した内容の短いレポートを返します。

この関数は、指定された列によって ORDER されたテーブルをループし、各行に対して UPDATE を作成します。次に、シーケンスの新しい値を設定します。それでおしまい。

  1. 値の順序は保持されます。
  2. 一時的な列またはテーブルの ADD および DROP は含まれません。
  3. 制約と外部キーの削除と追加は必要ありません。
  4. もちろん、これらの外部キーには ON UPDATE CASCADE を使用することをお勧めします。

コード :

CREATE OR REPLACE FUNCTION resequence(_tbl TEXT, _clm TEXT, _seq TEXT) RETURNS TEXT AS $FUNC$
DECLARE                                            
        _old BIGINT;_new BIGINT := 0;              
BEGIN
        FOR _old IN EXECUTE 'SELECT '||_clm||' FROM '||_tbl||' ORDER BY '||_clm LOOP
                _new=_new+1;
                EXECUTE 'UPDATE '||_tbl||' SET '||_clm||'='||_new||' WHERE '||_clm||'='||_old;
        END LOOP;
        RETURN (nextval(_seq::regclass)-1)||' -> '||setval(_seq::regclass,_new);
END $FUNC$ LANGUAGE plpgsql;
于 2019-11-28T20:29:03.433 に答える