2

私は約100000レコードで更新しているpostgresql dbを持っています。session.merge() を使用して各レコードを挿入/更新し、1000 レコードごとにコミットします。

i=0
for record in records:
    i+=1
    session.merge(record)
    if i%1000 == 0:
        session.commit()

このコードは正常に動作します。私のデータベースには UNIQUE フィールドを持つテーブルがあり、それに挿入する重複したレコードがいくつかあります。これが発生すると、フィールドが一意ではないことを示すエラーがスローされます。一度に 1000 レコードを挿入しているので、ロールバックではこれらのレコードをスキップできません。重複レコードの session.merge() をスキップする方法はありますか (もちろん、すべてのレコードを解析して重複レコードを見つける以外に)?

4

4 に答える 4

3

あなたはすでにこれを知っていると思いますが、定説から始めましょう: フィールドが一意である必要があると指定したため、データベースに一意性をチェックさせるか、それを起こさせないことによるエラーに対処する必要があります。

一意性のチェック:

if value not in database:
    session.add(value)
session.commit()

一意性をチェックせず、例外をキャッチします。

try:
    session.add(value)
    session.commit()
except IntegrityError:
    session.rollback()

最初のものには競合状態があります。私は2番目のパターンを使用する傾向があります。

さて、これを実際の問題に戻します。データベース内の列の一意性を保証したい場合は、明らかに、ロードされた値の実際の一意性をデータベースに保証させるか、データベースに提供させる必要がありますエラーとあなたはそれを処理します。

これは、セッションに 10 万個のオブジェクトを追加してすべてコミットするよりも明らかに時間がかかりますが、それがデータベースのしくみです。

一意性を確保するために、データベースの外部にロードするデータをロードする前にマッサージすることを検討することをお勧めします。そうすれば、ロード時に一意性をチェックする必要がなくなります。たとえば、csv ファイルやテキスト ファイルからロードする場合、コマンド ライン ツールを使用すると非常に簡単に実行できます。

于 2012-06-30T13:00:07.177 に答える
2

SQLAlchemyがbegin_nested()を介して公開するSAVEPOINTを使用して、「部分的なロールバック」を取得できます。あなたはこのようにそれを行うことができます:

for i, record in enumerate(records):
    try:
        with session.begin_nested():
            session.merge(record)
    except:
        print "Skipped record %s" % record
    if not i % 1000:
        session.commit()

上記の注意事項:

  1. Pythonでは、「i = i+1」のことは決して行いません。を使用しますenumerate()
  2. with session.begin_nested():begin_nested()と言うのと同じです。commit()例外がない場合、またはrollback()そうである場合。
于 2012-07-02T17:02:05.413 に答える
1

PostgreSQL ドキュメントのこの例の行に沿って関数を作成することを検討してください。

于 2012-06-30T16:47:43.133 に答える
0

重複する一意のキーを持つレコードの数が最小限であるため、これは私にとって最適なオプションです。

def update_exception(records, i, failed_records):
    failed_records.append(records[i]['pk'])
    session.rollback()
    start_range = int(round(i/1000,0) * 1000)
    for index in range(start_range, i+1):
        if records[index]['pk'] not in failed_records:
            ins_obj = Model()
            try:
                session.merge(ins_obj)
            except:
                failed_records.append(json_data[table_name][index-1]['pk'])
                pass

たとえば、2375でエラーが発生した場合、2375レコードの主キー「pk」をfailed_recordsに保存してから、2000から2375に再コミットします。コミットを1つずつ実行するよりもはるかに高速なようです。

于 2012-07-27T06:06:23.217 に答える