2014-06-25 11 views
22

Esistono esempi su come passare parametri con una query SQL in Pandas?Panda read_sql con parametri

In particolare, sto utilizzando un motore SQLAlchemy per connettersi a un database PostgreSQL. Finora ho trovato che le seguenti opere:

df = psql.read_sql(('select "Timestamp","Value" from "MyTable" ' 
        'where "Timestamp" BETWEEN %s AND %s'), 
        db,params=[datetime(2014,6,24,16,0),datetime(2014,6,24,17,0)], 
        index_col=['Timestamp']) 

La documentazione Panda dice che params possono anche essere passati come un dizionario, ma io non riesco a farlo funzionare dopo aver provato per esempio:

df = psql.read_sql(('select "Timestamp","Value" from "MyTable" ' 
        'where "Timestamp" BETWEEN :dstart AND :dfinish'), 
        db,params={"dstart":datetime(2014,6,24,16,0),"dfinish":datetime(2014,6,24,17,0)}, 
        index_col=['Timestamp']) 

Qual è il modo consigliato di eseguire questi tipi di query da Panda?

risposta

31

I read_sql documenti dicono che questo argomento params può essere un elenco, tupla o dict (vedi docs).

per passare i valori nella query SQL, ci sono diverse sintassi possibili: ?, :1, :name, %s, %(name)s (vedi PEP249).
Ma non tutte queste possibilità sono supportate da tutti i driver di database, , la cui sintassi è supportata dipende dal driver che si sta utilizzando (psycopg2 nel tuo caso, suppongo).

Nel suo secondo caso, quando si utilizza un dict, si utilizza 'argomenti con nome', e secondo la documentazione psycopg2, essi sostengono lo stile %(name)s (e quindi non è il :name suppongo), vedere http://initd.org/psycopg/docs/usage.html#query-parameters.
Quindi, utilizzando quello stile dovrebbe funzionare:

df = psql.read_sql(('select "Timestamp","Value" from "MyTable" ' 
        'where "Timestamp" BETWEEN %(dstart)s AND %(dfinish)s'), 
        db,params={"dstart":datetime(2014,6,24,16,0),"dfinish":datetime(2014,6,24,17,0)}, 
        index_col=['Timestamp']) 
+0

Ciò è molto utile: sto usando psycopg2, quindi la sintassi '% (name) s funziona perfettamente. – tobycoleman

+0

Probabilmente dovremmo menzionarne qualcosa nella docstring: https: //github.com/pydata/pandas/issues/7573 – joris

+0

Questa soluzione non funziona più su Postgres: è necessario usare ': notation', e quindi essere sicuri per avvolgere la stringa SQL con 'sqlalchemy.text()' – hamx0r

1

Uso questa configurazione con SQLite, il che significa che posso gestire i parametri in Python e non in Pandas in particolare. Funziona bene per me e è più facile da leggere, quindi lancia una query e parametri direttamente in read_sql.

con = sqlite3.connect("mydb.db") 

verses_sql = '''SELECT 
        kjv.b, 
        kjv.id, 
        kjv.t, 
        kjv.v, 
        ke.n, 
        ke.author 
       FROM t_kjv kjv 
       LEFT JOIN key_english ke on kjv.b = ke.b 
       WHERE blah = %s''' % blah 

df_verses = pd.read_sql(verses_sql, con, index_col='id') 

Facendo in questo modo significa che si può anche passare un dict alla stringa multilinea, se si preferisce che:

>>> d = { 'vars': "variables", 'example': "example" } 
>>> s = "This is an {example} with {vars}" 
>>> s.format(**d) 
'This is an example with variables' 
+0

qualcuno sta usando questo con Sybase e sqlanydb? – toasteez

+7

Ciò significa anche che in questo modo è possibile iniettare un'intera più roba che solo valori. In effetti ti permette di modificare la natura della query stessa. Questo approccio è soggetto a [SQL Injection] (https://www.google.com/search?q=sql+injection), da cui la necessità di utilizzare le variabili di bind. – YoYo

+0

@toasteez questo fa parte della sostituzione della stringa del modello Python e non ha nulla a che fare con SQL. Quindi è indipendente dal database usato e funzionerà altrettanto bene allo stesso modo. Si noti però l'articolo di SQL Injection. – YoYo