Использовать временную таблицу с SQLAlchemy
Я пытаюсь использовать временную таблицу с SQLAlchemy и объединить ее с существующей таблицей. Вот что у меня пока
engine = db.get_engine(db.app, 'MY_DATABASE')
df = pd.DataFrame({"id": [1, 2, 3], "value": [100, 200, 300], "date": [date.today(), date.today(), date.today()]})
temp_table = db.Table('#temp_table',
db.Column('id', db.Integer),
db.Column('value', db.Integer),
db.Column('date', db.DateTime))
temp_table.create(engine)
df.to_sql(name='tempdb.dbo.#temp_table',
con=engine,
if_exists='append',
index=False)
query = db.session.query(ExistingTable.id).join(temp_table, temp_table.c.id == ExistingTable.id)
out_df = pd.read_sql(query.statement, engine)
temp_table.drop(engine)
return out_df.to_dict('records')
это не возвращает никаких результатов, потому что операторы Insert, что to_sql
Не запускается (я думаю, это потому, что они запускаются с помощью sp_prepexec
, но я не совсем уверен в этом).
затем я попытался просто написать инструкцию SQL (CREATE TABLE #temp_table...
, INSERT INTO #temp_table...
, SELECT [id] FROM...
), а затем работает pd.read_sql(query, engine)
. Я получаю ошибку сообщение
этот объект результата не возвращает строки. Он был закрыт автоматически.
Я думаю, это потому, что заявление делает больше, чем просто SELECT
?
как я могу исправить эту проблему (любое решение будет работать, хотя первое было бы предпочтительнее, поскольку оно избегает жестко закодированного SQL). Чтобы быть ясным, я не могу изменить схему в существующей базе данных-это база данных поставщиков.
2 ответов
в случае, если количество записей, которые будут вставлены во временную таблицу, мало / умеренно, одной из возможностей было бы использовать literal subquery
или values CTE
вместо создания временной таблицы.
# MODEL
class ExistingTable(Base):
__tablename__ = 'existing_table'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String)
# ...
предположим также, следующие данные должны быть вставлены в temp
стол:
# This data retrieved from another database and used for filtering
rows = [
(1, 100, datetime.date(2017, 1, 1)),
(3, 300, datetime.date(2017, 3, 1)),
(5, 500, datetime.date(2017, 5, 1)),
]
создайте CTE или подзапрос, содержащий эти данные:
stmts = [
# @NOTE: optimization to reduce the size of the statement:
# make type cast only for first row, for other rows DB engine will infer
sa.select([
sa.cast(sa.literal(i), sa.Integer).label("id"),
sa.cast(sa.literal(v), sa.Integer).label("value"),
sa.cast(sa.literal(d), sa.DateTime).label("date"),
]) if idx == 0 else
sa.select([sa.literal(i), sa.literal(v), sa.literal(d)]) # no type cast
for idx, (i, v, d) in enumerate(rows)
]
subquery = sa.union_all(*stmts)
# Choose one option below.
# I personally prefer B because one could reuse the CTE multiple times in the same query
# subquery = subquery.alias("temp_table") # option A
subquery = subquery.cte(name="temp_table") # option B
создать окончательный запрос с необходимыми соединениями и фильтрами:
query = (
session
.query(ExistingTable.id)
.join(subquery, subquery.c.id == ExistingTable.id)
# .filter(subquery.c.date >= XXX_DATE)
)
# TEMP: Test result output
for res in query:
print(res)
наконец, получить данные панд кадр:
out_df = pd.read_sql(query.statement, engine)
result = out_df.to_dict('records')
вы можете попробовать использовать другое решение-таблица с ключом процесса
таблица процесс-keyed просто постоянная таблица которая служит как временная таблица. Чтобы разрешить процессам использовать таблицу одновременно, таблица содержит дополнительный столбец для идентификации процесса. Самый простой способ это глобальная переменная @@spid (@@spid-идентификатор процесса в SQL Сервер.)
...
одна альтернатива для process-key-использовать Идентификатор (тип данных тип uniqueidentifier).