Formatierung von Python SQL-Abfragezeichenfolgen

93

Ich versuche, den besten Weg zum Formatieren einer SQL-Abfragezeichenfolge zu finden. Wenn ich meine Anwendung debugge, möchte ich mich anmelden, um alle SQL-Abfragezeichenfolgen abzulegen, und es ist wichtig, dass die Zeichenfolge ordnungsgemäß formatiert ist.

Option 1

def myquery():
    sql = "select field1, field2, field3, field4 from table where condition1=1 and condition2=2"
    con = mymodule.get_connection()
    ...
  • Dies ist gut zum Drucken der SQL-Zeichenfolge.
  • Es ist keine gute Lösung, wenn die Zeichenfolge lang ist und nicht der Standardbreite von 80 Zeichen entspricht.

Option 2

def query():
    sql = """
        select field1, field2, field3, field4
        from table
        where condition1=1
        and condition2=2"""
    con = mymodule.get_connection()
    ...
  • Hier ist der Code klar, aber wenn Sie die SQL-Abfragezeichenfolge drucken, erhalten Sie all diese nervigen Leerzeichen.

    u '\ nSelect feld1, field2, field3, Field4 \ n_ _ ___ aus Tabelle \ n _ ___ wo condition1 = 1 \ n _ ___ _and condition2 = 2'

Hinweis: Ich habe Leerzeichen durch Unterstriche ersetzt _, da sie vom Editor abgeschnitten werden

Option 3

def query():
    sql = """select field1, field2, field3, field4
from table
where condition1=1
and condition2=2"""
    con = mymodule.get_connection()
    ...
  • Ich mag diese Option nicht, weil sie die Klarheit des gut tabellierten Codes beeinträchtigt.

Option 4

def query():
    sql = "select field1, field2, field3, field4 " \
          "from table " \
          "where condition1=1 " \
          "and condition2=2 "
    con = mymodule.get_connection()    
    ...
  • Ich mag diese Option nicht, weil all die zusätzlichen Eingaben in jeder Zeile und es schwierig ist, die Abfrage auch zu bearbeiten.

Für mich wäre die beste Lösung Option 2, aber ich mag die zusätzlichen Leerzeichen nicht, wenn ich die SQL-Zeichenfolge drucke.

Kennen Sie andere Möglichkeiten?

ssoler
quelle
Dies ist, was die Psycopg-Leute einen naiven Ansatz für die Zusammensetzung von Abfragezeichenfolgen nennen, z. B. mithilfe der Zeichenfolgenverkettung - initd.org/psycopg/docs/… . Verwenden Sie stattdessen Abfrageparameter, um SQL-Injection-Angriffe zu vermeiden und Python-Objekte automatisch in und aus SQL-Literalen zu konvertieren. stackoverflow.com/questions/3134691/…
Matthew Cornell
Diese Frage ist eigentlich nicht spezifisch für SQL-Abfragen, gilt jedoch allgemein für die Formatierung mehrzeiliger Zeichenfolgen in Python. Das SQL-Tag sollte entfernt werden.
cstork

Antworten:

129

Es tut mir leid, dass ich in einem so alten Thread gepostet habe - aber als jemand, der auch eine Leidenschaft für pythonisches "Bestes" teilt, dachte ich, ich würde unsere Lösung teilen.

