【发布时间】:2022-01-20 17:23:50
【问题描述】:
我正在尝试使用 Oracle DB 在 SQLAlchemy 中进行批量插入操作,该操作使用 blob 数据插入 60k 行。
这就是我的表格和代码的样子:
CREATE TABLE datatables (
id INTEGER NOT NULL,
table_name VARCHAR2(50 CHAR),
row_id VARCHAR2(50 CHAR),
row_data BLOB,
PRIMARY KEY (id)
)
with Session() as session:
session.execute(
DataTables.__table__.insert(),
datas
)
DataTables 是表的 SQLAclhemy 类映射,datas 是这样的字典列表{'id': 1, 'table_name': 'app', 'row_id': 'version', 'row_data': '....'}
用这样的代码我得到了这个 sql 语句
sqlalchemy.engine.Engine INSERT INTO datatables (id, table_name, row_id, row_data) VALUES (:id, :table_name, :row_id, :row_data)
sqlalchemy.engine.Engine [generated in 0.14718s] [{'id': 1, 'table_name': 'app', 'row_id': 'version', 'row_data': b'some_data'}, ...]
它会永远运行,即使 30 分钟后它也没有完成。当我启用 DPI 跟踪时,会有很多这样的行:
ODPI [12912] 2021-12-18 00:07:36.019: ref 0000019C3D15C5B0 (dpiConn) -> 8
ODPI [12912] 2021-12-18 00:07:36.020: ref 0000019C413F0C20 (dpiLob) -> 1 [NEW]
ODPI [12912] 2021-12-18 00:07:36.020: ref 0000019C3D15C5B0 (dpiConn) -> 9
ODPI [12912] 2021-12-18 00:07:36.020: ref 0000019C413F1990 (dpiLob) -> 1 [NEW]
但是,如果我使用原始 sql:
session.execute('insert into DATATABLES (id, table_name, row_id, row_data) values (:id, :table_name, :row_id, :row_data)', datas)
DPI 跟踪更改为:
ODPI [00796] 2021-12-18 00:14:55.246: ref 000002486741EAF0 (dpiVar) -> 0
ODPI [00796] 2021-12-18 00:14:55.246: ref 00000248617D2DF0 (dpiConn) -> 6
ODPI [00796] 2021-12-18 00:14:55.246: fn end dpiVar_release(000002486741EAF0) -> 0
ODPI [00796] 2021-12-18 00:14:55.246: fn start dpiVar_setFromBytes(000002486741EBB0)
ODPI [00796] 2021-12-18 00:14:55.247: fn end dpiVar_setFromBytes(000002486741EBB0) -> 0
ODPI [00796] 2021-12-18 00:14:55.247: fn start dpiVar_setFromBytes(0000024864E5FDE0)
ODPI [00796] 2021-12-18 00:14:55.247: fn end dpiVar_setFromBytes(0000024864E5FDE0) -> 0
ODPI [00796] 2021-12-18 00:14:55.247: fn start dpiVar_setFromBytes(0000024864E601A0)
ODPI [00796] 2021-12-18 00:14:55.247: fn end dpiVar_setFromBytes(0000024864E601A0) -> 0
批量插入在 15 秒内完成。
两种情况下的 SQL 语句相同。为什么它的工作方式不同?当然我可以使用原始 sql,但我想使用DataTables.__table__.insert(),因为如果表或列名称会改变 - 我不需要每次都修复 sql。
【问题讨论】:
-
使用 Alchemy 进行的最可能解释是您正在逐行插入。激活 10046 跟踪来证明这一点。
-
10046 跟踪?抱歉,我对oracle调试不是很熟悉
-
参见例如here
-
我检查了跟踪 - 它是 executemany,而不是逐行。唯一的区别是当我使用
DataTables.__table__.insert()时它使用dpiLob_setFromBytes,但使用原始sql 时它是dpiVar_setFromBytes。不知道为什么它会从 lob 变为 var 以及为什么在这种情况下 lob 会变慢。 -
在使用 dpiLob_setFromBytes() 的情况下,您正在创建一个临时 LOB,写入它,然后从临时 LOB 传输到永久 LOB。在使用 dpiVar_setFromBytes() 的另一种情况下,数据是直接传输的——正如您所发现的,它的效率要高得多!
标签: python oracle sqlalchemy cx-oracle