Как я могу получить dict из запроса sqlite?


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

С итерацией я получаю списки, соответствующие строкам.

for row in res:
    print row

Я могу получить название столбцов

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

Но есть ли какая-то функция или настройка для получения словарей вместо списка?

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

или я должен делать сам?



3
@ vy32: Этот вопрос от июля 2010 года, тот, с которым вы связались, - ноябрь 2010 года. Так что это обман. И, как и следовало ожидать, к этому добавлен обратный комментарий :-)
aneroid

Ответы:


159

Вы можете использовать row_factory , как в примере в документации:

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

или следуйте совету, который дан сразу после этого примера в документации:

Если возврата кортежа недостаточно и вам нужен доступ к столбцам на основе имени, вам следует подумать о настройке row_factory для высокооптимизированного типа sqlite3.Row. Row обеспечивает доступ к столбцам как на основе индексов, так и без учета регистра, практически без дополнительных затрат памяти. Вероятно, это будет лучше, чем ваш собственный подход на основе словаря или даже решение на основе db_row.


Если в именах столбцов есть специальные символы, например, SELECT 1 AS "dog[cat]"тогда у них cursorне будет правильного описания для создания словаря.
Crazometer

Я установил connection.row_factory = sqlite3.Rowи попробовал, connection.row_factory = dict_factoryкак показано, но cur.fetchall()все еще дает мне список кортежей - есть идеи, почему это не работает?
отображаемое имя

@displayname, в документации не говорится: «Он пытается имитировать кортеж в большинстве своих функций». Я почти уверен, что это чем-то похоже на то, что вы можете получить collections.namedtuple. Когда я использую, cur.fetchmany()я получаю такие записи, как <sqlite3.Row object at 0x...>.
оны

Даже 7 лет спустя этот ответ является наиболее полезным копированием и вставкой из документов, которые я нашел на SO. Спасибо!
WillardSolutions

41

Я думал, что отвечу на этот вопрос, хотя ответ частично упоминается как в ответах Адама Шмидега, так и Алекса Мартелли. Для того, чтобы такие же люди, как я, задавали тот же вопрос, легко могли найти ответ.

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
В настоящее время fetchall()кажется, что возвращает sqlite3.Rowобъекты. Однако они могут быть преобразованы в словарь , просто используя dict(): result = [dict(row) for row in c.fetchall()].
Гонсалу Рибейро

21

Даже используя класс sqlite3.Row - вы по-прежнему не можете использовать форматирование строк в виде:

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

Чтобы обойти это, я использую вспомогательную функцию, которая берет строку и преобразует ее в словарь. Я использую это только тогда, когда объект словаря предпочтительнее объекта Row (например, для таких вещей, как форматирование строки, где объект Row также не поддерживает API словаря). Но всегда используйте объект Row.

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

9
sqlite3.Row реализует протокол сопоставления. Вы можете просто сделатьprint "%(id)i - %(name)s: %(value)s" % dict(row)
Mzzzzzz


8

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

Так что да, сделай сам.


> варьируется между базами данных - что вроде, sqlite 3.7 и 3.8?
Nucular

@ user1123466: ... Как между SQLite, MySQL, Postgres, Oracle, MS SQL Server, Firebird ...
Игнасио Васкес-Абрамс,


3

Самый быстрый в моих тестах:

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)

против:

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)

Вам решать :)


2

Как упоминалось в ответе @gandalf, нужно использовать conn.row_factory = sqlite3.Row, но результаты не являются непосредственно словарями. В dictпоследнем цикле нужно добавить дополнительное "приведение" к :

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

Подобные решения, как и ранее упомянутые, но наиболее компактные:

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

Это сработало для меня, где приведенный выше ответ db.row_factory = sqlite3.Rowне сработал для меня (поскольку он привел к JSON TypeError)
Филипп

1

Я думаю, ты был на правильном пути. Давайте сделаем это очень простым и завершим то, что вы пытались сделать:

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)

Обратной стороной является то .fetchall(), что если ваша таблица очень большая , это убивает потребление памяти . Но для тривиальных приложений, работающих с несколькими тысячами строк текста и числовых столбцов, этого простого подхода достаточно.

Для серьезных вещей вам следует изучить фабрики строк, как это предлагается во многих других ответах.


0

Или вы можете преобразовать sqlite3.Rows в словарь следующим образом. Это даст словарь со списком для каждой строки.

    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

Общая альтернатива, использующая всего три строки

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

Но если ваш запрос ничего не вернет, это приведет к ошибке. В таком случае...

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

или

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)

Результат определенно верный, но лучшего я не знаю.


0

Словари в Python предоставляют произвольный доступ к своим элементам. Таким образом, любой словарь с «именами», хотя он может быть информативным, с одной стороны (например, каковы имена полей), «отменяет порядок» полей, что может быть нежелательным.

Лучше всего собрать имена в отдельный список, а затем при необходимости самостоятельно объединить их с результатами.

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

Также помните, что имена во всех подходах - это имена, которые вы указали в запросе, а не имена в базе данных. Исключение составляетSELECT * FROM

Если ваша единственная забота - получить результаты с помощью словаря, обязательно используйте conn.row_factory = sqlite3.Row(уже указано в другом ответе).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.