Die Lösung besteht darin, SQL-Anweisungen mithilfe der String Literal Concatenation ( http://docs.python.org/ ) von Python zu erstellen , die irgendwo zwischen Option 2 und Option 4 qualifiziert werden können

Codebeispiel:

sql = ("SELECT field1, field2, field3, field4 "
       "FROM table "
       "WHERE condition1=1 "
       "AND condition2=2;")

Funktioniert auch mit F-Strings :

fields = "field1, field2, field3, field4"
table = "table"
conditions = "condition1=1 AND condition2=2"

sql = (f"SELECT {fields} "
       f"FROM {table} "
       f"WHERE {conditions};")

Vorteile:

  1. Es behält das pythonische Format "gut tabellarisch" bei, fügt jedoch keine Leerzeichen hinzu (was die Protokollierung verschmutzt).
  2. Es vermeidet die Hässlichkeit der Fortsetzung des Backslashs von Option 4, was es schwierig macht, Aussagen hinzuzufügen (ganz zu schweigen von der Leerraumblindheit).
  3. Außerdem ist es sehr einfach, die Anweisung in VIM zu erweitern (positionieren Sie den Cursor einfach auf den Einfügepunkt und drücken Sie UMSCHALT-O , um eine neue Zeile zu öffnen).
user590028
quelle
1
Wenn dies zum Drucken ist, denke ich, dass eine bessere Alternative darin besteht, es als mutiline Zeichenfolge mit zu schreiben """und textwrap.dedent()vor der Ausgabe zu verwenden
slezica
Ich habe mit dieser Option gespielt, aber dadurch wurde die Protokollausgabe auch mehrzeilig. Beim Verfolgen einer DB-Chat-App verursachte dies eine umfangreiche Ausgabe.
user590028
1
Dies ist ein alter Thread, aber ich habe dieses Format als Best Practice verwendet, jedoch wird es bei längeren Abfragen langweilig
Jabda
7
Sollten wir nicht immer doppelte Anführungszeichen verwenden "sql query", um ein Durcheinander mit SQL-Zeichenfolgen zu vermeiden (die standardmäßig einfache Anführungszeichen verwenden)?
tpvasconcelos
19

Sie haben offensichtlich viele Möglichkeiten in Betracht gezogen, die SQL so zu schreiben, dass sie in Ordnung gedruckt wird. Wie wäre es jedoch, wenn Sie die für die Debug-Protokollierung verwendete 'print'-Anweisung ändern, anstatt Ihre SQL auf eine Weise zu schreiben, die Sie nicht mögen? Wie wäre es mit einer Protokollierungsfunktion wie dieser, wenn Sie oben Ihre bevorzugte Option verwenden:

def debugLogSQL(sql):
     print ' '.join([line.strip() for line in sql.splitlines()]).strip()

sql = """
    select field1, field2, field3, field4
    from table"""
if debug:
    debugLogSQL(sql)

Dies würde es auch trivial machen, zusätzliche Logik hinzuzufügen, um die protokollierte Zeichenfolge auf mehrere Zeilen aufzuteilen, wenn die Zeile länger als die gewünschte Länge ist.

cdlk
quelle
11

Die sauberste Art und Weise, auf die ich gestoßen bin, ist vom SQL-Styleguide inspiriert .

sql = """
    SELECT field1, field2, field3, field4
      FROM table
     WHERE condition1 = 1
       AND condition2 = 2;
"""

Im Wesentlichen sollten die Schlüsselwörter, mit denen eine Klausel beginnt, rechtsbündig und die Feldnamen usw. linksbündig sein. Dies sieht sehr ordentlich aus und ist auch einfacher zu debuggen.

aandis
quelle
2
sql = ("select field1, field2, field3, field4 "
       "from table "
       "where condition1={} "
       "and condition2={}").format(1, 2)

Output: 'select field1, field2, field3, field4 from table 
         where condition1=1 and condition2=2'

Wenn der Wert der Bedingung eine Zeichenfolge sein soll, können Sie Folgendes tun:

sql = ("select field1, field2, field3, field4 "
       "from table "
       "where condition1='{0}' "
       "and condition2='{1}'").format('2016-10-12', '2017-10-12')

Output: "select field1, field2, field3, field4 from table where
         condition1='2016-10-12' and condition2='2017-10-12'"
Pangpang
quelle
5
Bitte mach das niemals. Es heißt SQL-Injection und ist wirklich gefährlich. Nahezu jede Python-Datenbankbibliothek bietet eine Möglichkeit zur Verwendung von Parametern. Wenn Sie sich format()mit SQL-Zeichenfolgen abfinden, ist dies ein wichtiger Codegeruch.
Mattmc3
Ich glaube nicht, dass wir es nicht verwenden können. Sie müssen die Parameter validieren, bevor Sie es verwenden, und Sie sollten wissen, was Sie übergeben.
Pangpang
Die Validierung ist viel fehleranfälliger als nur die Verwendung where condition1=:field1und anschließende Übergabe der Werte als Parameter. Wenn Sie verwenden .format(), gibt es eine Möglichkeit, ein ';DROP TABLE Usersin Ihr SQL einzufügen. In PEP-249 erfahren Sie, wie Sie Parameter richtig verwenden. python.org/dev/peps/pep-0249/#paramstyle
mattmc3
0

Um eine Formatierung vollständig zu vermeiden , ist es meiner Meinung nach eine gute Lösung, Verfahren zu verwenden .

Wenn Sie eine Prozedur aufrufen, erhalten Sie das Ergebnis jeder Abfrage, die Sie in diese Prozedur einfügen möchten . Sie können tatsächlich mehrere Abfragen innerhalb einer Prozedur verarbeiten. Der Aufruf gibt nur die zuletzt aufgerufene Abfrage zurück .

MYSQL

DROP PROCEDURE IF EXISTS example;
 DELIMITER //
 CREATE PROCEDURE example()
   BEGIN
   SELECT 2+222+2222+222+222+2222+2222 AS this_is_a_really_long_string_test;
   END //
 DELIMITER;

#calling the procedure gives you the result of whatever query you want to put in this procedure. You can actually process multiple queries within a procedure. The call just returns the last query result
 call example;

Python

sql =('call example;')
Paroofkey
quelle
-1

Sie könnten die Feldnamen in ein Array "Felder" einfügen und dann:


sql = 'select %s from table where condition1=1 and condition2=2' % (
 ', '.join(fields))
jcomeau_ictx
quelle
Wenn Ihre Bedingungsliste wächst, können Sie dasselbe tun, indem Sie 'und' .join (Bedingungen) verwenden
jcomeau_ictx
Mit Ihrer Lösung wäre die Abfrage noch schwieriger zu bearbeiten als mit Option_4, und es wäre auch schwierig, sie zu lesen.
ssoler
@ssoler, das hängt davon ab, wie man Dinge macht. Ich deklariere nur wenige Variablen in meinen Programmen und verwende stattdessen Arrays von Strings, was Methoden wie die oben genannten zumindest für mich sehr nützlich und wartbar macht .
jcomeau_ictx
-1

Ich würde vorschlagen, bei Option 2 zu bleiben (ich verwende sie immer für Abfragen, die komplexer sind als SELECT * FROM table), und wenn Sie sie auf nette Weise drucken möchten, können Sie immer ein separates Modul verwenden .

Michal Chruszcz
quelle
-1

Für kurze Abfragen, die in eine oder zwei Zeilen passen, verwende ich die String-Literal-Lösung in der oben genannten Lösung mit der höchsten Bewertung. Bei längeren Abfragen teile ich sie in .sqlDateien auf. Ich benutze dann eine Wrapper-Funktion, um die Datei zu laden und das Skript auszuführen, so etwas wie:

script_cache = {}
def execute_script(cursor,script,*args,**kwargs):
    if not script in script_cache:
        with open(script,'r') as s:
            script_cache[script] = s
    return cursor.execute(script_cache[script],*args,**kwargs)

Natürlich lebt dies oft in einer Klasse, so dass ich normalerweise nicht cursorexplizit bestehen muss. Ich benutze auch allgemein codecs.open(), aber dies vermittelt die allgemeine Idee. Dann sind SQL-Skripte vollständig in ihren eigenen Dateien enthalten und haben eine eigene Syntaxhervorhebung.

Aikon
quelle
-2
sql = """\
select field1, field2, field3, field4
from table
where condition1=1
and condition2=2
"""

[In Antwort auf Kommentar bearbeiten]
Eine SQL-Zeichenfolge in einer Methode zu haben, bedeutet NICHT, dass Sie sie "tabellieren" müssen:

>>> class Foo:
...     def fubar(self):
...         sql = """\
... select *
... from frobozz
... where zorkmids > 10
... ;"""
...         print sql
...
>>> Foo().fubar()
select *
from frobozz
where zorkmids > 10
;
>>>
John Machin
quelle
IMO das ist das gleiche wie Option_2
ssoler
@ssoler: Ihre Option_2 hat in allen Zeilen führende Leerzeichen . Beachten Sie, dass in Ihrem Beispiel die führenden Leerzeichen zuvor weggelassen werden select. Meine Antwort hat keine führenden Leerzeichen. Was hat Sie dazu gebracht, sich die Meinung zu bilden, dass sie gleich sind?
John Machin
Wenn Sie Ihre SQL-Zeichenfolge in eine Methode einfügen, müssen Sie alle Zeilen tabellieren (Option_2). Eine mögliche Lösung hierfür ist Option_3.
Ssoler
@ssoler: Entschuldigung, ich verstehe diese Bemerkung nicht. Bitte schauen Sie sich meine aktualisierte Antwort an.
John Machin
Ihre aktualisierte Antwort ist meine Option_3, nicht wahr? Ich mag diese Option nicht, weil sie die Klarheit des gut tabellierten Codes beeinträchtigt.
Ssoler