web-dev-qa-db-fra.com

Utiliser la table temporaire avec SQLAlchemy

J'essaie d'utiliser une table temporaire avec SQLAlchemy et de la joindre à une table existante. C'est ce que j'ai jusqu'ici

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')

Cela ne renvoie aucun résultat car les instructions insérées que to_sql ne fait pas exécuter (je pense que c'est parce qu'elles sont exécutées à l'aide de sp_prepexec, mais je n'en suis pas tout à fait sûr).

J'ai ensuite essayé d'écrire simplement l'instruction SQL (CREATE TABLE #temp_table..., INSERT INTO #temp_table..., SELECT [id] FROM...) puis d'exécuter pd.read_sql(query, engine). Je reçois le message d'erreur

Cet objet de résultat ne renvoie pas de lignes. Il a été fermé automatiquement.

J'imagine que c'est parce que l'énoncé fait plus que SELECT?

Comment puis-je résoudre ce problème (l'une ou l'autre solution fonctionnerait, bien que la première soit préférable car elle évite le code SQL codé en dur). Pour être clair, je ne peux pas modifier le schéma dans la base de données existante - c'est une base de données fournisseur.

7
Kris Harper

Si le nombre d'enregistrements à insérer dans la table temporaire est faible ou modéré, une possibilité serait d'utiliser un literal subquery ou un values CTE au lieu de créer une table temporaire.

# MODEL
class ExistingTable(Base):
    __table= 'existing_table'
    id = sa.Column(sa.Integer, primary_key=True)
    name = sa.Column(sa.String)
    # ...

Supposons également que les données suivantes doivent être insérées dans la table 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)),
]

Créez un CTE ou une sous-requête contenant ces données:

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

Créez une requête finale avec les jointures et les filtres requis:

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)    

Enfin, obtenez le cadre de données sur les pandas:

out_df = pd.read_sql(query.statement, engine)
result = out_df.to_dict('records')
10
van

Vous pouvez essayer d'utiliser une autre solution - Process-Keyed Table 

Une table de processus est simplement une table permanente qui sert de table temporaire . Pour permettre aux processus d'utiliser la table simultanément, la table Comporte une colonne supplémentaire pour identifier le processus. La méthode la plus simple pour Est la variable globale @@ spid (@@ spid est l'ID de processus dans SQL Server). 

...

Une alternative pour la clé de processus consiste à utiliser un GUID (type de données Uniqueidentifier).

http://www.sommarskog.se/share_data.html#prockeyed

1
Mikhail Lobanov