У меня есть production_table
и stage_table
. У меня есть скрипт python, который работает в течение нескольких часов и генерирует данные в stage_table
. Я хочу, чтобы в конце сценария stage_table
данные COPY
с stage_table
на production_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 вместе. Я не хочу, чтобы TRUNCATE произошел, если COPY прерывается.
Обычный способ обработки нескольких операторов в одной транзакции в SQLAlchemy заключался бы в том, чтобы начать явную транзакцию и выполнить каждый оператор в ней:
with engine.begin() as conn:
conn.execute(statement_1)
conn.execute(statement_2)
...
Что касается вашей первоначальной попытки, в MySQL нет инструкции COPY. У некоторых других СУБД есть что-то в этом роде. Кроме того, не все драйверы DB-API поддерживают несколько операторов в одном запросе или команде, по крайней мере, из коробки, что, похоже, также имеет место здесь. См. Эту проблему и соответствующую заметку в журнале изменений PyMySQL.
Самая большая проблема заключается в том, что не все заявления в MySQL могут быть отброшены назад, из которых наиболее распространенными являются заявления DDL. Другими словами, вы просто не можете выполнить TRUNCATE [TABLE]...
в той же транзакции, что и следующий INSERT INTO...
и должны разработать приложение вокруг этого ограничения. Как было предложено в комментариях Кристианом В., вы могли бы создать совершенно новую таблицу из своей промежуточной таблицы и переименовать ее или просто поменять местами производственные и промежуточные таблицы. 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")