2017-05-23 4 views
6

Ich versuche zu verwenden, verwenden Sie eine temporäre Tabelle mit SQLAlchemy und verknüpfen Sie es mit einer vorhandenen Tabelle. Dies ist, was ich bisherVerwenden Sie temporäre Tabelle mit 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') 

Dies keine Ergebnisse zurück, da die Insert-Anweisungen, dass to_sql tut nicht laufen (ich denke, das ist, weil sie sp_prepexec laufen verwenden, aber ich bin nicht ganz sicher darüber).

Ich versuchte dann nur das Schreiben der SQL-Anweisung (CREATE TABLE #temp_table..., INSERT INTO #temp_table..., SELECT [id] FROM...) und dann pd.read_sql(query, engine) ausgeführt. Ich erhalte die Fehlermeldung

Dieses Ergebnisobjekt gibt keine Zeilen zurück. Es wurde automatisch geschlossen.

Ich denke, das liegt daran, dass die Aussage mehr als nur SELECT?

Wie kann ich dieses Problem beheben (jede Lösung würde funktionieren, obwohl die erste vorzuziehen wäre, da sie hartcodiertes SQL vermeidet). Um es klar zu sagen, ich kann das Schema in der vorhandenen Datenbank nicht ändern - es ist eine Anbieterdatenbank.

+0

gibt es irgendwelche Aufzeichnungen in 'ExistingTable'? –

+0

@AzatIbrakov Ja. Ich habe es tatsächlich in einen linken Join geändert und hinzugefügt 'temp_table.c.date' nur um sicher zu sein. Ich bekomme Zeilen zurück mit 'None' in der' date' Spalte. –

+0

Warum hat Ihre Spalte "Datum" den Typ "DateTime" anstelle von "Date"? –

Antwort

4

Falls die Anzahl der Datensätze in der temporären Tabelle eingefügt werden soll klein/mittel, wäre eine Möglichkeit, ein literal subquery oder ein values CTE statt der Schaffung temporäre Tabelle zu verwenden sein.

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

sei auch angenommen wird, folgende Daten in temp Tabelle eingefügt werden:

# 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)), 
] 

erstellen CTE bzw. eine Unterabfrage, dass die Daten enthalten:

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 

Erstellen endgültige Abfrage mit dem erforderliche Verbindungen und Filter:

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)  

Schließlich Pandas Datenrahmen erhalten:

out_df = pd.read_sql(query.statement, engine) 
result = out_df.to_dict('records') 
+0

Ha, ich dachte * nur * daran, das heute Morgen auf meinem Weg zu tun. Ich werde es versuchen und Sie wissen lassen. –

+0

Ja, das funktioniert. Danke für die tolle Beschreibung. –

0

können Sie versuchen, eine andere Lösung zu verwenden - Prozessgestellte Tabelle

Verfahren lackierte Tabelle einfach eine permanente Tabelle ist, die als temporäre Tabelle dient. Damit Prozesse die Tabelle gleichzeitig verwenden können, verfügt die Tabelle über eine zusätzliche Spalte zur Identifizierung des Prozesses. Der einfachste Weg zu ist dies die globale Variable @@ Spid (@@ Spid ist die Prozess-ID in SQL Server).

...

Eine Alternative für den Prozess-Schlüssel ist, eine GUID (Datentyp UNIQUE) zu verwenden.

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

+0

Schlägst du vor, diese Tabelle in tempdb zu erstellen? Ich denke so oder so, es wird zu dem gleichen Problem kommen, das ich im zweiten Teil meiner Frage habe, wo 'read_sql' keine Zeilen zurückgibt. –

+0

Diese Tabelle sollte in Ihrer Datenbank ('MY_DATABASE') erstellt werden, keine temporäre Datenbank. Es ist nicht gut, aber es sollte funktionieren. –

+0

Ich habe keinen Zugriff zum Erstellen von Tabellen in dieser Datenbank. Es ist eine Anbieterdatenbank. –

Verwandte Themen