SQLAlchemy 在 Oracle DB 中批量插入 blob 数据

Posted

技术标签:

【中文标题】SQLAlchemy 在 Oracle DB 中批量插入 blob 数据【英文标题】:SQLAlchemy bulk insert blob data in Oracle DB 【发布时间】: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() 的另一种情况下,数据是直接传输的——正如您所发现的,它的效率要高得多! 【参考方案1】:

最好将此类问题报告为 SQLAlchemy 中的错误,您可以通过以下方式进行报告:https://github.com/sqlalchemy/sqlalchemy/issues/,否则我们完全不知道这个问题。

在这种情况下,我们已收到有关此问题的警报,并将设法为 SQLAlchemy 2.0 的 cx_oracle 开发人员更新我们的 LOB 设置,跟踪地址为 https://github.com/sqlalchemy/sqlalchemy/issues/7494

【讨论】:

说实话,我还以为是我做错了什么,而不是 SQLAlchemy 的错误行为。感谢您报告问题

以上是关于SQLAlchemy 在 Oracle DB 中批量插入 blob 数据的主要内容,如果未能解决你的问题,请参考以下文章

Matlab中批读取DCM文件

Matlab中批读取DCM文件

Matlab中批读取DCM文件

在 Flask-SQLAlchemy 中查询表的简写

从 flask-sqlalchemy db 获取所有模型

SQLAlchemy 如何获取字段的最大值?