pyodbc 커서 결과를 Python 사전으로 출력


답변:


163

미리 열을 알지 못하는 경우 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

이 작업을 수행하려면 인쇄 (열) 및 인쇄 (결과)를 괄호로 묶어야합니다.
LJT

2
@LJT Only in python3 ...하지만 print () 함수는 python2에서 작동하기 때문에 사용하는 것이 좋습니다.
Auspex 2017

1
@BenLutgens이 예제는 dict가 아닌 dict 목록 을 생성하기 때문 입니다.
Auspex 2017

1
업데이트 : 기본적으로 pypyodbc는 소문자 = True로 설정합니다. 다음과 같이 덮어 쓸 수 있습니다. import pypyodbc; pypyodbc.lowercase = False. 참조 : 링크
Weihui Guo

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

3
이것이 SQL 주입 공격에 노출되어 있습니까?
Ben

@ 벤 예! 요청이 항상 신뢰할 수있는 클라이언트에서 오는지 1000 % 확신하지 않는 한 절대 사용해서는 안됩니다.
보라 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)]

설명은 튜플이있는 튜플이므로 각 튜플은 각 열의 헤더와 데이터 유형을 설명하므로 다음을 사용하여 각 튜플의 첫 번째 튜플을 추출 할 수 있습니다.

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

파이썬 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 Queries를 수행하는 루틴을 완전히 일반화하려고하지만 이름이 아닌 인덱스 번호로 결과를 참조해야합니다. 사전 대신 목록 목록을 사용하여이를 수행 할 수 있습니다. 반환 된 데이터의 각 행은 반환 된 목록에 필드 (열) 값 목록으로 표시됩니다. 열 이름은 반환 된 목록의 첫 번째 항목으로 제공 될 수 있으므로 호출 루틴에서 반환 된 목록을 구문 분석하는 것은 정말 쉽고 유연 할 수 있습니다. 이런 식으로 데이터베이스 호출을 수행하는 루틴은 처리중인 데이터에 대해 알 필요가 없습니다. 다음은 그러한 루틴입니다.

    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())하지만 당신은 아이디어를 얻습니다. 이후의 예제 tho는 모든 데이터를 가져 오지 않고 한 번에 한 행씩 가져올 때 유용합니다. 예를 들면 다음과 같습니다.

각 데이터 행에 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 덕분에 .. 생각합니다) : 아래의 beargle에서
보다 직접적인 솔루션 입니다!

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/…
2013 년

나는 약간의 일반화 된 스키마 수집기를 작성했습니다.
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에 반환되었습니다 . 별도의 쿼리가 필요하지 않습니다.
Bryan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.