【问题标题】:How can I get dict from sqlite query?如何从 sqlite 查询中获取 dict?
【发布时间】:2011-03-19 01:06:14
【问题描述】:
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'}

还是我自己做?

【问题讨论】:

  • @vy32:这个问题来自 2010 年 7 月,您链接到的问题是 2010 年 11 月。所以这是骗子。正如人们所期望的那样,已经对那个发表了相反的评论:-)

标签: python sql sqlite dictionary dataformat


【解决方案1】:

来自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?
  • @user1123466: ...就像在 SQLite、MySQL、Postgres、Oracle、MS SQL Server、Firebird 之间...
【解决方案2】:

您可以使用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 类型。行同时提供 基于索引且不区分大小写 基于名称的列访问 几乎没有内存开销。它会 可能比你自己的好 自定义基于字典的方法或 甚至是基于 db_row 的解决方案。

这是第二种解决方案的代码:

con.row_factory = sqlite3.Row

【讨论】:

  • 如果您的列名中有特殊字符,例如SELECT 1 AS "dog[cat]",那么cursor 将没有正确的描述来创建字典。
  • 我已经设置了connection.row_factory = sqlite3.Row,并尝试了connection.row_factory = dict_factory,但cur.fetchall() 仍然给我一个元组列表——知道为什么这不起作用吗?
  • @displayname,不是文档说明“它试图在其大部分功能中模仿一个元组。”。我很确定它与您可以从collections.namedtuple 获得的内容在某种程度上相似。当我使用cur.fetchmany() 时,我会得到类似<sqlite3.Row object at 0x...> 的条目。
  • 即使 7 年后,这个答案也是我在 SO 上找到的文档中最有用的复制和粘贴。谢谢!
【解决方案3】:

即使使用 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))       

【讨论】:

  • sqlite3.Row 实现了映射协议。你可以做print "%(id)i - %(name)s: %(value)s" % dict(row)
【解决方案4】:

或者您可以将 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

【讨论】:

    【解决方案5】:

    一个通用的替代方案,只使用三行

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

    【讨论】:

      【解决方案6】:

      我想我会回答这个问题,尽管 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
      

      【讨论】:

      • 目前fetchall() 似乎返回sqlite3.Row 对象。然而,这些可以简单地通过使用 dict(): result = [dict(row) for row in c.fetchall()] 转换为字典。
      【解决方案7】:

      短版:

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

      【讨论】:

        【解决方案8】:
        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)
        

        结果肯定是真的,但我不知道最好的。

        【讨论】:

          【解决方案9】:

          我的测试中最快的:

          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)
          

          你决定:)

          【讨论】:

            【解决方案10】:

            类似于前面提到的解决方案,但最紧凑:

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

            【讨论】:

            • 这对我有用,上面的答案 db.row_factory = sqlite3.Row 对我不起作用(因为它导致 JSON TypeError)
            【解决方案11】:

            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(已在另一个答案中说明)。

            【讨论】:

              【解决方案12】:

              连接到 SQLite 后: con = sqlite3.connect(.....) 运行就足够了:

              con.row_factory = sqlite3.Row
              

              瞧!

              【讨论】:

                【解决方案13】:

                正如@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'}
                

                【讨论】:

                  【解决方案14】:

                  我认为你是在正确的轨道上。让我们保持这个非常简单并完成您想要做的事情:

                  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(),如果您的表非常大,这会消耗您的内存。但对于仅处理几千行文本和数字列的普通应用程序,这种简单的方法就足够了。

                  对于严肃的事情,您应该研究行工厂,正如许多其他答案中所建议的那样。

                  【讨论】:

                    【解决方案15】:

                    获取查询结果

                    output_obj = con.execute(query)
                    results = output_obj.fetchall()
                    

                    选项 1) 带 Zip 的显式循环

                    for row in results:
                        col_names = [tup[0] for tup in output_obj.description]
                        row_values = [i for i in row]
                        row_as_dict = dict(zip(col_names,row_values))
                    

                    选项 2) 更快的循环 w/Dict Comp

                    for row in results:
                        row_as_dict = {output_obj.description[i][0]:row[i] for i in range(len(row))}
                    

                    【讨论】:

                      猜你喜欢
                      • 2018-06-08
                      • 1970-01-01
                      • 2017-05-05
                      • 2014-07-22
                      • 2016-11-25
                      • 2014-01-01
                      • 2014-10-26
                      • 2011-04-10
                      • 1970-01-01
                      相关资源
                      最近更新 更多