psycopg2 fügt vor Variablen in SQL statements E ein

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
endu
User
Beiträge: 9
Registriert: Dienstag 31. März 2009, 13:43

hiho,

mein pythoncode sieht z.b. folgendermaßen aus.
name='endu'
curs.execute("""
SELECT * FROM Kunde WHERE LOWER(kname) LIKE LOWER(%s))""",
(name,))
nun fügt mir scheinbar psycopg2 während der Abfrage ein E ein, so das das Statement nun lautet.
SELECT * FROM Kunde WHERE LOWER(kname) LIKE LOWER(E'endu')
(zum testen habe ich einfach mal "bla" an das Ende des Statements geschrieben, was beim Ausführen zu einer Fehlermeldung führt, die auch die problematische stelle des statements mit ausgibt.)

kennt jemand das problem bzw. noch besser eine lösung dafür?

zum System:
psycopg2-2.0.9
python 2.6.1
postgresql 8.3
windows xp
Zuletzt geändert von endu am Dienstag 31. März 2009, 16:15, insgesamt 1-mal geändert.
lunar

Funktioniert die Abfrage denn noch, sprich, gibt sie korrekte Ergebnisse zurück?
endu
User
Beiträge: 9
Registriert: Dienstag 31. März 2009, 13:43

teilweise. im where bereich funktioniert es seltsamerweise meistens.

wenn ich allerdings als variable die tabelle angebe z.b. mit
tab_test='Kunde'
curs.execute("""SELECT * FROM %s""",(tab_test,))
ende ich jedesmal mit
psycopg2.ProgrammingError: syntax error at or near "E'Kunde'"
LINE 1: SELECT * FROM E'Kunde'
Update am Rande:
Im Where Bereich tritt das E immer auf, wenn ich auf die Variable einen Typecast wie z.b. str(name) anwende.
lunar

Platzhalter sind nicht für Tabellen gedacht, sondern nur für Parameter in SELECT- und INSERT-Ausdrücken.

Das "E" hängt auch garantiert nicht mit dem Typecast zusammen, das Modul kann nämlich gar nicht wissen, dass du eine Typkonvertierung durchführst.
frabron
User
Beiträge: 306
Registriert: Dienstag 31. März 2009, 14:36

Zur Info:
Die E'' Syntax in Postgres dient zum Escapen von Backslashes in Zeichenketten. Backslashes läuten immer eine besondere Zeichenfolge ein, um das zu umgehen, kann man den Inhalt in mittels der E'' Syntax verpacken.

Kommen in deinen Queries Backslashes oder andere Sonderzeichen vor?

http://www.postgresql.org/docs/8.3/inte ... e-8-1.html
endu
User
Beiträge: 9
Registriert: Dienstag 31. März 2009, 13:43

hallo frabron,

danke für den Link, das erklärt schonmal warum das E jedesmal wenn etwas als string übergeben wird auftaucht.

Die Frage ist nun, ob ich das irgendwie gezielt abschalten kann.
Entweder generell oder wie sich der String so umpacken lässt, dass diese Behandlung nicht auftritt.

In meinen Queries kommen definitiv keine Backslashes und nur vereinzelt ein Unterstrich vor (aber an dieser stelle kann ich nötigenfalls auch das Feld umbenennen) sonstige Sonderzeichen werden nicht verwendet.

@lunar:
Platzhalter sind nicht für Tabellen gedacht, sondern nur für Parameter in SELECT- und INSERT-Ausdrücken.
Heißt das, es ist nicht möglich oder wie ist es zu verstehen?

Derzeit habe ich u.a. 4 fast identisch aufgebaute Tabellen aus denen jeweils mit einer eigenen Funktion die selbe Abfrage ausgeführt wird.

Diese 4 Funktionen sollen zu einer zusammengefasst und über einen Parameter einfach die benötigte Tabelle ausgewählt werden.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

endu hat geschrieben: @lunar:
Platzhalter sind nicht für Tabellen gedacht, sondern nur für Parameter in SELECT- und INSERT-Ausdrücken.
Heißt das, es ist nicht möglich oder wie ist es zu verstehen?

Derzeit habe ich u.a. 4 fast identisch aufgebaute Tabellen aus denen jeweils mit einer eigenen Funktion die selbe Abfrage ausgeführt wird.
Also "select *" sind imho nicht wirklich brauchbar!
Davon abgesehen: Wenn die Tabellen wirklich nahezu identisch sind, könnte es u.U. ja sinnvoll sein, daraus gleich eine Tabelle zu basteln? Dazu müßte man natürlich mehr über das Datenmodell wissen :-)
Diese 4 Funktionen sollen zu einer zusammengefasst und über einen Parameter einfach die benötigte Tabelle ausgewählt werden.
Hm ... riecht für mich nach einer Bestätigung, dass die Tabellen eigentlich unnötig sind. Denn im Code musst Du ja irgend wann einmal eine Entscheidung treffen, dass Du aus Tabelle x die Daten y haben willst! Und ob ich dann genau eine Funktion aufrufe, oder je nach Fall eine bestimmte macht für mich da auf den ersten Blick keinen Sinn.
endu
User
Beiträge: 9
Registriert: Dienstag 31. März 2009, 13:43

