将pyodbc游标结果输出为python字典


Answers:


159

如果您不提前知道列,请使用Cursor.description生成列名列表,并在每行压缩后生成字典列表。该示例假定已建立连接和查询:

>>> cursor = connection.cursor().execute(sql)
>>> columns = [column[0] for column in cursor.description]
>>> print(columns)
['name', 'create_date']
>>> results = []
>>> for row in cursor.fetchall():
...     results.append(dict(zip(columns, row)))
...
>>> print(results)
[{'create_date': datetime.datetime(2003, 4, 8, 9, 13, 36, 390000), 'name': u'master'},   
 {'create_date': datetime.datetime(2013, 1, 30, 12, 31, 40, 340000), 'name': u'tempdb'},
 {'create_date': datetime.datetime(2003, 4, 8, 9, 13, 36, 390000), 'name': u'model'},     
 {'create_date': datetime.datetime(2010, 4, 2, 17, 35, 8, 970000), 'name': u'msdb'}]

1
不知道cursor.description。这只是节省了我很多时间。
TehTris 2015年

需要在打印(列)和打印(结果)
两边

2
@LJT仅在python3中...但是由于print()函数在python2中有效,因此使用它是一个好习惯。
Auspex

1
@BenLutgens因为该示例生成的是字典列表,而不是字典。
Auspex

1
更新:默认情况下,pypyodbc设置小写= True。您可以这样覆盖:import pypyodbc; pypyodbc.lowercase = False。参考:链接
Guoweihui

13

通过将@Beargle的结果与bottlepy配合使用,我能够创建此非常简洁的查询公开端点:

@route('/api/query/<query_str>')
def query(query_str):
    cursor.execute(query_str)
    return {'results':
            [dict(zip([column[0] for column in cursor.description], row))
             for row in cursor.fetchall()]}

2
这会遭受SQL注入攻击吗?

@本是的!除非您1000%确保请求始终来自受信任的客户端,否则永远不要使用它。
Bora M. Alper

6

这是您可能可以使用的简短版本

>>> cursor.select("<your SQL here>")
>>> single_row = dict(zip(zip(*cursor.description)[0], cursor.fetchone()))
>>> multiple_rows = [dict(zip(zip(*cursor.description)[0], row)) for row in cursor.fetchall()]

您可能会知道,当在列表中添加*时,您基本上会删除列表,而将各个列表条目保留为所调用函数的参数。通过使用zip拉链,我们选择了第1到n项,并将它们拉链在一起,就像裤子中的拉链一样。

所以用

zip(*[(a,1,2),(b,1,2)])
# interpreted by python as zip((a,1,2),(b,1,2))

你得到

[('a', 'b'), (1, 1), (2, 2)]

由于description是带有元组的元组,其中每个元组都描述了每一列的标题和数据类型,因此您可以使用以下命令提取每个元组的第一个

>>> columns = zip(*cursor.description)[0]

相当于

>>> columns = [column[0] for column in cursor.description]

使用python3.4,我得到了:TypeError: 'zip' object is not subscriptable,所以我不能使用这个zip(*description)[0]技巧。
malat

在python 3.4中,zip是一个迭代器。您可以将zip包裹在列表中(zip(* description))[0] @malat
Tommy Strand

您保存了一行带有columns变量的行,但通过分别计算每一行的列名来乘以函数的复杂性
Sergey Nudnov

3

主要从@Torxed响应开始,我创建了一组全面的函数集,以将模式和数据查找到字典中:

def schema_dict(cursor):
    cursor.execute("SELECT sys.objects.name, sys.columns.name FROM sys.objects INNER JOIN sys.columns ON sys.objects.object_id = sys.columns. object_id WHERE sys.objects.type = 'U';")
    schema = {}

    for it in cursor.fetchall():
        if it[0] not in schema:
            schema[it[0]]={'scheme':[]}
        else:
            schema[it[0]]['scheme'].append(it[1])

    return schema


def populate_dict(cursor, schema):
    for i in schema.keys():
        cursor.execute("select * from {table};".format(table=i))

        for row in cursor.fetchall():
            colindex = 0

            for col in schema[i]['scheme']:
                if not 'data' in schema[i]:
                    schema[i]['data']=[]

                schema[i]['data'].append(row[colindex])
                colindex += 1

    return schema

def database_to_dict():
    cursor = connect()
    schema = populate_dict(cursor, schema_dict(cursor))

随意使用所有代码,以减少行数;但与此同时,它可行!

;)


2

对于游标不可用的情况-例如,当某些函数调用或内部方法返回了行时,您仍然可以使用row.cursor_description创建字典表示形式

def row_to_dict(row):
    return dict(zip([t[0] for t in row.cursor_description], row))

1

