Formattazione della stringa di query SQL Python


93

Sto cercando di trovare il modo migliore per formattare una stringa di query sql. Quando eseguo il debug della mia applicazione, vorrei registrare in un file tutte le stringhe di query sql ed è importante che la stringa sia formattata correttamente.

opzione 1

def myquery():
    sql = "select field1, field2, field3, field4 from table where condition1=1 and condition2=2"
    con = mymodule.get_connection()
    ...
  • Questo è utile per stampare la stringa sql.
  • Non è una buona soluzione se la stringa è lunga e non si adatta alla larghezza standard di 80 caratteri.

opzione 2

def query():
    sql = """
        select field1, field2, field3, field4
        from table
        where condition1=1
        and condition2=2"""
    con = mymodule.get_connection()
    ...
  • Qui il codice è chiaro ma quando si stampa la stringa di query sql si ottengono tutti questi fastidiosi spazi bianchi.

    u '\ nseleziona campo1, campo2, campo3, campo4 \ ​​n_ _ ___ dalla tabella \ n _ ___ dove condizione1 = 1 \ n _ ___ _ e condizione2 = 2'

Nota: ho sostituito gli spazi bianchi con il carattere di sottolineatura _, perché vengono tagliati dall'editor

Opzione 3

def query():
    sql = """select field1, field2, field3, field4
from table
where condition1=1
and condition2=2"""
    con = mymodule.get_connection()
    ...
  • Non mi piace questa opzione perché rompe la chiarezza del codice ben tabulato.

Opzione 4

def query():
    sql = "select field1, field2, field3, field4 " \
          "from table " \
          "where condition1=1 " \
          "and condition2=2 "
    con = mymodule.get_connection()    
    ...
  • Non mi piace questa opzione perché tutta la digitazione extra in ogni riga ed è difficile anche modificare la query.

Per me la soluzione migliore sarebbe l' opzione 2, ma non mi piacciono gli spazi bianchi extra quando stampo la stringa sql.

Conosci altre opzioni?


Questo è ciò che le persone di Psycopg chiamano un approccio ingenuo alla composizione delle stringhe di query, ad esempio utilizzando la concatenazione di stringhe - initd.org/psycopg/docs/… . Utilizzare invece parametri di query per evitare attacchi di SQL injection e per convertire automaticamente gli oggetti Python da e verso i letterali SQL. stackoverflow.com/questions/3134691/…
Matthew Cornell

Questa domanda in realtà non è specifica per le query SQL, ma si applica generalmente alla formattazione di stringhe multilinea in Python. Il tag SQL dovrebbe essere rimosso.
cstork

Risposte:


130

Ci scusiamo per aver postato su un thread così vecchio, ma come qualcuno che condivide anche la passione per il "meglio" pitonico, ho pensato di condividere la nostra soluzione.

La soluzione è creare istruzioni SQL utilizzando String Literal Concatenation di python ( http://docs.python.org/ ), che potrebbe essere qualificata da qualche parte tra l'opzione 2 e l'opzione 4

Esempio di codice:

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

Funziona anche con le stringhe f :

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

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

Professionisti:

  1. Mantiene il formato pitonico "ben tabulato", ma non aggiunge caratteri di spazio estranei (che inquina la registrazione).
  2. Evita la bruttezza della continuazione del backslash dell'opzione 4, che rende difficile aggiungere dichiarazioni (per non parlare della cecità da spazi bianchi).
  3. Inoltre, è molto semplice espandere l'istruzione in VIM (posizionare il cursore sul punto di inserimento e premere MAIUSC-O per aprire una nuova riga).

1
Se questo è per la stampa, penso che un'alternativa migliore sia scriverlo come stringa mutiline """e usarlo textwrap.dedent()prima
dell'output

Ho giocato con questa opzione, ma ha reso anche l'output del registro multilinea. Durante il monitoraggio di un'app db chatty, questo ha causato un output voluminoso.
user590028

1
Questo è un vecchio thread, ma ho utilizzato questo formato come best practice, tuttavia diventa noioso con query più lunghe
Jabda

7
Non dovremmo sempre usare le virgolette doppie "sql query"per evitare di fare confusione con le stringhe SQL (che usano le virgolette singole come standard)?
tpvasconcelos

19

Ovviamente hai considerato molti modi per scrivere l'SQL in modo che venga stampato bene, ma che ne dici di cambiare l'istruzione 'print' che usi per il log di debug, piuttosto che scrivere il tuo SQL in modi che non ti piacciono? Usando la tua opzione preferita sopra, che ne dici di una funzione di registrazione come questa:

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)

