2013-06-18 3 views
11

Sto provando ad inserire dati da un dizionario in un database usando parametri con nome. Ho questo lavoro con una semplice dichiarazione SQL, ad es.Python sqlite insert named parameters o null

SQL = "INSERT INTO status (location, arrival, departure) VALUES (:location, :arrival,:departure)" 
dict = {'location': 'somewhere', 'arrival': '1000', 'departure': '1001'} 
c.execute(SQL,dict) 

Inserisce da qualche parte nella posizione, 1000 nella colonna di arrivo e 1001 nella colonna di partenza.

I dati che effettivamente avrò conterranno la posizione ma potrebbero contenere arrivo o partenza ma potrebbero non avere entrambi (nel qual caso NULL o NULL possono entrare nella tabella). In questo caso, ottengo sqlite3.ProgrammingError: Non è stato fornire un valore per il legame 2.

posso risolvere questo problema utilizzando defaultdict:

c.execute(SQL,defaultdict(str,dict)) 

per rendere le cose un po 'più complicato, io in realtà hanno un elenco di dizionari contenenti più posizioni con arrivo o partenza.

({'location': 'place1', 'departure': '1000'}, 
    {'location': 'palce2', 'arrival': '1010'}, 
    {'location': 'place2', 'departure': '1001'}) 

e voglio essere in grado di eseguire questo con c.executemany però io ora non posso usare defaultdict.

È possibile eseguire il ciclo di ogni dizionario nell'elenco ed eseguire molte istruzioni c.execute, ma executemany sembra un modo più ordinato di farlo.

Ho semplificato questo esempio per comodità, i dati effettivi hanno molte più voci nel dizionario e lo costruisco da un file di testo JSON.

Qualcuno ha qualche suggerimento su come potrei fare questo?

risposta

12

Usa None per inserire un NULL:

dict = {'location': 'somewhere', 'arrival': '1000', 'departure': None} 

È possibile utilizzare un dizionario predefinito e un generatore per usare questo con executemany():

defaults = {'location': '', 'arrival': None, 'departure': None} 

c.executemany(SQL, ({k: d.get(k, defaults[k]) for k in defaults} for d in your_list_of_dictionaries) 
+0

Grazie Martin - che funziona. Per chiunque abbia questo problema, il dizionario predefinito deve contenere una voce per ogni possibile elemento, anche se il dizionario effettivo lo avrà sempre. Inoltre, c'è una parentesi chiusa finale mancante nella riga executemany. – user2497185

0

c'è una soluzione più semplice a questo problema che dovrebbe essere fattibile nella maggior parte dei casi; basta passare alexecutemanyun elenco didefaultdictinvece di una lista didict.

In altre parole, se si costruisce da zero le righe come defaultdict si può passare l'elenco dei defaultdict righe direttamente al comando executemany, invece di costruire, come dizionari e poi rattoppare la situazione prima di utilizzare executemany.

Il seguente esempio di lavoro (Python 3.4.3) mostra il punto:

import sqlite3 
from collections import defaultdict 
# initialization 
db = sqlite3.connect(':memory:') 
c = db.cursor() 
c.execute("CREATE TABLE status(location TEXT, arrival TEXT, departure TEXT)") 
SQL = "INSERT INTO status VALUES (:location, :arrival, :departure)" 
# build each row as a defaultdict 
f = lambda:None # use str if you prefer 
row1 = defaultdict(f,{'location':'place1', 'departure':'1000'}) 
row2 = defaultdict(f,{'location':'place2', 'arrival':'1010'}) 
rows = (row1, row2) 
# insert rows, executemany can be safely used without additional code 
c.executemany(SQL, rows) 
db.commit() 
# print result 
c.execute("SELECT * FROM status") 
print(list(zip(*c.description))[0]) 
for r in c.fetchall(): 
    print(r) 
db.close() 

Se lo si esegue, esso stampa:

('location', 'arrival', 'departure') 
('place1', None, '1000') # None in Python maps to NULL in sqlite3 
('place2', '1010', None)