Seite 1 von 1

Datenbankabfrage mit Variablen

Verfasst: Montag 6. Januar 2020, 13:05
von Mahera
Hallo Zusammen,

ich programmiere seit kurzer Zeit Python und möchte über eine grafische Oberfläche eine SQL Abfrage machen.
Ich habe das ganze objektorientiert angelegt. Ich habe nun eine Combobox, welche Daten aus einem Datenframe, genauer die Werte einer Spalte enthält, die nicht gleich sind.
Nun möchte ich den ausgewählten Wert, den ich über ein Event (Änderung der Auswahl) bekomme in eine Abfrage einbinden, was aber immer wieder Fehlermeldungen generiert, dass die Abfrage nicht möglich ist.
hier kurz mein Code:
def search():
conn=sdfjlaskdjflasjdf <--- die verbindung funktioniert, da es geht, wenn question = 'select 3 from Table'
b=combo.get()
q = 'Column1, Column2 from Table WHERE Column2 in '+b
df = pd.read_sql_query(question=q, conn)
Würde mich freuen, wenn jemand eine Idee hätte.
Danke.

Re: Datenbankabfrage mit Variablen

Verfasst: Montag 6. Januar 2020, 14:06
von Sirius3
Hallo Mahera,

`read_sql_query` hat keinen Parameter `question`. Das SQL-Statement muß ein vollständiges SELECT -Statement enthalten. Man formatiert keine Parameter in SQL-Statements sondern benutzt Parameter und das `param`-Argument von ›read_sql_query‹.
Besser helfen könnte man, wenn Du hier funktionierenden Python-Code in Code-Tags </> posten würdest und auch die komplette dazu gehörige Fehlermeldung.

Re: Datenbankabfrage mit Variablen

Verfasst: Dienstag 7. Januar 2020, 10:13
von Mahera
Hallo,

sorry mein Fehler. habe die Funktion hier, nur die Verbindung zur Datenbank habe ich jetzt ausgeklammernt.

def query(question="select * from Robin_FlowCalibration"):
import pyodbc
conn = pyodbc.connect(***)
df_DUT = pd.read_sql_query(question, conn)
return df_DUT
Über die query Funktion rufe ich die Abfrage auf.

Jetzt will ich die Abfrage, also question wie Folgend zusamensetzen:
question = 'ProdDeviceId, TestRig from Robin_FlowCalibration WHERE TestRig in '+b
wobei b hier eine Variable ist, die ich aus einer Combobox via evnt abrufe und als String definiere.

Das hier ist de Fehlercode:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\PROGRA~1\Anaconda3\lib\site-packages\pandas\io\sql.py", line 1400, in execute
cur.execute(*args)
pyodbc.ProgrammingError: ('42000', "[42000] [Microsoft][ODBC SQL Server Driver][SQL Server]Incorrect syntax near ','. (102) (SQLExecDirectW)")

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "C:\PROGRA~1\Anaconda3\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "C:/Users/e00204311/Desktop/PY/Fensteröffnen mit Button_200106_2.py", line 39, in search
data = Windowrules.query(question=q) <-- das ist das definierte Dataframe
File "C:/Users/e00204311/Desktop/PY/Fensteröffnen mit Button_200106_2.py", line 14, in query
df_DUT = pd.read_sql_query(question, conn) <-- das ist die abgefragte Tabelle als Dataframe
File "C:\PROGRA~1\Anaconda3\lib\site-packages\pandas\io\sql.py", line 314, in read_sql_query
parse_dates=parse_dates, chunksize=chunksize)
File "C:\PROGRA~1\Anaconda3\lib\site-packages\pandas\io\sql.py", line 1435, in read_query
cursor = self.execute(*args)
File "C:\PROGRA~1\Anaconda3\lib\site-packages\pandas\io\sql.py", line 1412, in execute
raise_with_traceback(ex)
File "C:\PROGRA~1\Anaconda3\lib\site-packages\pandas\compat\__init__.py", line 403, in raise_with_traceback
raise exc.with_traceback(traceback)
File "C:\PROGRA~1\Anaconda3\lib\site-packages\pandas\io\sql.py", line 1400, in execute
cur.execute(*args)
pandas.io.sql.DatabaseError: Execution failed on sql 'ProdDeviceId, TestRig from Robin_FlowCalibration WHERE TestRig in FCP-6.10': ('42000', "[42000] [Microsoft][ODBC SQL Server Driver][SQL Server]Incorrect syntax near ','. (102) (SQLExecDirectW)")

Re: Datenbankabfrage mit Variablen

Verfasst: Dienstag 7. Januar 2020, 10:41
von __blackjack__
@Mahera: Wie Sirius3 schon schrieb formatiert man keine Werte in Zeichenketten mit SQL sondern benutzt Platzhalter im SQL und das `params`-Argument für die Werte. Und dann vielleicht auch besser gleich `read_sql_query()` statt `read_sql()` wie Sirius3 ja auch schon nahegelegt hat.

Soweit ich das sehe sind DB-API V2 `Connection`-Objekte auch nur bei `sqlite3` von Pandas wirklich unterstützt und für andere Datenbanken sollte man SQLAlchemy verwenden. Dann braucht man auch kein SQL mehr als Zeichenkette schreiben, sondern kann ein „selectable“ als `sql`-Argument übergeben. Was es einem speziell bei einem IN-Test einfacher macht wenn danach eine dynamische Anzahl von Werten folgen.

Importe sollten am Anfang vom Modul stehen, nicht in Funktionen versteckt.

Die Datenbankverbindung sollte genau wie Dateien explizit wieder geschlossen werden.

Re: Datenbankabfrage mit Variablen

Verfasst: Dienstag 7. Januar 2020, 10:43
von Sirius3
Und wo ist jetzt die Zeile 39 von C:/Users/e00204311/Desktop/PY/Fensteröffnen mit Button_200106_2.py, bzw. die gesamte Datei?
Es ist eine schlechte Idee, Dateinamen mit Leerzeichen und Umlauten zu benutzen. Das läßt sich nämlich nicht als Modul importieren.

Wie es aussieht, gilt immer noch das selbe, was ich im ersten Beitrag schon geschrieben habe: Das SQL-Statement muß ein vollständiges SELECT -Statement enthalten. Man formatiert keine Parameter in SQL-Statements sondern benutzt Parameter und das `param`-Argument von ›read_sql_query‹.
Besser helfen könnte man, wenn Du hier funktionierenden Python-Code in Code-Tags </> posten würdest

Re: Datenbankabfrage mit Variablen

Verfasst: Dienstag 7. Januar 2020, 11:10
von __blackjack__
Hier mal ein Beispiel mit „reflection“ der Tabelle(n) (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import pandas as pd
from sqlalchemy import MetaData, create_engine, select


def main():
    engine = create_engine("mssql+pyodbc://<username>:<password>@<dsnname>")
    meta_data = MetaData(engine)
    meta_data.reflect()
    robin_flow_calibration_table = meta_data.tables["Robin_FlowCalibration"]

    values = ["a", "b", "c"]
    data = pd.read_sql_query(
        select(
            [
                robin_flow_calibration_table.c.ProdDeviceId,
                robin_flow_calibration_table.c.TestRig,
            ],
            robin_flow_calibration_table.c.TestRig.in_(values),
        ),
        engine,
    )
    print(data)


if __name__ == "__main__":
    main()
Ich würde aber eher die Tabellen im Code definieren und dabei gleich die Spalten so umbenennen, dass sie den Python-Namenskonventionen entsprechen.