Ciò renderebbe anche banale aggiungere ulteriore logica per dividere la stringa registrata su più righe se la linea è più lunga della lunghezza desiderata.


11

Il modo più pulito in cui mi sono imbattuto è ispirato alla guida allo stile sql .

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

In sostanza, le parole chiave che iniziano una clausola dovrebbero essere allineate a destra e i nomi dei campi ecc. Dovrebbero essere allineati a sinistra. Questo sembra molto pulito ed è anche più facile da eseguire il debug.


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'

se il valore di condition dovrebbe essere una stringa, puoi fare così:

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

5
Per favore, non farlo mai e poi mai. Si chiama SQL injection ed è davvero pericoloso. Praticamente ogni libreria di database Python fornisce una struttura per l'utilizzo dei parametri. Se ti accorgi di usare format()con stringhe SQL, è un grande odore di codice.
mattmc3

Non credo che non possiamo usarlo, devi convalidare i parametri prima di usarlo e dovresti sapere cosa passi.
pangpang

La convalida è molto più soggetta a errori rispetto al semplice utilizzo where condition1=:field1e quindi al passaggio dei valori come parametri. Se stai usando .format(), ci sarà un modo per ';DROP TABLE Usersinserire un nel tuo SQL. Dai un'occhiata a PEP-249 per come usare correttamente i parametri. python.org/dev/peps/pep-0249/#paramstyle
mattmc3

0

Per evitare del tutto la formattazione , penso che un'ottima soluzione sia usare le procedure .

La chiamata di una procedura fornisce il risultato di qualsiasi query si desideri inserire in questa procedura. Puoi effettivamente elaborare più query all'interno di una procedura. La chiamata restituirà solo l'ultima query chiamata.

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;

Pitone

sql =('call example;')

-1

potresti inserire i nomi dei campi in un array "campi" e poi:


sql = 'select %s from table where condition1=1 and condition2=2' % (
 ', '.join(fields))

se il tuo elenco di condizioni cresce, potresti fare lo stesso, usando 'e' .join (condizioni)
jcomeau_ictx

con la tua soluzione, la query sarebbe ancora più difficile da modificare rispetto a Option_4, e sarebbe anche difficile da leggere.
ssoler

@ssoler, dipende da come si fanno le cose. Dichiaro poche variabili nei miei programmi e uso invece array di stringhe, il che rende metodi come quelli sopra molto utili e gestibili, almeno da me.
jcomeau_ictx

-1

Suggerirei di attenersi all'opzione 2 (la uso sempre per query più complesse di SELECT * FROM table) e se vuoi stamparla in modo carino puoi sempre usare un modulo separato .


-1

Per query brevi che possono stare su una o due righe, utilizzo la soluzione letterale stringa nella soluzione più votata sopra. Per query più lunghe, le suddivido in .sqlfile. Quindi uso una funzione wrapper per caricare il file ed eseguire lo script, qualcosa come:

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)

Ovviamente questo spesso vive all'interno di una classe, quindi di solito non devo passare cursoresplicitamente. Anche io generalmente uso codecs.open(), ma questo rende l'idea generale. Quindi gli script SQL sono completamente autonomi nei propri file con la propria evidenziazione della sintassi.


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

[modifica in risposta per commentare]
Avere una stringa SQL all'interno di un metodo NON significa che devi "tabularla":

>>> class Foo:
...     def fubar(self):
...         sql = """\
... select *
... from frobozz
... where zorkmids > 10
... ;"""
...         print sql
...
>>> Foo().fubar()
select *
from frobozz
where zorkmids > 10
;
>>>

IMO questo è lo stesso di Option_2
ssoler

@ssoler: la tua opzione_2 ha spazi iniziali su tutte le righe; nota che il tuo esempio omette gli spazi iniziali prima select. La mia risposta non ha spazi iniziali. Cosa ti ha portato a pensare che sono la stessa cosa?
John Machin

Se metti la tua stringa sql all'interno di un metodo dovrai tabulare tutte le righe (Opzione_2). Una possibile soluzione a questo problema è Option_3.
ssoler

@ssoler: Scusa, non capisco quell'osservazione. Per favore guarda la mia risposta aggiornata.
John Machin

La tua risposta aggiornata è la mia Option_3, non è vero? Non mi piace questa opzione perché rompe la chiarezza del codice ben tabulato.
ssoler
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.