如何使用Python将批量插入Oracle数据库?

时间:2013-02-15 21:58:37

标签: python oracle python-2.7 cx-oracle batch-insert

我有一些月度天气数据要插入到Oracle数据库表中,但我想在批处理中插入相应的记录以提高效率。任何人都可以建议我如何在Python中执行此操作?

例如,假设我的表有四个字段:工作站ID,日期和两个值字段。记录由站ID和日期字段(组合键)唯一标识。我必须为每个站点插入的值将保存在一个包含X个完整年份数据的列表中,例如,如果有两年的值,那么值列表将包含24个值。

我认为如果我想一次插入一条记录,下面就是我这样做的方式:

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方法相比的解释/示例,那么它将特别有用。

编辑:也许我需要使用如here所述的cursor.executemany()?

提前感谢任何建议,评论等。

5 个答案:

答案 0 :(得分:17)

这是我提出的似乎运作良好的内容(但如果有办法改进,请发表评论):

# 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()

答案 1 :(得分:8)

使用Cursor.prepare()Cursor.executemany()

来自cx_Oracle documentation

  

Cursor.prepare声明 [,标记])

     

这可以在调用execute()之前使用,以定义将要执行的语句。完成此操作后,当使用None或与语句相同的字符串对象调用execute()时,将不会执行准备阶段。 [...]

     

Cursor.executemany声明参数

     

准备一个针对数据库执行的语句,然后针对序列参数中找到的所有参数映射或序列执行该语句。该语句的管理方式与execute()方法管理它的方式相同。

因此,使用上述两个函数,您的代码变为:

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系列文章。

答案 2 :(得分:3)

我将使用union创建一个大的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中构建字符串并将其作为一个要执行的语句提供给oracle。

答案 3 :(得分:3)

正如其中一条评论所说,请考虑使用1) should work Login component Failed: Cannot read property 'componentInstance' of undefined TypeError: Cannot read property 'componentInstance' of undefined at new ComponentFixture_ (C:/repos/FAR/node_modules/angular2/src/testing/test_component_builder.js:38:51) at C:/repos/FAR/node_modules/angular2/src/testing/test_component_builder.js:204:52 at Zone.run (C:/repos/FAR/node_modules/zone.js/dist/zone-microtask.js:1217:24) at zoneBoundFn (C:/repos/FAR/node_modules/zone.js/dist/zone-microtask.js:1194:26) at lib$es6$promise$$internal$$tryCatch (C:/repos/FAR/node_modules/zone.js/dist/zone-microtask.js:442:17) at lib$es6$promise$$internal$$invokeCallback (C:/repos/FAR/node_modules/zone.js/dist/zone-microtask.js:454:18) at lib$es6$promise$$internal$$publish (C:/repos/FAR/node_modules/zone.js/dist/zone-microtask.js:425:12) at C:/repos/FAR/node_modules/zone.js/dist/zone-microtask.js:97:10 at Zone.run (C:/repos/FAR/node_modules/zone.js/dist/zone-microtask.js:1217:24) at zoneBoundFn (C:/repos/FAR/node_modules/zone.js/dist/zone-microtask.js:1194:26) at lib$es6$promise$asap$$flush (C:/repos/FAR/node_modules/zone.js/dist/zone-microtask.js:236:10) 。据说它比使用INSERT ALL要快得多。

例如:

executemany()

http://www.techonthenet.com/oracle/questions/insert_rows.php

答案 4 :(得分:2)

我的测试结果:

我插入5000行。每行3列。

  1. 运行插入5000次,费用为1.24分钟。
  2. 使用executemany运行,费用为0.125秒。
  3. 使用插入所有代码运行:它需要4.08分钟。
  4. python代码,它设置了sql之类的   全部插入t(a,b,c)   select:1,:2,:3 from double union all select:4,:5 :: 6 from daul ...

    设置这个长sql的python代码,花费0.145329秒。

    我在一台非常古老的太阳机上测试我的代码。 cpu:1415 MH。

    在第三种情况下,我检查了数据库端,等待事件是" SQL * Net来自客户端的更多数据"。这意味着服务器正在等待来自客户端的更多数据。

    没有测试,第三种方法的结果对我来说难以置信。

    所以我的简短建议就是使用executemany。