Die Ergebnisse des Pyodbc-Cursors werden als Python-Wörterbuch ausgegeben


79

Wie serialisieren ich Pyodbc Ausgabe Cursor (von .fetchone, .fetchmanyoder .fetchall) als Python - Wörterbuch?

Ich verwende Bottlepy und muss dict zurückgeben, damit es als JSON zurückgegeben werden kann.


Und ja, ich habe bemerkt, dass dies in den FAQ für PEPE249 steht , aber das ändert nichts an meiner Anforderung.
Foo Stack

Antworten:


159

Wenn Sie Spalten nicht im Voraus kennen , erstellen Sie mit Cursor.description eine Liste mit Spaltennamen und komprimieren Sie mit jeder Zeile, um eine Liste mit Wörterbüchern zu erstellen. Beispiel: Angenommen, Verbindung und Abfrage werden erstellt:

>>> 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
wusste nicht über cursor.description. Das hat mir nur eine Menge Zeit gespart.
TehTris

benötigt, um Klammern um print (Spalten) und print (Ergebnisse) zu wickeln, damit dies funktioniert
LJT

2
@LJT Nur in Python3 ... aber da die Funktion print () in Python2 funktioniert, empfiehlt es sich, sie zu verwenden.
Auspex

1
@ BenLutgens Da das Beispiel eine Liste von Diktaten erzeugt , kein Diktat.
Auspex

1
Update: Standardmäßig setzt pypyodbc Kleinbuchstaben = True. Sie können dies folgendermaßen überschreiben: import pypyodbc; pypyodbc.lowercase = False. Referenz: Link
Weihui Guo

13

Mit dem Ergebnis von @ Beargle und Bottlepy konnte ich diese sehr präzise Abfrage erstellen, die den Endpunkt verfügbar macht:

@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
Ist dies SQL-Injection-Angriffen ausgesetzt?
Ben

@ Ben Ja! Sie sollten es niemals verwenden, es sei denn, Sie sind zu 1000% sicher, dass die Anforderungen immer von einem vertrauenswürdigen Client stammen.
Bora M. Alper

6

Hier ist eine Kurzversion, die Sie möglicherweise verwenden können

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

Wie Sie vielleicht wissen, entfernen Sie beim Hinzufügen von * zu einer Liste die Liste im Grunde genommen und belassen die einzelnen Listeneinträge als Parameter für die von Ihnen aufgerufene Funktion. Mit dem Reißverschluss wählen wir den 1. bis n-Eintrag und reißen sie zusammen wie einen Reißverschluss in Ihrer Hose.

also mit

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

du erhältst

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

Da description ein Tupel mit Tupeln ist, wobei jedes Tupel den Header und den Datentyp für jede Spalte beschreibt, können Sie das erste jedes Tupels mit extrahieren

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

gleichwertig

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

Mit python3.4 bekomme ich : TypeError: 'zip' object is not subscriptable, also kann ich den zip(*description)[0]Trick nicht anwenden .
Malat

In Python 3.4 ist zip ein Iterator. Sie können die Postleitzahl in eine Listenliste einschließen (Postleitzahl (* Beschreibung)) [0] @malat
Tommy Strand

Sie haben eine Zeile mit columnsVariablen gespeichert , aber die Komplexität der Funktion multipliziert, indem Sie die
Spaltennamen

3

Hauptsächlich ging ich von der @ Torxed-Antwort aus und erstellte einen vollständigen allgemeinen Satz von Funktionen, um das Schema und die Daten in einem Wörterbuch zu finden:

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

Fühlen Sie sich frei, alles Code-Golf zu spielen, um die Linien zu reduzieren; aber in der Zwischenzeit funktioniert es!

;)


2

In Situationen, in denen der Cursor nicht verfügbar ist, z. B. wenn die Zeilen durch einen Funktionsaufruf oder eine innere Methode zurückgegeben wurden, können Sie mithilfe von row.cursor_description dennoch eine Wörterbuchdarstellung erstellen

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

1

Ich weiß, dass diese Frage alt ist, aber sie hat mir geholfen, herauszufinden, wie ich das tun soll, was ich brauche, was sich geringfügig von dem unterscheidet, was OP verlangt. Deshalb dachte ich, ich würde sie teilen, um allen anderen zu helfen, die das brauchen, was ich brauchte: Wenn Wenn Sie eine Routine, die SQL Select-Abfragen ausführt, vollständig verallgemeinern möchten, müssen Sie die Ergebnisse jedoch anhand einer Indexnummer und nicht anhand eines Namens referenzieren. Sie können dies mit einer Liste von Listen anstelle eines Wörterbuchs tun. Jede Zeile der zurückgegebenen Daten wird in der zurückgegebenen Liste als Liste der Feldwerte (Spaltenwerte) dargestellt. Die Spaltennamen können als erster Eintrag der zurückgegebenen Liste angegeben werden, sodass das Parsen der zurückgegebenen Liste in der aufrufenden Routine sehr einfach und flexibel sein kann. Auf diese Weise muss die Routine, die den Datenbankaufruf ausführt, nichts über die Daten wissen, die sie verarbeitet. Hier ist eine solche Routine:

    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

Ich mag @ bryan und @ foo-stack Antworten. Wenn Sie mit postgresql arbeiten und es verwenden, können psycopg2Sie einige Extras von psycopg2 verwenden , um dasselbe zu erreichen, indem Sie DictCursorbeim Erstellen Ihres Cursors aus der Verbindung Folgendes angeben :

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

Jetzt können Sie Ihre SQL-Abfrage ausführen und erhalten ein Wörterbuch zum Abrufen Ihrer Ergebnisse, ohne sie manuell zuordnen zu müssen.

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

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

Bitte beachten Sie, import psycopg2.extrasdass dies erforderlich ist.


0

Angenommen, Sie kennen Ihre Spaltennamen! Außerdem gibt es hier drei verschiedene Lösungen, die
Sie wahrscheinlich als letzte ansehen möchten!

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

Das ist eine indizierte Version, nicht die schönste Lösung, aber sie wird funktionieren. Eine andere Möglichkeit wäre, den Spaltennamen als Wörterbuchschlüssel mit einer Liste in jedem Schlüssel zu indizieren, die die Daten in der Reihenfolge der Zeilennummer enthält. indem Sie tun:

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

Wenn ich das schreibe, verstehe ich, dass das Tun for col in colnamesdurch ersetzt werden könntefor colindex in range(0, len()) aber Sie bekommen die Idee. Das spätere Beispiel wäre nützlich, wenn nicht alle Daten, sondern jeweils eine Zeile abgerufen werden sollen, zum Beispiel:

Verwenden von dict für jede Datenzeile

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

Tabellennamen bekommen (ich denke .. dank Foo Stack):
eine direktere Lösung von Beargle unten!

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

Danke, aber gibt es eine allgemeine Lösung, wenn ich meine Spaltennamen nicht kenne?
Foo Stack

Ja, es heißt SQL-Syntax. Sie können Ihre Datenbank nach den Namen der Tabelle abfragen, für die Sie eine Abfrage durchführen. stackoverflow.com/questions/4645456/…
Torxed

Ich habe einen netten kleinen verallgemeinerten Schemasammler geschrieben:
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 Spaltennamen werden bereits in cursor.description zurückgegeben . Eine separate Abfrage ist nicht erforderlich.
Bryan
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.