如何使用sqlalchemy截断和插入同一事务?

时间:2018-08-02 08:20:57

标签: python mysql sql sqlalchemy

我有一个production_tablestage_table。 我有一个运行几个小时的Python脚本,并在stage_table中生成数据。 我想在脚本结尾处将COPY的数据从stage_tableproduction_table

基本上这就是我想要的:

1. TRUNCATE production_table
2. COPY production_table from stage_table

这是我的代码:

from sqlalchemy import create_engine
from sqlalchemy.sql import text as sa_text
engine = create_engine("mysql+pymysql://  AMAZON AWS")
engine.execute(sa_text('''TRUNCATE TABLE {1}; COPY TABLE {1} from {0}'''.format(stage_table, production_table)).execution_options(autocommit=True))

这应该生成:

TRUNCATE TABLE production_table; COPY TABLE production_table from stage_table

但是这不起作用。

  

sqlalchemy.exc.ProgrammingError:(pymysql.err.ProgrammingError)(1064,   u“您的SQL语法有误;

我如何使其起作用?以及如何确保TRUNCATE和COPY在一起。如果COPY中止,我不希望发生TRUNCATE。

1 个答案:

答案 0 :(得分:2)

在SQLAlchemy中的单个事务中处理多个语句的通常方法是开始一个显式事务并执行其中的每个语句:

with engine.begin() as conn:
    conn.execute(statement_1)
    conn.execute(statement_2)
    ...

关于您的原始尝试,MySQL中没有COPY语句。其他一些DBMS确实具有这种类型的东西。同样,并不是所有的DB-API驱动程序都支持single query or command中的多个语句,至少是开箱即用的,在这里似乎也是如此。参见this issuerelated note in the PyMySQL ChangeLog

最大的问题是not all statements in MySQL can be rolled back,其中最常见的是DDL语句。换句话说,您根本无法在与以下TRUNCATE [TABLE] ...相同的事务中执行INSERT INTO ...,并且必须围绕该限制来设计应用程序。如Christian W.的注释中所建议,您也许可以从登台表中创建一个全新的表并重命名,或者只是交换生产表和登台表。 RENAME TABLE ...也不能回滚,但是至少您要减少错误窗口,并且可以撤消更改,因为原始生产表仍会以新名称存在。完成所有其他操作后,您可以删除原始生产表。以下是说明此想法的内容,但如果出现问题,则需要手动干预:

# No point in faking transactions here, since MySQL in use.
engine.execute("CREATE TABLE new_production AS SELECT * FROM stage_table")
engine.execute("RENAME TABLE production_table TO old_production")
engine.execute("RENAME TABLE new_production TO production_table")
# Point of no return:
engine.execute("DROP TABLE old_production")