ok, ich hätte nicht Select * schreiben sollen. Ich wollte das Beispiel vereinfachen um das Problem besser demonstrieren zu können...

Die 4 Tabellen zusammenzufassen ist leider keine Option, da dann einige Anwendungen, auf die ich keinen Einfluss habe angepasst werden müssten.

Letzlich kann man es sich so vorstellen, 4 (zukünftig evtl. weitere) Abteilungen haben jeweils eine eigene Tabelle mit identischen Feldnamen. Nun kommt eine 5te Abteilung ins Spiel die Zugriff auf alle Daten benötigt.

Verbildlicht sieht das etwa so aus:

SELECT tabid, name, wert1, wert2, wert3, wert4
FROM <tabelle der entsprechenden Abteilung>
WHERE name=<Bestimmer Name nach dem gesucht wird.>

Ich weiß selbst das das kein schönes Datenbankmodell ist, aber das kann ich (leider) nicht beeinflussen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

endu hat geschrieben:ok, ich hätte nicht Select * schreiben sollen. Ich wollte das Beispiel vereinfachen um das Problem besser demonstrieren zu können...
ok ;-)
Letzlich kann man es sich so vorstellen, 4 (zukünftig evtl. weitere) Abteilungen haben jeweils eine eigene Tabelle mit identischen Feldnamen. Nun kommt eine 5te Abteilung ins Spiel die Zugriff auf alle Daten benötigt.
Auch schreibend? Ansonsten könnte man das evtl. über einen view lösen, der mit unions alles zusammenflantscht?
Ich weiß selbst das das kein schönes Datenbankmodell ist, aber das kann ich (leider) nicht beeinflussen.
Ja, das ist sicherlich schade. Aber manchmal gibt's eben gewachsene Strukturen ...

Mir fällt grad ein, dass man das mit dem view evtl. aber als Lösung für eine Migration zu einem besseren DB-Modell nutzen könnte:

1.) Es wird eine neue Tabelle angelegt, die um ein Feld "AbteilungsNr." o.ä. erweitert wird.

2.) Man migriert die Daten einfach in die neue Tabelle

3.) Man legt 4 views an, die genau so heißen, wie die ehemaligen Tabellen und eben nur deren Inhalte rausfiltert (where AbteilungsNr = x)

Damit sollten die "alten" Applikationen weiter lauffähig sein, man hat aber als Basis nun ein besseres DB-Design.

Wobei man einen View ja nur lesend nutzen kann fällt mir grad auf. Mist! Dann geht's natürlich nicht so einfach ...

*seufz* gewachsene Strukturen sind manchmal so nervig :-D
lunar

endu hat geschrieben:
Platzhalter sind nicht für Tabellen gedacht, sondern nur für Parameter in SELECT- und INSERT-Ausdrücken.
Heißt das, es ist nicht möglich oder wie ist es zu verstehen?
Wie sollte es sonst zu verstehen sein? Parameter sind dazu da, Werte in einen Ausdruck einzufügen, nicht den Ausdruck selbst zu verändern.

Für programmatische Manipulation von SQL-Ausdrücken gibt es SQLAlchemy.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

endu hat geschrieben:
Platzhalter sind nicht für Tabellen gedacht, sondern nur für Parameter in SELECT- und INSERT-Ausdrücken.
Heißt das, es ist nicht möglich oder wie ist es zu verstehen?
Hallo endu!

Das bedeutet nur, dass du die Platzhalter für Parameter verwenden sollst und nicht für den Tabellennamen. Um den Tabellennamen zu ersetzen, kannst du die die Abfrage mit den ganz normalen Python-Mitteln zusammensetzen.

Code: Alles auswählen

>>> from string import Template
>>> tabellenname = "tabelle_a"
>>> parameter = [["Max", "Mustermann"], ["Maria", "Musterfrau"]]
>>> sql = Template("""
... SELECT address FROM ${tabellenname} WHERE
... (first_name = %s) AND (last_name = %s)
... """).substitute(tabellenname = tabellenname)
>>> sql
'\nSELECT address FROM tabelle_a WHERE\n(first_name = %s) AND (last_name = %s)\n'
>>> cursor.executemany(sql, parameter)
EDIT: OK, executemany macht hier nicht viel Sinn. Aber ich hoffe, dass ich die Lösung trotzdem aufzeigen konnte.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
endu
User
Beiträge: 9
Registriert: Dienstag 31. März 2009, 13:43

So, ich denke der Weg mit den Templates ist genau das was ich gesucht habe.

Danke @ gerold für den Hinweis und das Codebeispiel. Das hat mich gravierend weitergebracht.

@Hyperion
Die Abteilungen müssen auch weiter schreiben. Aber da an dieser Datenbankstruktur nichts ändern darf, muss ich damit leben.

@lunar
Danke dür den Hinweis auf SQLAlchemy. Ich ich werde mich da auch mal einlesen.

Mein Resümee des Threads:
1. Quelle des Problems mit dem E am Anfang war die von frabron angesprochene Postgrebehandlug von Zeichenketten.

2. Um unterschiedliche Tabellen mit einer Abfrage zu verarbeiten, lässt sich das Statement als Template oder über SQLAlchemy zusammenbauen

Somit bin ich zufrieden :)

Danke!
Antworten