16

Oracle データベース テーブルに挿入したい毎月の気象データがありますが、より効率的にするために、対応するレコードをバッチで挿入したいと考えています。Pythonでこれを行う方法について誰かアドバイスできますか?

たとえば、テーブルにステーション ID、日付、2 つの値フィールドの 4 つのフィールドがあるとします。レコードは、ステーション ID と日付フィールド (複合キー) によって一意に識別されます。各ステーションに挿入する必要がある値は、X 年分のデータを含むリストに保持されます。たとえば、2 年の値がある場合、値リストには 24 個の値が含まれます。

一度に 1 つずつレコードを挿入したい場合は、以下のようにすると思います。

connection_string = "scott/tiger@testdb"
connection = cx_Oracle.Connection(connection_string)
cursor = cx_Oracle.Cursor(connection)
station_id = 'STATION_1'
start_year = 2000

temps = [ 1, 3, 5, 7, 9, 1, 3, 5, 7, 9, 1, 3 ]
precips = [ 2, 4, 6, 8, 2, 4, 6, 8, 2, 4, 6, 8 ]
number_of_years = len(temps) / 12
for i in range(number_of_years):
    for j in range(12):
        # make a date for the first day of the month
        date_value = datetime.date(start_year + i, j + 1, 1)
        index = (i * 12) + j
        sql_insert = 'insert into my_table (id, date_column, temp, precip) values (%s, %s, %s, %s)', (station_id, date_value, temps[index], precips[index]))
        cursor.execute(sql_insert)
connection.commit()

上記のことを行う方法はありますが、効率を上げるためにバッチ挿入を実行する方法はありますか? ところで、私の経験は Java/JDBC/Hibernate に関するものなので、誰かが Java アプローチと比較した説明/例を提供できれば、特に役に立ちます。

編集:おそらくここで説明されているように、cursor.executemany() を使用する必要がありますか?

提案、コメントなど、事前に感謝します。

4

5 に答える 5

19

これが私が思いついたもので、うまくいくようです(ただし、これを改善する方法があればコメントしてください):

# build rows for each date and add to a list of rows we'll use to insert as a batch 
rows = [] 
numberOfYears = endYear - startYear + 1
for i in range(numberOfYears):
    for j in range(12):
        # make a date for the first day of the month
        dateValue = datetime.date(startYear + i, j + 1, 1)
        index = (i * 12) + j
        row = (stationId, dateValue, temps[index], precips[index])
        rows.append(row)

# insert all of the rows as a batch and commit
ip = '192.1.2.3' 
port = 1521
SID = 'my_sid'
dsn = cx_Oracle.makedsn(ip, port, SID)
connection = cx_Oracle.connect('username', 'password', dsn)
cursor = cx_Oracle.Cursor(connection)
cursor.prepare('insert into ' + database_table_name + ' (id, record_date, temp, precip) values (:1, :2, :3, :4)')
cursor.executemany(None, rows)
connection.commit()
cursor.close()
connection.close()
于 2013-02-16T01:07:29.607 に答える
9

とを使用Cursor.prepare()Cursor.executemany()ます。

cx_Oracle ドキュメントから:

Cursor.prepare(ステートメント[,タグ])

これは、execute() の呼び出しの前に使用して、実行されるステートメントを定義できます。これが完了すると、execute() への呼び出しが None またはステートメントと同じ文字列オブジェクトで行われたときに、準備フェーズは実行されません。[...]

Cursor.executemany(ステートメントパラメーター)

データベースに対して実行するステートメントを準備し、シーケンス パラメーターで見つかったすべてのパラメーター マッピングまたはシーケンスに対して実行します。ステートメントは、execute() メソッドが管理するのと同じ方法で管理されます。

したがって、上記の 2 つの関数を使用すると、コードは次のようになります。

connection_string = "scott/tiger@testdb"
connection = cx_Oracle.Connection(connection_string)
cursor = cx_Oracle.Cursor(connection)
station_id = 'STATION_1'
start_year = 2000

temps = [ 1, 3, 5, 7, 9, 1, 3, 5, 7, 9, 1, 3 ]
precips = [ 2, 4, 6, 8, 2, 4, 6, 8, 2, 4, 6, 8 ]
number_of_years = len(temps) / 12

# list comprehension of dates for the first day of the month
date_values = [datetime.date(start_year + i, j + 1, 1) for i in range(number_of_years) for j in range(12)]

# second argument to executemany() should be of the form:
# [{'1': value_a1, '2': value_a2}, {'1': value_b1, '2': value_b2}]
dict_sequence = [{'1': date_values[i], '2': temps[i], '3': precips[i]} for i in range(1, len(temps))]

sql_insert = 'insert into my_table (id, date_column, temp, precip) values (%s, :1, :2, :3)', station_id)
cursor.prepare(sql_insert)
cursor.executemany(None, dict_sequence)
connection.commit()

Oracle のMastering Oracle+Pythonシリーズの記事も参照してください。

于 2013-02-15T22:31:21.633 に答える
5

参考までに私のテスト結果:

5000行に挿入します。行ごとに 3 列。

  1. insert を 5000 回実行すると、1.24 分かかります。
  2. executemany で実行すると、0.125 秒かかります。
  3. すべてのコードを挿入して実行します。4.08 分かかります。

すべてをt(a、b、c)に挿入するようにSQLを設定するPythonコードは、デュアルユニオンから:1、:2、:3を選択し、すべての選択:4、:5::6をダウルから選択します...

この長い SQL をセットアップするための Python コードは、0.145329 秒かかりました。

非常に古い Sun マシンでコードをテストしています。CPU:1415MH。

3 番目のケースでは、データベース側を確認しました。待機イベントは「クライアントからの SQL*Net 追加データ」です。これは、サーバーがクライアントからの追加データを待っていることを意味します。

3 番目の方法の結果は、テストなしでは信じられないほどです。

したがって、私からの短い提案は、executemany を使用することです。

于 2016-05-26T05:08:10.213 に答える
3

ユニオンを使用して大きな SQL 挿入ステートメントを作成します。

insert into mytable(col1, col2, col3)
select a, b, c from dual union
select d, e, f from dual union
select g, h, i from dual

Python で文字列を作成し、実行する 1 つのステートメントとして oracle に渡すことができます。

于 2016-02-08T17:55:20.610 に答える