¿Cómo puedo obtener dict de la consulta sqlite?

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

Con la iteración, obtengo listas que corresponden a las filas.

for row in res:
    print row

Puedo obtener el nombre de las columnas

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

Pero, ¿hay alguna función o configuración para obtener diccionarios en lugar de listas?

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

o tengo que hacerlo yo mismo?

Meloun
fuente
3
@ vy32: Esta pregunta es de julio de 2010, la que vinculó es noviembre de 2010. Así que ese es el engañado. Y como era de esperar, el comentario inverso se ha puesto en ese :-)
aneroide

Respuestas:

158

Puede usar row_factory , como en el ejemplo de los documentos:

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 siga los consejos que se dan justo después de este ejemplo en los documentos:

Si devolver una tupla no es suficiente y desea acceso basado en el nombre a las columnas, debería considerar establecer row_factory en el tipo sqlite3.Row altamente optimizado. Row proporciona acceso a columnas basado en índices y sin distinción entre mayúsculas y minúsculas, casi sin sobrecarga de memoria. Probablemente sea mejor que su propio enfoque basado en diccionario personalizado o incluso una solución basada en db_row.

Alex Martelli
fuente
Si los nombres de sus columnas tienen caracteres especiales, por ejemplo, SELECT 1 AS "dog[cat]"entonces cursorno tendrán la descripción correcta para crear un diccionario.
Crazometer
Lo configuré connection.row_factory = sqlite3.Rowe intenté connection.row_factory = dict_factorycomo se muestra, pero cur.fetchall()todavía me está dando una lista de tuplas, ¿alguna idea de por qué esto no funciona?
displayname
@displayname, no es la documentación que dice "Intenta imitar una tupla en la mayoría de sus características". Estoy bastante seguro de que de alguna manera es similar a lo que puede obtener collections.namedtuple. Cuando lo uso cur.fetchmany(), obtengo entradas como <sqlite3.Row object at 0x...>.
ony
Incluso 7 años después, esta respuesta es la copia y pegado más útil de los documentos que encontré en SO. ¡Gracias!
WillardSolutions
40

Pensé que respondía a esta pregunta a pesar de que la respuesta se menciona en parte en las respuestas de Adam Schmideg y Alex Martelli. Para que otros como yo que tienen la misma pregunta, encuentren la respuesta fácilmente.

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
Gandalf
fuente
21
Actualmente fetchall()parece devolver sqlite3.Rowobjetos. Sin embargo, estos pueden ser convertidos en un diccionario simplemente usando dict(): result = [dict(row) for row in c.fetchall()].
Gonçalo Ribeiro
21

Incluso usando la clase sqlite3.Row, todavía no puede usar el formato de cadena en forma de:

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

Para superar esto, utilizo una función auxiliar que toma la fila y la convierte en un diccionario. Solo uso esto cuando el objeto del diccionario es preferible al objeto Fila (por ejemplo, para cosas como el formato de cadena donde el objeto Fila no es compatible de forma nativa con la API del diccionario). Pero use el objeto Row el resto de veces.

def dict_from_row(row):
    return dict(zip(row.keys(), row))       
bbengfort
fuente
9
sqlite3.Row implementa el protocolo de mapeo. Puedes hacerloprint "%(id)i - %(name)s: %(value)s" % dict(row)
Mzzzzzz
9

Después de conectarse a SQLite: con = sqlite3.connect(.....)basta con ejecutar:

con.row_factory = sqlite3.Row

¡Voila!

Lukasz Czerwinski
fuente
8

Desde 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.

Así que sí, hazlo tú mismo.

Ignacio Vázquez-Abrams
fuente
> varía entre las bases de datos, ¿como qué, sqlite 3.7 y 3.8?
Nucular
@ user1123466: ... Como entre SQLite, MySQL, Postgres, Oracle, MS SQL Server, Firebird ...
Ignacio Vazquez-Abrams
3

Versión más corta:

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

Más rápido en mis pruebas:

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)

Tú decides :)

Ran Aroussi
fuente
1

Soluciones similares a las mencionadas anteriormente, pero más compactas:

db.row_factory = lambda C, R: { c[0]: R[i] for i, c in enumerate(C.description) }
Falko
fuente
Esto funcionó para mí, donde las respuestas anteriores db.row_factory = sqlite3.Rowno funcionaron para mí (ya que resultó en un JSON TypeError)
Phillip
1

Como se menciona en la respuesta de @gandalf, uno tiene que usar conn.row_factory = sqlite3.Row, pero los resultados no son directamente diccionarios. Uno tiene que agregar un "elenco" adicional dicten el último bucle:

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'}
Basj
fuente
1

Creo que estabas en el camino correcto. Mantengamos esto muy simple y completemos lo que estaba tratando de hacer:

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)

La desventaja es que .fetchall(), que es un asesinato en el consumo de memoria , si su mesa es muy grande. Pero para aplicaciones triviales que se ocupan de unos pocos miles de filas de texto y columnas numéricas, este enfoque simple es suficientemente bueno.

Para cosas serias, debe buscar en las fábricas de filas, como se propone en muchas otras respuestas.

Tammi
fuente
0

O puede convertir sqlite3.Rows en un diccionario de la siguiente manera. Esto le dará un diccionario con una lista para cada fila.

    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
andere
fuente
0

Una alternativa genérica, usando solo tres líneas

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,)))

Pero si su consulta no devuelve nada, resultará en un error. En este 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))}
macabeo
fuente
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)

El resultado es definitivamente cierto, pero no sé cuál es el mejor.

Emrah Tuncel
fuente
0

Los diccionarios en Python proporcionan acceso arbitrario a sus elementos. Por lo tanto, cualquier diccionario con "nombres", aunque podría ser informativo por un lado (también conocido como los nombres de los campos) "desordena" los campos, lo que podría no ser deseado.

El mejor enfoque es obtener los nombres en una lista separada y luego combinarlos con los resultados usted mismo, si es necesario.

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

También recuerde que los nombres, en todos los enfoques, son los nombres que proporcionó en la consulta, no los nombres en la base de datos. La excepción es laSELECT * FROM

Si su única preocupación es obtener los resultados usando un diccionario, definitivamente use el conn.row_factory = sqlite3.Row(ya mencionado en otra respuesta).

ilias iliadis
fuente