Résultat du curseur pyodbc comme dictionnaire python
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'}]
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()]}
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]
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!
;)
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
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.
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]]