Come posso ottenere dict da sqlite query?


118
db = sqlite.connect("test.sqlite")
res = db.execute("select * from table")

Con l'iterazione ottengo elenchi corrispondenti alle righe.

for row in res:
    print row

Posso ottenere il nome delle colonne

col_name_list = [tuple[0] for tuple in res.description]

Ma c'è qualche funzione o impostazione per ottenere dizionari invece di elenco?

{'col1': 'value', 'col2': 'value'}

o devo fare io?



3
@ vy32: questa domanda è del luglio 2010, quella a cui ti sei collegato è del novembre 2010. Quindi quella è la vittima. E come ci si aspetterebbe, è stato inserito il commento inverso su quello :-)
aneroid

Risposte:


158

Potresti usare row_factory , come nell'esempio nei documenti:

import sqlite3

def dict_factory(cursor, row):
    d = {}
    for idx, col in enumerate(cursor.description):
        d[col[0]] = row[idx]
    return d

con = sqlite3.connect(":memory:")
con.row_factory = dict_factory
cur = con.cursor()
cur.execute("select 1 as a")
print cur.fetchone()["a"]

o segui il consiglio che viene fornito subito dopo questo esempio nei documenti:

Se la restituzione di una tupla non è sufficiente e si desidera un accesso basato sul nome alle colonne, è necessario considerare l'impostazione di row_factory sul tipo sqlite3.Row altamente ottimizzato. Row fornisce accesso basato su nome e senza distinzione tra maiuscole e minuscole alle colonne con quasi nessun sovraccarico di memoria. Probabilmente sarà migliore del tuo approccio basato sul dizionario personalizzato o anche di una soluzione basata su db_row.


Se i nomi di colonna hanno caratteri speciali in loro ad esempio SELECT 1 AS "dog[cat]", allora il cursornon avrà la descrizione corretta per creare un dict.
Crazometer

Ho impostato connection.row_factory = sqlite3.Rowe provato connection.row_factory = dict_factorycome mostrato, ma cur.fetchall()mi sta ancora dando un elenco di tuple - qualche idea del perché non funziona?
displayname

@displayname, non è documentazione afferma "Cerca di imitare una tupla nella maggior parte delle sue funzionalità.". Sono abbastanza sicuro che sia in qualche modo simile a ciò da cui puoi ottenere collections.namedtuple. Quando uso cur.fetchmany()ottengo voci come <sqlite3.Row object at 0x...>.
ony

Anche 7 anni dopo, questa risposta è il copia e incolla più utile dai documenti che ho trovato su SO. Grazie!
WillardSolutions

40

Pensavo di rispondere a questa domanda anche se la risposta è in parte menzionata sia nelle risposte di Adam Schmideg che di Alex Martelli. Affinché altri come me che hanno la stessa domanda, trovino facilmente la risposta.

conn = sqlite3.connect(":memory:")

#This is the important part, here we are setting row_factory property of
#connection object to sqlite3.Row(sqlite3.Row is an implementation of
#row_factory)
conn.row_factory = sqlite3.Row
c = conn.cursor()
c.execute('select * from stocks')

result = c.fetchall()
#returns a list of dictionaries, each item in list(each dictionary)
#represents a row of the table

21
Attualmente fetchall()sembra restituire sqlite3.Rowoggetti. Tuttavia questi possono essere convertiti in un dizionario semplicemente utilizzando dict(): result = [dict(row) for row in c.fetchall()].
Gonçalo Ribeiro

21

Anche usando la classe sqlite3.Row, non puoi ancora usare la formattazione delle stringhe sotto forma di:

print "%(id)i - %(name)s: %(value)s" % row

Per superare questo problema, utilizzo una funzione di supporto che prende la riga e la converte in un dizionario. Lo uso solo quando l'oggetto dizionario è preferibile all'oggetto Row (ad esempio per cose come la formattazione di stringhe in cui l'oggetto Row non supporta nativamente anche l'API del dizionario). Ma usa l'oggetto Row tutte le altre volte.

def dict_from_row(row):
    return dict(zip(row.keys(), row))       

9
sqlite3.Row implementa il protocollo di mappatura. Puoi semplicemente farloprint "%(id)i - %(name)s: %(value)s" % dict(row)
Mzzzzzz

9

Dopo esserti connesso a SQLite: con = sqlite3.connect(.....)è sufficiente eseguire:

con.row_factory = sqlite3.Row

Ecco!


8

Da PEP 249 :

Question: 

   How can I construct a dictionary out of the tuples returned by
   .fetch*():

Answer:

   There are several existing tools available which provide
   helpers for this task. Most of them use the approach of using
   the column names defined in the cursor attribute .description
   as basis for the keys in the row dictionary.

   Note that the reason for not extending the DB API specification
   to also support dictionary return values for the .fetch*()
   methods is that this approach has several drawbacks:

   * Some databases don't support case-sensitive column names or
     auto-convert them to all lowercase or all uppercase
     characters.

   * Columns in the result set which are generated by the query
     (e.g.  using SQL functions) don't map to table column names
     and databases usually generate names for these columns in a
     very database specific way.

   As a result, accessing the columns through dictionary keys
   varies between databases and makes writing portable code
   impossible.

Quindi sì, fallo da solo.


> varia tra i database - come cosa, sqlite 3.7 e 3.8?
Nucular

@ user1123466: ... Come tra SQLite, MySQL, Postgres, Oracle, MS SQL Server, Firebird ...
Ignacio Vazquez-Abrams

3

Versione più breve:

db.row_factory = lambda c, r: dict([(col[0], r[idx]) for idx, col in enumerate(c.description)])

3

Il più veloce nei miei test:

conn.row_factory = lambda c, r: dict(zip([col[0] for col in c.description], r))
c = conn.cursor()

