为何engine.execute()执行MERGE语句无效,需通过engine.begin()?
问题背景
我想了解不同数据库连接方式的工作原理。现有两个表:Table_1的column_2字段全部为null,Table_2的column_2字段存有有效值,我需要将Table_2的数据合并到Table_1中。
使用以下代码执行MERGE语句后,Table_1的column_2仍为null,操作无效:
engine.execute( f''' MERGE INTO TABLE_1 A USING ( SELECT * FROM TABLE_2 ) B ON A.COLUMN_1 = B.COLUMN_1 WHEN MATCHED THEN UPDATE SET A.COLUMN_2 = B.COLUMN_2 ;''')
但使用以下代码执行相同MERGE语句时,Table_1的column_2被成功填充有效值,操作有效:
with engine.begin() as conn: conn.execute( f''' MERGE INTO TABLE_1 A USING ( SELECT * FROM TABLE_2 ) B ON A.COLUMN_1 = B.COLUMN_1 WHEN MATCHED THEN UPDATE SET A.COLUMN_2 = B.COLUMN_2 ;''')
我原本以为是engine.execute()默认不提交更改,但使用它执行CREATE TABLE和ALTER TABLE语句时无需通过engine.begin()即可成功,因此想请教为何MERGE语句必须通过engine.begin()执行才有效?
原因解析
这本质是SQLAlchemy中自动提交行为的差异导致的,核心在于不同类型语句的事务处理逻辑:
DDL语句(如CREATE/ALTER TABLE)的自动提交
SQLAlchemy针对DDL(数据定义语言)语句有特殊处理:大部分数据库的DDL语句本身会强制触发事务提交,而且SQLAlchemy的engine.execute()在执行DDL时会自动触发隐式提交,所以这类语句不需要手动开启事务上下文就能生效。DML语句(如MERGE/UPDATE/INSERT)的事务处理
MERGE属于DML(数据操纵语言)语句,这类语句默认会被纳入事务管理。engine.execute()在执行DML时,默认不会自动提交事务——它会开启一个隐式事务,但执行后不会主动提交,导致修改停留在未提交状态,看起来像是操作无效。
而with engine.begin() as conn:这个上下文管理器会自动完成事务生命周期:开启事务、执行上下文内的语句、无异常则自动提交,有异常则回滚,所以MERGE的修改会被永久保存到数据库中。
- 补充验证
如果不想用engine.begin()上下文,也可以手动调用提交来让engine.execute()的MERGE生效:
result = engine.execute( f''' MERGE INTO TABLE_1 A USING ( SELECT * FROM TABLE_2 ) B ON A.COLUMN_1 = B.COLUMN_1 WHEN MATCHED THEN UPDATE SET A.COLUMN_2 = B.COLUMN_2 ;''') result.connection.commit()
内容的提问来源于stack exchange,提问作者SRJCoding

