ฉันจะรับ dict จากแบบสอบถาม sqlite ได้อย่างไร


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

ด้วยการทำซ้ำฉันจะได้รับรายการ coresponding ไปยังแถว

for row in res:
    print row

ฉันสามารถหาชื่อคอลัมน์ได้

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

แต่มีฟังก์ชันหรือการตั้งค่าบางอย่างเพื่อรับพจนานุกรมแทนรายการหรือไม่?

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

หรือต้องทำเอง?


เป็นไปได้ที่จะซ้ำกันของPython - mysqlDB ผลลัพธ์ sqlite เป็นพจนานุกรม
vy32

3
@ vy32: คำถามนี้มาจากเดือนกรกฎาคม 2010 คำถามที่คุณเชื่อมโยงคือพฤศจิกายน 2010 ดังนั้นคนที่หลอกลวง และตามที่คาดไว้ความคิดเห็นย้อนกลับได้ถูกใส่ไว้ที่ :-)
aneroid

คำตอบ:


160

คุณสามารถใช้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

@displayname ไม่ใช่สถานะเอกสาร "พยายามเลียนแบบทูเปิลในคุณลักษณะส่วนใหญ่" collections.namedtupleผมค่อนข้างมั่นใจว่ามันจะเป็นอย่างใดคล้ายกับสิ่งที่คุณจะได้รับจาก เมื่อฉันใช้cur.fetchmany()ฉันจะได้รับรายการเช่น<sqlite3.Row object at 0x...>.
ony

แม้ 7 ปีต่อมาคำตอบนี้เป็นสำเนาและวางที่มีประโยชน์ที่สุดจากเอกสารที่ฉันพบใน SO ขอบคุณ!
WillardSolutions

41

ฉันคิดว่าฉันตอบคำถามนี้แม้ว่าคำตอบจะถูกกล่าวถึงบางส่วนในทั้งคำตอบของ Adam Schmideg และ Alex Martelli เพื่อให้คนอื่น ๆ เช่นฉันที่มีคำถามเดียวกันสามารถหาคำตอบได้อย่างง่ายดาย

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()]
Gonçalo Ribeiro

21

แม้จะใช้คลาส sqlite3.Row คุณยังไม่สามารถใช้การจัดรูปแบบสตริงในรูปแบบของ:

print "%(id)i - %(name)s: %(value)s" % 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

9

หลังจากที่คุณเชื่อมต่อกับ SQLite: con = sqlite3.connect(.....)เพียงพอที่จะเรียกใช้:

con.row_factory = sqlite3.Row

Voila!


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 ...
Ignacio Vazquez-Abrams


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)

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)

คุณตัดสินใจ :)


2

ดังที่กล่าวไว้ในคำตอบของ @ แกนดัล์ฟต้องใช้conn.row_factory = sqlite3.Rowแต่ผลลัพธ์ไม่ใช่พจนานุกรมโดยตรง เราต้องเพิ่ม "cast" เพิ่มเติม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(ระบุไว้แล้วในคำตอบอื่น)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.