Bagaimana saya bisa mendapatkan dict dari kueri sqlite?


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

Dengan iterasi saya mendapatkan daftar yang sesuai dengan baris.

for row in res:
    print row

Saya bisa mendapatkan nama kolom

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

Tetapi apakah ada fungsi atau pengaturan untuk mendapatkan kamus daripada daftar?

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

atau saya harus melakukannya sendiri?



3
@ vy32: Pertanyaan ini dari Juli 2010, yang Anda tautkan adalah November 2010. Jadi yang itu si penipu. Dan seperti yang diharapkan, komentar terbalik telah diberikan pada yang itu :-)
aneroid

Jawaban:


158

Anda bisa menggunakan row_factory , seperti pada contoh di dokumen:

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

atau ikuti saran yang diberikan tepat setelah contoh ini di dokumen:

Jika mengembalikan tuple tidak cukup dan Anda menginginkan akses berbasis nama ke kolom, Anda harus mempertimbangkan untuk menyetel row_factory ke tipe sqlite3.Row yang sangat dioptimalkan. Row menyediakan akses berbasis nama berbasis indeks dan case-sensitive ke kolom dengan hampir tidak ada overhead memori. Ini mungkin akan lebih baik daripada pendekatan berbasis kamus khusus Anda sendiri atau bahkan solusi berbasis db_row.


Jika nama kolom Anda memiliki karakter khusus di dalamnya, misalnya, SELECT 1 AS "dog[cat]"maka cursortidak akan memiliki deskripsi yang benar untuk membuat dict.
Crazometer

Saya telah mengatur connection.row_factory = sqlite3.Rowdan saya mencoba connection.row_factory = dict_factoryseperti yang ditunjukkan tetapi cur.fetchall()masih memberi saya daftar tupel - tahu mengapa ini tidak berfungsi?
tampilan

@displayname, bukanlah status dokumentasi "Ia mencoba meniru tupel di sebagian besar fiturnya.". Saya cukup yakin itu entah bagaimana mirip dengan apa yang bisa Anda dapatkan collections.namedtuple. Ketika saya menggunakan cur.fetchmany()saya mendapatkan entri seperti <sqlite3.Row object at 0x...>.
ony

Bahkan 7 tahun kemudian, jawaban ini adalah salin dan tempel yang paling bermanfaat dari dokumen yang saya temukan di SO. Terima kasih!
WillardSolutions

40

Saya pikir saya menjawab pertanyaan ini meskipun jawabannya sebagian disebutkan dalam jawaban Adam Schmideg dan Alex Martelli. Agar orang lain seperti saya yang memiliki pertanyaan yang sama, dapat menemukan jawabannya dengan mudah.

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
Saat ini fetchall()tampaknya mengembalikan sqlite3.Rowobjek. Namun ini dapat dikonversi ke kamus hanya dengan menggunakan dict(): result = [dict(row) for row in c.fetchall()].
Gonçalo Ribeiro

21

Bahkan menggunakan sqlite3.Row class-- Anda masih tidak dapat menggunakan pemformatan string dalam bentuk:

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

Untuk melewati ini, saya menggunakan fungsi helper yang mengambil baris dan mengubahnya menjadi kamus. Saya hanya menggunakan ini ketika objek kamus lebih disukai daripada objek Row (misalnya untuk hal-hal seperti pemformatan string di mana objek Row tidak secara native mendukung API kamus juga). Tapi gunakan objek Row di lain waktu.

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

9
sqlite3.Row mengimplementasikan protokol pemetaan. Anda bisa melakukannyaprint "%(id)i - %(name)s: %(value)s" % dict(row)
Mzzzzzz

9

Setelah Anda terhubung ke SQLite: con = sqlite3.connect(.....)cukup dengan menjalankan:

con.row_factory = sqlite3.Row

Voila!


8

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

Jadi ya, lakukan sendiri.


> bervariasi antar database - seperti apa, sqlite 3.7 dan 3.8?
Nucular

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

3

Versi lebih pendek:

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

3

Tercepat dalam pengujian saya:

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)

Kamu putuskan :)


2

Seperti yang disebutkan oleh jawaban @ gandalf, harus menggunakan conn.row_factory = sqlite3.Row, tetapi hasilnya tidak langsung kamus. Seseorang harus menambahkan "cast" tambahan dictdi loop terakhir:

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

Mirip seperti solusi yang disebutkan sebelumnya, tetapi paling ringkas:

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

Ini berhasil untuk saya, di mana jawaban di atas db.row_factory = sqlite3.Rowtidak berhasil untuk saya (karena menghasilkan JSON TypeError)
Phillip

1

Saya pikir Anda berada di jalur yang benar. Mari kita pertahankan ini sangat sederhana dan selesaikan apa yang Anda coba lakukan:

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)

Sisi negatifnya adalah .fetchall(), yang membunuh konsumsi memori Anda , jika meja Anda sangat besar. Tetapi untuk aplikasi sepele yang berurusan dengan hanya beberapa ribu baris teks dan kolom numerik, pendekatan sederhana ini sudah cukup baik.

Untuk hal-hal serius, Anda harus melihat pabrik baris, seperti yang diusulkan dalam banyak jawaban lain.


0

Atau Anda dapat mengonversi sqlite3. Baris ke kamus sebagai berikut. Ini akan memberikan kamus dengan daftar untuk setiap baris.

    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

Alternatif umum, hanya menggunakan tiga baris

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

Tetapi jika kueri Anda tidak mengembalikan apa-apa, akan menghasilkan kesalahan. Pada kasus ini...

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

atau

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)

Hasilnya pasti benar, tapi saya tidak tahu yang terbaik.


0

Kamus dalam python menyediakan akses sewenang-wenang ke elemen mereka. Jadi setiap kamus dengan "nama" meskipun mungkin informatif di satu sisi (alias apa nama bidang) "membatalkan pesanan" bidang, yang mungkin tidak diinginkan.

Pendekatan terbaik adalah memasukkan nama dalam daftar terpisah dan kemudian menggabungkannya dengan hasil sendiri, jika diperlukan.

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

Ingat juga bahwa nama, dalam semua pendekatan, adalah nama yang Anda berikan dalam kueri, bukan nama dalam database. Pengecualian adalahSELECT * FROM

Jika satu-satunya perhatian Anda adalah mendapatkan hasil menggunakan kamus, maka pasti gunakan conn.row_factory = sqlite3.Row(sudah dinyatakan di jawaban lain).

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.