我知道这个问题很旧,但是它帮助我弄清楚如何做我需要的事情,这与OP的要求略有不同,因此我想与大家分享,以帮助需要我需要的其他任何人:您想完全概括执行SQL Select查询的例程,但是您需要通过索引号而不是名称来引用结果,您可以使用列表列表而不是字典来执行此操作。返回数据的每一行在返回列表中表示为字段(列)值的列表。可以将列名作为返回列表的第一个条目提供,因此在调用例程中解析返回的列表确实非常容易和灵活。这样,执行数据库调用的例程无需了解有关其正在处理的数据的任何信息。这是一个例程:

    def read_DB_Records(self, tablename, fieldlist, wherefield, wherevalue) -> list:

        DBfile = 'C:/DATA/MyDatabase.accdb'
        # this connection string is for Access 2007, 2010 or later .accdb files
        conn = pyodbc.connect(r'Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ='+DBfile)
        cursor = conn.cursor()

        # Build the SQL Query string using the passed-in field list:
        SQL = "SELECT "
        for i in range(0, len(fieldlist)):
            SQL = SQL + "[" + fieldlist[i] + "]"
            if i < (len(fieldlist)-1):
                SQL = SQL + ", "
        SQL = SQL + " FROM " + tablename

        # Support an optional WHERE clause:
        if wherefield != "" and wherevalue != "" :
            SQL = SQL + " WHERE [" + wherefield + "] = " + "'" + wherevalue + "';"

        results = []    # Create the results list object

        cursor.execute(SQL) # Execute the Query

        # (Optional) Get a list of the column names returned from the query:
        columns = [column[0] for column in cursor.description]
        results.append(columns) # append the column names to the return list

        # Now add each row as a list of column data to the results list
        for row in cursor.fetchall():   # iterate over the cursor
            results.append(list(row))   # add the row as a list to the list of lists

        cursor.close()  # close the cursor
        conn.close()    # close the DB connection

        return results  # return the list of lists

1

我喜欢@bryan和@ foo-stack的答案。如果您使用的是Postgresql并且正在使用psycopg2,则可以通过指定从连接创建游标时将cursorfactory指定为a ,从而使用psycopg2中的一些好东西来实现相同DictCursor目的:

cur = conn.cursor( cursor_factory=psycopg2.extras.DictCursor )

因此,现在您可以执行sql查询,并且将获得字典来获取结果,而无需手动映射它们。

cur.execute( sql_query )
results = cur.fetchall()

for row in results:
    print row['row_no']

请注意,您必须import psycopg2.extras为此工作。


0

假设您知道列名!另外,这是三种不同的解决方案,
您可能想看看最后一种!

colnames = ['city', 'area', 'street']
data = {}

counter = 0
for row in x.fetchall():
    if not counter in data:
        data[counter] = {}

    colcounter = 0
    for colname in colnames:
        data[counter][colname] = row[colcounter]
        colcounter += 1

    counter += 1

那是一个索引版本,不是最漂亮的解决方案,但是它可以工作。另一种方法是将列名索引为字典键,并在每个键中按行号顺序包含数据的列表。通过做:

colnames = ['city', 'area', 'street']
data = {}

for row in x.fetchall():
    colindex = 0
    for col in colnames:
        if not col in data:
            data[col] = []
        data[col].append(row[colindex])
        colindex += 1

写这篇文章,我知道做事for col in colnames可以被代替,for colindex in range(0, len())但是你明白了。当不获取所有数据,但一次仅获取一行数据时,后面的示例将非常有用,例如:

对每行数据使用dict

def fetchone_dict(stuff):
    colnames = ['city', 'area', 'street']
    data = {}

    for colindex in range(0, colnames):
        data[colnames[colindex]] = stuff[colindex]
    return data

row = x.fetchone()
print fetchone_dict(row)['city']

获取表名(我想..多亏了Foo Stack):下面
是一个更直接的解决方案

cursor.execute("SELECT sys.objects.name, sys.columns.name FROM sys.objects INNER JOIN sys.columns ON sys.objects.object_id = sys.columns. object_id WHERE sys.objects.type = 'U';")
schema = {}
for it in cursor.fetchall():
    if it[0] in schema:
       schema[it[0]].append(it[1])
    else:
        schema[it[0]] = [it[1]]

谢谢,但是当我不知道列名时有通用的解决方案吗?
Foo Stack

是的,它称为SQL语法。您可以在数据库中查询要查询的表的名称。stackoverflow.com/questions/4645456/…–
破坏

我写了一个很好的通用模式收集器:
Foo Stack

1
cursor.execute("SELECT sys.objects.name, sys.columns.name FROM sys.objects INNER JOIN sys.columns ON sys.objects.object_id = sys.columns. object_id WHERE sys.objects.type = 'U';") schema = {} for it in cursor.fetchall(): if it[0] in schema: schema[it[0]].append(it[1]) else: schema[it[0]] = [it[1]]
Foo Stack

@FooStack列名称已在cursor.description中返回。不需要单独的查询。
布莱恩
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.