web-dev-qa-db-ja.com

pyodbcカーソルの結果をpython辞書として出力

Pyodbcカーソル出力をシリアル化する方法(.fetchone.fetchmany または .fetchall)Python辞書?

私はbottlepyを使用しており、JSONとして返すことができるようにdictを返す必要があります。

51
Foo Stack

事前に列がわからない場合は、 cursor.description を使用して列名のリストを作成し、 Zip を使用して各行で辞書のリストを作成します。例では、接続とクエリが構築されていると想定しています。

>>> 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'}]
104
Bryan

Bottlepyで@Beargleの結果を使用して、エンドポイントを公開するこの非常に簡潔なクエリを作成できました。

@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()]}
7
Foo Stack

これは、使用できる可能性のある短い形式のバージョンです

>>> 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します。

を使用して

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]
4
Tommy Strand

主に@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
Foo Stack

@bryanと@ foo-stackの回答が好きです。 postgresqlを使用していて、_psycopg2_を使用している場合は、カーソルを作成するときにcursorfactoryをDictCursorに指定することで psycopg2の利点 を使用して同じことを実現できます。このような接続:

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_を使わなければならないことに注意してください。

2
matthaeus

私はこの質問が古いことを知っていますが、それは私が必要なことを行う方法を理解するのに役立ちました。これはOPが求めていたものとは少し異なりますので、私が共有したいと思い、必要なものを必要とする他の人を助けるために: SQL Selectクエリを実行するルーチンを完全に一般化したいが、名前ではなくインデックス番号で結果を参照する必要がある場合は、辞書の代わりにリストのリストを使用してこれを行うことができます。返されるデータの各行は、返されるリストでfield(column)値のリストとして表されます。列名は、返されたリストの最初のエントリとして提供できるため、呼び出し元のルーチンで返されたリストを解析することは非常に簡単で柔軟です。このように、データベース呼び出しを行うルーチンは、処理するデータについて何も知る必要がありません。そのようなルーチンは次のとおりです。

    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
Grimravus

カーソルが使用できない状況-たとえば、関数呼び出しまたは内部メソッドによって行が返された場合、row.cursor_descriptionを使用して辞書表現を作成できます

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

列名を知っていると仮定します!また、次の3つの異なるソリューションがあります。
おそらく最後の1つを見たいと思うでしょう!

_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())に置き換えることができることを理解していますが、あなたはそのアイデアを得ます。後者の例は、すべてのデータを取得するのではなく、一度に1行ずつ取得する場合に役立ちます。たとえば、次のようになります。

データの各行に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]]
_
0
Torxed