Como posso obter o dict da consulta sqlite?

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

Com a iteração, obtenho listas correspondentes às linhas.

for row in res:
    print row

Posso obter o nome das colunas

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

Mas existe alguma função ou configuração para obter dicionários em vez de lista?

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

ou eu tenho que fazer sozinho?

Meloun
fonte
3
@ vy32: Esta pergunta é de julho de 2010, a que você vinculou é novembro de 2010. Então esse é o idiota. E como seria de se esperar, o comentário inverso foi colocado sobre aquele :-)
aneróide

Respostas:

158

Você pode usar row_factory , como no exemplo da documentação:

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

ou siga o conselho fornecido logo após este exemplo nos documentos:

Se retornar uma tupla não for suficiente e você quiser acesso baseado em nome para colunas, você deve considerar configurar row_factory para o tipo sqlite3.Row altamente otimizado. Row fornece acesso baseado em índice e sem distinção entre maiúsculas e minúsculas a colunas com quase nenhuma sobrecarga de memória. Provavelmente será melhor do que sua própria abordagem baseada em dicionário personalizado ou mesmo uma solução baseada em db_row.

Alex Martelli
fonte
Se os nomes das colunas tiverem caracteres especiais, por exemplo SELECT 1 AS "dog[cat]", o cursornão terá a descrição correta para criar um dicionário.
Crazometer de
Eu configurei connection.row_factory = sqlite3.Rowe tentei connection.row_factory = dict_factoryconforme mostrado, mas cur.fetchall()ainda está me dando uma lista de tuplas - alguma ideia de por que isso não está funcionando?
displayname
@displayname, não afirma a documentação "Ele tenta imitar uma tupla na maioria de seus recursos.". Tenho certeza de que é de alguma forma semelhante ao que você pode obter collections.namedtuple. Quando eu uso cur.fetchmany(), recebo entradas como <sqlite3.Row object at 0x...>.
ony
Mesmo 7 anos depois, esta resposta é o copiar e colar mais útil dos documentos que encontrei no SO. Obrigado!
WillardSolutions
40

Achei que tivesse respondido a essa pergunta, embora a resposta seja parcialmente mencionada nas respostas de Adam Schmideg e Alex Martelli. Para que outros como eu que tenham a mesma pergunta, encontrem a resposta facilmente.

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
fonte
21
Atualmente fetchall()parece retornar sqlite3.Rowobjetos. No entanto, estes podem ser convertidos em um dicionário simplesmente usando dict(): result = [dict(row) for row in c.fetchall()].
Gonçalo Ribeiro
21

Mesmo usando a classe sqlite3.Row - você ainda não pode usar a formatação de string na forma de:

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

Para superar isso, uso uma função auxiliar que pega a linha e converte em um dicionário. Eu só uso isso quando o objeto de dicionário é preferível ao objeto Row (por exemplo, para coisas como formatação de string, onde o objeto Row não suporta nativamente a API do dicionário também). Mas use o objeto Row todas as outras vezes.

def dict_from_row(row):
    return dict(zip(row.keys(), row))       
bbengfort
fonte
9
sqlite3.Row implementa o protocolo de mapeamento. Você pode simplesmente fazerprint "%(id)i - %(name)s: %(value)s" % dict(row)
Mzzzzzz
9

Depois de se conectar ao SQLite: con = sqlite3.connect(.....)é suficiente apenas executar:

con.row_factory = sqlite3.Row

Voila!

Lukasz Czerwinski
fonte
8

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

Então, sim, faça você mesmo.

Ignacio Vazquez-Abrams
fonte
> varia entre bancos de dados - como o que, sqlite 3.7 e 3.8?
Nucular
@ user1123466: ... Como entre SQLite, MySQL, Postgres, Oracle, MS SQL Server, Firebird ...
Ignacio Vazquez-Abrams
3

Versão mais curta:

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

O mais rápido em meus testes:

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)

Você decide :)

Ran Aroussi
fonte
1

Soluções semelhantes às mencionadas antes, mas mais compactas:

db.row_factory = lambda C, R: { c[0]: R[i] for i, c in enumerate(C.description) }
Falko
fonte
Isso funcionou para mim, onde a resposta acima db.row_factory = sqlite3.Rownão funcionou para mim (pois resultou em um JSON TypeError)
Phillip
1

Conforme mencionado pela resposta de @gandalf, deve-se usar conn.row_factory = sqlite3.Row, mas os resultados não são dicionários diretos . É necessário adicionar um "elenco" adicional ao dictúltimo loop:

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
fonte
1

Acho que você estava no caminho certo. Vamos manter isso muito simples e completo o que você estava tentando fazer:

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)

A desvantagem é que .fetchall(), se a sua mesa for muito grande, o consumo de memória é um assassinato . Mas para aplicativos triviais que lidam com meros poucos milhares de linhas de texto e colunas numéricas, essa abordagem simples é boa o suficiente.

Para assuntos sérios, você deve olhar para as fábricas em linha, como proposto em muitas outras respostas.

Tammi
fonte
0

Ou você pode converter sqlite3.Rows em um dicionário da seguinte maneira. Isso fornecerá um dicionário com uma lista para cada linha.

    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
fonte
0

Uma alternativa genérica, usando apenas três linhas

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

Mas se sua consulta não retornar nada, resultará em erro. Nesse 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))}

ou

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))}
macabeus
fonte
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)

O resultado é definitivamente verdadeiro, mas não conheço o melhor.

Emrah Tuncel
fonte
0

Dicionários em python fornecem acesso arbitrário aos seus elementos. Portanto, qualquer dicionário com "nomes", embora possa ser informativo por um lado (também conhecido como quais são os nomes de campo), "desordena" os campos, o que pode ser indesejado.

A melhor abordagem é colocar os nomes em uma lista separada e combiná-los com os resultados você mesmo, se necessário.

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

Lembre-se também de que os nomes, em todas as abordagens, são os nomes que você forneceu na consulta, não os nomes no banco de dados. A exceção é oSELECT * FROM

Se sua única preocupação é obter os resultados usando um dicionário, então, definitivamente, use o conn.row_factory = sqlite3.Row(já declarado em outra resposta).

ilias iliadis
fonte