Вивести курсор pyodbc у вигляді словника python


83

Як мені серіалізувати вихід курсору pyodbc (з .fetchone, .fetchmanyабо .fetchall) як словник Python?

Я використовую пляшку і мені потрібно повернути dict, щоб він міг повернути його як JSON.


І так, я помітив, що це було у поширених запитаннях щодо PEPE249 , однак це не змінює мої вимоги.
Foo Stack

Відповіді:


163

Якщо ви не знаєте стовпців заздалегідь, використовуйте 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'}]

1
не знав про cursor.description. це просто врятувало мені багато часу.
TehTris

потрібно було обернути дужки навколо друку (стовпці) та роздрукувати (результати), щоб це працювало
LJT

2
@LJT Тільки в python3 ... але оскільки функція print () працює в python2, корисно використовувати її.
Auspex

1
@BenLutgens Оскільки приклад дає список диктовок, а не дикту.
Auspex

1
Оновлення: за замовчуванням pypyodbc встановлює малі регістри = True. Ви можете переписати це так: import pypyodbc; pypyodbc.lowercase = Неправильно. Довідково: посилання
Вейхуей Го

13

Використовуючи результат @ 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()]}

3
це піддається атакам ін'єкції SQL?
Бен,

@Ben Так! Ви ніколи не повинні використовувати його, якщо ви не впевнені на 1000%, що запити завжди надходитимуть від надійного клієнта.
Бора М. Альпер,

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

Як ви могли знати, додаючи * до списку, ви в основному видаляєте список, залишаючи окремі записи списку як параметри функції, яку ви викликаєте. За допомогою застібки-блискавки ми обираємо 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]фокус.
Малат

У python 3.4 zip є ітератором. Ви можете обернути zip у списку списків (zip (* опис)) [0] @malat
Tommy Strand

Ви зберегли один рядок із columnsзмінною, але помножили складність функції, обчислюючи назви стовпців для кожного рядка окремо
Сергій Нуднов

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

Я знаю, що це питання давнє, але воно допомогло мені зрозуміти, як зробити те, що мені потрібно, і воно дещо відрізняється від того, про що просив ОП, тому я подумав поділитися, щоб допомогти кожному, хто потребує того, що мені потрібно: Якщо ви хочете повністю узагальнити підпрограму, яка виконує запити вибору SQL, але вам потрібно посилатися на результати за номером індексу, а не за іменем, це можна зробити зі списком списків замість словника. Кожен рядок повернутих даних представлений у поверненому списку як список значень полів (стовпців). Імена стовпців можна вказати як перший запис у списку, що повертається, тому аналіз списку, що повертається, у процедурі виклику може бути дуже простим та гнучким. Таким чином, програма, що виконує виклик бази даних, не повинна знати нічого про дані, якими вона обробляє. Ось така рутина:

    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ви можете використати деякі смаколики з psycopg2, щоб досягти цього, вказавши cursorfactory як a 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 / ...
Torxed

Я написав приємний узагальнений узагальнювач схем:
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 . Окремий запит не потрібен.
Брайан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.