3

異なるネットワーク間のデータ転送情報を保存するデータベースの維持に取り組んでいます。基本的に、各データ転送はログに記録され、毎月末にログ ファイルをデータベースのテーブルにロードする perl スクリプトを実行します。私は perl スクリプトやデータベース スキーマを設計しませんでした。このプロジェクトに取り掛かる前に行われました。

このリンクを使用してテーブルの主キーを取得しました (usage_detail はテーブルの名前です)。テーブルには非常に多くのレコードがあるため、重複を追跡するのは簡単ではありません。多くの重複が読み込まれるという問題があり (転送ごとにログを記録するバグ スクリプトが原因で、それは別のトピックのため)、最終的に最新の読み込みを破棄し、ログ ファイルを修正した後にすべての新しい読み込みを再読み込みする必要がありました。 . ご想像のとおり、これはばかげていて退屈です。

これを修正するために、テーブルに主キーを追加したいと思います。いくつかの理由により、主キー用にまったく新しい列を追加したくありません。フィールドを見た後、複数列の主キーを見つけました。基本的には、転送開始タイムスタンプ、転送終了タイムスタンプ、転送されたファイルの名前 (パス全体も含む) で構成されます。これらのフィールドが同じである 2 つのレコードが存在する可能性は非常に低いようです。

ここに私の質問があります: 1) この主キーをテーブルに追加すると、テーブルに既に存在する可能性のある重複はどうなりますか?

2)この主キーを実際にテーブルに追加するにはどうすればよいですか(PostgreSQL 8.1.22を使用しています)。

3) 主キーが追加された後、ロード スクリプトの実行中に、複製をロードしようとします。PostgreSQL はどのようなエラーをスローしますか? スクリプトでそれをキャッチできますか?

4) ロード スクリプトに関する情報があまりないことは承知していますが、私が提供した情報を基に、スクリプトで何か変更が必要になる可能性があると思いますか?

どんな助けでも大歓迎です。ありがとう。

4

2 に答える 2

3

シリアル列を使用する

あなたの計画は、4,000 万 (!) 行の不必要に巨大なインデックスを追加することです。そして、あなたはそれがユニークになるかどうかさえ確信が持てません. 私はその行動経路に反対することを強くお勧めします。serial代わりに列を追加して、それで完了です。

ALTER TABLE tbl ADD COLUMN tbl_id serial PRIMARY KEY;

それはあなたがする必要があるすべてです。残りは自動的に行われます。マニュアルまたはこれらの密接に関連する回答の詳細:
PostgreSQL primary key auto increment crashes in C++
Auto increment SQL function

serial列の追加は 1 回限りの操作ですが、費用がかかります。テーブル全体を書き換える必要があり、操作中は更新がブロックされます。営業時間外に同時負荷なしで実行するのが最善です。ここでマニュアルを引用します:

null 以外の既定値を持つ列を追加するか、既存の列の型を変更すると、テーブル全体とインデックスを書き換える必要があります。[...] 大きなテーブルでは、テーブルやインデックスの再構築にかなりの時間がかかる場合があります。一時的に 2 倍のディスク容量が必要になります。

これによりテーブル全体が効果的に書き換えられるため、シリアル pk 列を含む新しいテーブルを作成し、古いテーブルからすべての行を挿入して、シリアルにそのシーケンスのデフォルト値を入力させ、古いテーブルを削除して新しいテーブルの名前を変更することもできます。これらの密接に関連する回答の詳細:
Updating database rows without locked the table in PostgreSQL 9.2
Add new column without table lock?

すべての INSERT ステートメントにターゲット リストがあることを確認してから、追加の列でそれらを混乱させることはできません。

INSERT INTO tbl (col1, col2, ...) VALUES ...

いいえ:

tbl 値に挿入 ...

Aserialinteger列 (4 バイト) で実装されます。
主キー制約は、一意のインデックスとNOT NULL関連する列の制約で実装されます。
インデックスの内容は、テーブルと同じように保存されます。追加の物理ストレージが別途必要です。この関連する回答の物理ストレージの詳細:
PostgreSQL でのスペースの計算と節約

インデックスには、2 つのタイムスタンプ (2 x 8 バイト) と長いファイル名が含まれます。パス(〜50バイト?)これにより、インデックスが約2.5 GB大きくなり(40M x 60 ..何かバイト)、すべての操作が遅くなります。

重複の処理

「重複のインポート」の処理方法は、データのインポート方法と「重複」の正確な定義方法によって異なります。

ステートメントについて話している場合、1 つの方法は、一時的なステージング テーブルを使用し、コマンドで単純なorCOPYを使用して重複を折りたたむことです。SELECT DISTINCTDISTINCT ONINSERT

CREATE TEMP TABLE tbl_tmp AS
SELECT * FROM tbl LIMIT 0;     -- copy structure without data and constraints

COPY tbl_tmp FROM '/path/to/file.csv';

INSERT INTO tbl (col1, col2, col3)
SELECT DISTINCT ON (col1, col2)
       col1, col2, col3 FROM tbl_tmp;

または、既存の行との重複も禁止するには:

INSERT INTO tbl (col1, col2, col3)
SELECT i.*
FROM  (
   SELECT DISTINCT ON (col1, col2)
          col1, col2, col3
   FROM   tbl_tmp
   ) i
LEFT   JOIN tbl t USING (col1, col2)
WHERE  t.col1 IS NULL;

温度。テーブルはセッションの最後に自動的に削除されます。

しかし、適切な修正は、最初に重複を生成するエラーの根本に対処することです。

元の質問

1) すべての列に 1 つの重複がある場合、pk をまったく追加できませんでした。

2) PostgreSQL データベースバージョン 8.1に 5 フィートの棒で触れるだけです。それは絶望的に古く、時代遅れで非効率的であり、もはやサポートされておらず、おそらく多くの未修正のセキュリティホールがあります. Postgres の公式バージョン管理サイト。
@Davidはすでに SQL ステートメントを提供しています。

3 & 4) 重複キー違反。PostgreSQL がエラーをスローすることは、トランザクション全体がロールバックされることも意味します。perl スクリプトでそれをキャッチしても、残りのトランザクションを通過させることはできません。たとえば、例外をキャッチできるplpgsqlを使用してサーバー側スクリプトを作成する必要があります。

于 2013-06-13T14:26:18.073 に答える
2
  1. 重複して追加することはできません。最初に重複を削除する必要があります。
  2. ALTER TABLE foo ADD CONSTRAINT foo_pkey PRIMARY KEY(fld1, fld2);
  3. PostgreSQL は、SQL 状態のコード 23505 でエラーを返します。私は perl について何も知りませんが、これをトラップできると思います。
  4. 繰り返しますが、perl については何も知りませんが、エラーをキャッチできると思います。それについてどうするかを決めるのはあなた次第です。

注: サポートされていないバージョンの PostgreSQL を使用しています (おそらくアップグレードする必要があります)。これを SqlFiddle でテストすることさえできませんでした。したがって、すべての回答は PosgreSQL 9.1 に基づいて提供されます。

于 2013-06-13T14:13:47.420 に答える