Résultat du curseur pyodbc comme dictionnaire python

comment sérialiser la sortie du curseur pyodbc (à partir de .fetchone,.fetchmany ou <!-Comme un dictionnaire de Python?

j'utilise bottlepy et j'ai besoin de retourner dict pour qu'il puisse le retourner en JSON.

37
demandé sur Bryan 2013-05-13 14:02:02

7 réponses

Si vous ne connaissez pas les colonnes à l'avance, utilisez curseur.description pour générer une liste de noms de colonnes et zip avec chaque ligne pour produire une liste de dictionnaires. Exemple suppose que la connexion et la requête sont construites:

>>> 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'}]
80
répondu Bryan 2013-12-17 15:11:34

en utilisant le résultat de @Beargle avec bottlepy, j'ai pu créer cette requête très concise exposant endpoint:

@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
répondu Foo Stack 2013-05-14 12:37:14

Voici une version abrégée que vous pouvez utiliser

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

comme vous le savez peut-être, lorsque vous ajoutez * à une liste, vous supprimez la liste, laissant les entrées individuelles de la liste comme paramètres à la fonction que vous appelez. En utilisant zip nous choisissons la première à n entrée et les zip ensemble comme une fermeture à glissière dans votre pantalon.

en utilisant

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

vous obtenez

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

puisque la description est un tuple avec tuples, où chaque tuple décrit l'en-tête et le type de données pour chaque colonne, vous pouvez extraire le premier de chaque tuple

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

équivalent à

>>> columns = [column[0] for column in cursor.description]
3
répondu Tommy Strand 2014-12-11 12:00:57

j'ai créé un ensemble complet de fonctions généralisées pour trouver le schéma et les données dans un dictionnaire:

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

N'hésitez pas à aller tout en code-golf là-dessus pour réduire les lignes; mais en attendant, ça marche!

;)

2
répondu Foo Stack 2013-05-13 18:18:24

je sais que cette question Est ancienne, mais elle m'a aidé à trouver comment faire ce dont j'avais besoin, ce qui est légèrement différent de ce que L'OP demandait, donc j'ai pensé que je partagerais, pour aider n'importe qui d'autre qui a besoin de ce dont j'avais besoin: Si vous voulez pleinement généraliser une routine qui exécute des requêtes SQL Select, mais que vous avez besoin de référencer les résultats par un numéro d'index, pas un nom, vous pouvez le faire avec une liste de listes au lieu d'un dictionnaire. Chaque ligne de données renvoyées sont représentés dans la liste retournée comme une liste de champ(colonne) de valeurs. Les noms de colonnes peuvent être fournis comme première entrée de la liste retournée, donc l'analyse de la liste retournée dans la routine d'appel peut être très facile et flexible. De cette façon, la routine de faire l'appel de base de données n'a pas besoin de savoir quoi que ce soit sur les données qu'il traite. Voici un exemple d'une 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
répondu Grimravus 2018-02-22 18:29:50

j'aime les réponses de @bryan et @ foo-stack. Si vous travaillez avec postgresql et que vous utilisez psycopg2 vous pouvez utiliser quelques goodies de psycopg2 pour obtenir le même en spécifiant le cursorfactory être un DictCursor lors de la création de votre curseur à partir de la connexion, comme ceci:

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

Alors maintenant, vous pouvez exécuter votre requête sql et vous obtiendrez un dictionnaire pour chercher vos résultats, sans avoir besoin de la carte à la main.

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

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

s'il vous Plaît notez que vous aurez à import psycopg2.extras pour que cela fonctionne.

1
répondu matthaeus 2018-03-01 10:06:55

en supposant que vous connaissez vos noms de colonne! Aussi, voici trois solutions différentes,

vous devriez regarder le dernier!

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

C'est une version indexée, pas la plus belle solution mais ça va marcher. Une autre possibilité serait d'indexer le nom de la colonne en tant que clé du dictionnaire avec une liste dans chaque clé contenant les données par ordre de numéro de ligne. en faisant:

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

en écrivant ceci, je comprends que faire for col in colnames peut être remplacé par for colindex in range(0, len()) mais vous obtenez l'idée. L'exemple suivant tho serait utile lorsque vous ne récupérez pas toutes les données, mais une ligne à la fois, par exemple:

utilisation de dict pour chaque ligne de données

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

Se tablenames (je pense.. merci à Toto Pile):

une solution plus directe à partir de beargle ci-dessous!

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
répondu Torxed 2013-05-13 16:46:07