%timeit c.execute('SELECT * FROM table').fetchall()
19.8 µs ± 1.05 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)

vs:

conn.row_factory = lambda c, r: dict([(col[0], r[idx]) for idx, col in enumerate(c.description)])
c = conn.cursor()

%timeit c.execute('SELECT * FROM table').fetchall()
19.4 µs ± 75.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Tu decidi :)


2

Come accennato dalla risposta di @ gandalf, bisogna usare conn.row_factory = sqlite3.Row, ma i risultati non sono direttamente dizionari. È necessario aggiungere un ulteriore "cast" dictnell'ultimo ciclo:

import sqlite3
conn = sqlite3.connect(":memory:")
conn.execute('create table t (a text, b text, c text)')
conn.execute('insert into t values ("aaa", "bbb", "ccc")')
conn.execute('insert into t values ("AAA", "BBB", "CCC")')
conn.row_factory = sqlite3.Row
c = conn.cursor()
c.execute('select * from t')
for r in c.fetchall():
    print(dict(r))

# {'a': 'aaa', 'b': 'bbb', 'c': 'ccc'}
# {'a': 'AAA', 'b': 'BBB', 'c': 'CCC'}

1

Simili alle soluzioni sopra menzionate, ma più compatte:

db.row_factory = lambda C, R: { c[0]: R[i] for i, c in enumerate(C.description) }

Questo ha funzionato per me, dove la risposta di cui sopra db.row_factory = sqlite3.Rownon ha funzionato per me (poiché ha prodotto un errore di tipo JSON)
Phillip

1

Penso che tu fossi sulla strada giusta. Manteniamo questo molto semplice e completiamo ciò che stavi cercando di fare:

import sqlite3
db = sqlite3.connect("test.sqlite3")
cur = db.cursor()
res = cur.execute("select * from table").fetchall()
data = dict(zip([c[0] for c in cur.description], res[0]))

print(data)

Il rovescio della medaglia è quello .fetchall(), che è un omicidio sul tuo consumo di memoria , se il tuo tavolo è molto grande. Ma per applicazioni banali che si occupano di poche migliaia di righe di testo e colonne numeriche, questo semplice approccio è abbastanza buono.

Per cose serie, dovresti esaminare le fabbriche di righe, come proposto in molte altre risposte.


0

Oppure puoi convertire sqlite3.Rows in un dizionario come segue. Questo darà un dizionario con un elenco per ogni riga.

    def from_sqlite_Row_to_dict(list_with_rows):
    ''' Turn a list with sqlite3.Row objects into a dictionary'''
    d ={} # the dictionary to be filled with the row data and to be returned

    for i, row in enumerate(list_with_rows): # iterate throw the sqlite3.Row objects            
        l = [] # for each Row use a separate list
        for col in range(0, len(row)): # copy over the row date (ie. column data) to a list
            l.append(row[col])
        d[i] = l # add the list to the dictionary   
    return d

0

Un'alternativa generica, utilizzando solo tre righe

def select_column_and_value(db, sql, parameters=()):
    execute = db.execute(sql, parameters)
    fetch = execute.fetchone()
    return {k[0]: v for k, v in list(zip(execute.description, fetch))}

con = sqlite3.connect('/mydatabase.db')
c = con.cursor()
print(select_column_and_value(c, 'SELECT * FROM things WHERE id=?', (id,)))

Ma se la tua query non restituisce nulla, si verificherà un errore. In questo caso...

def select_column_and_value(self, sql, parameters=()):
    execute = self.execute(sql, parameters)
    fetch = execute.fetchone()

    if fetch is None:
        return {k[0]: None for k in execute.description}

    return {k[0]: v for k, v in list(zip(execute.description, fetch))}

o

def select_column_and_value(self, sql, parameters=()):
    execute = self.execute(sql, parameters)
    fetch = execute.fetchone()

    if fetch is None:
        return {}

    return {k[0]: v for k, v in list(zip(execute.description, fetch))}

0
import sqlite3

db = sqlite3.connect('mydatabase.db')
cursor = db.execute('SELECT * FROM students ORDER BY CREATE_AT')
studentList = cursor.fetchall()

columnNames = list(map(lambda x: x[0], cursor.description)) #students table column names list
studentsAssoc = {} #Assoc format is dictionary similarly


#THIS IS ASSOC PROCESS
for lineNumber, student in enumerate(studentList):
    studentsAssoc[lineNumber] = {}

    for columnNumber, value in enumerate(student):
        studentsAssoc[lineNumber][columnNames[columnNumber]] = value


print(studentsAssoc)

Il risultato è sicuramente vero, ma non conosco il migliore.


0

I dizionari in Python forniscono accesso arbitrario ai loro elementi. Quindi qualsiasi dizionario con "nomi", sebbene possa essere informativo da un lato (ovvero quali sono i nomi dei campi) "annulla l'ordine" dei campi, il che potrebbe essere indesiderato.

L'approccio migliore è ottenere i nomi in un elenco separato e quindi combinarli con i risultati da soli, se necessario.

try:
         mycursor = self.memconn.cursor()
         mycursor.execute('''SELECT * FROM maintbl;''')
         #first get the names, because they will be lost after retrieval of rows
         names = list(map(lambda x: x[0], mycursor.description))
         manyrows = mycursor.fetchall()

         return manyrows, names

Ricorda anche che i nomi, in tutti gli approcci, sono i nomi forniti nella query, non i nomi nel database. L'eccezione è il fileSELECT * FROM

Se la tua unica preoccupazione è ottenere i risultati utilizzando un dizionario, usa sicuramente il conn.row_factory = sqlite3.Row(già indicato in un'altra risposta).

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.