Si usted no sabe columnas antes de tiempo, utilizar Cursor.description para construir una lista de nombres de columna y cremallera con cada fila para producir una lista de diccionarios. El ejemplo asume que la conexión y la consulta están construidas:
no conocía cursor.description. esto me ahorró un montón de tiempo.
TehTris
es necesario envolver paréntesis alrededor de la impresión (columnas) y la impresión (resultados) para que esto funcione
LJT
2
@LJT Solo en python3 ... pero dado que la función print () funciona en python2, es una buena práctica usarla.
Auspex
1
@BenLutgens Porque el ejemplo produce una lista de dictados, no un dictado.
Auspex
1
Actualización: de forma predeterminada, pypyodbc establece minúsculas = Verdadero. Puede sobrescribir esto así: import pypyodbc; pypyodbc.lowercase = Falso. Referencia: enlace
Weihui Guo
13
Usando el resultado de @ Beargle con bottlepy, pude crear esta consulta muy concisa que expone el punto final:
@route('/api/query/<query_str>')defquery(query_str):
cursor.execute(query_str)
return {'results':
[dict(zip([column[0] for column in cursor.description], row))
for row in cursor.fetchall()]}
@Ben ¡Sí! Nunca debe usarlo a menos que esté 1000% seguro de que las solicitudes siempre vendrán de un cliente confiable.
Bora M. Alper
6
Aquí hay una versión de formato corto que podría usar
>>> 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()]
Como ya sabrá, cuando agrega * a una lista, básicamente elimina la lista, dejando las entradas individuales de la lista como parámetros para la función que está llamando. Al usar la cremallera, seleccionamos la entrada 1 an y la juntamos como la cremallera de tus pantalones.
entonces usando
zip(*[(a,1,2),(b,1,2)])
# interpreted by python as zip((a,1,2),(b,1,2))
usted obtiene
[('a', 'b'), (1, 1), (2, 2)]
Dado que la descripción es una tupla con tuplas, donde cada tupla describe el encabezado y el tipo de datos de cada columna, puede extraer el primero de cada tupla con
>>> columns = zip(*cursor.description)[0]
equivalente a
>>> columns = [column[0] for column in cursor.description]
Con python3.4 obtengo:, TypeError: 'zip' object is not subscriptableasí que no puedo usar el zip(*description)[0]truco.
malat
En Python 3.4, zip es un iterador. Puede envolver el zip en una lista de lista (zip (* descripción)) [0] @malat
Tommy Strand
Guardó una línea con columnsvariable, pero multiplicó la complejidad de la función al calcular los nombres de las columnas para cada fila por separado
Sergey Nudnov
3
Principalmente saliendo de la respuesta @Torxed, creé un conjunto completo de funciones generalizadas para encontrar el esquema y los datos en un diccionario:
defschema_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] notin schema:
schema[it[0]]={'scheme':[]}
else:
schema[it[0]]['scheme'].append(it[1])
return schema
defpopulate_dict(cursor, schema):for i in schema.keys():
cursor.execute("select * from {table};".format(table=i))
for row in cursor.fetchall():
colindex = 0for col in schema[i]['scheme']:
ifnot'data'in schema[i]:
schema[i]['data']=[]
schema[i]['data'].append(row[colindex])
colindex += 1return schema
defdatabase_to_dict():
cursor = connect()
schema = populate_dict(cursor, schema_dict(cursor))
Siéntase libre de hacer todo el código de golf en esto para reducir las líneas; pero mientras tanto, ¡funciona!
Para situaciones en las que el cursor no está disponible, por ejemplo, cuando las filas han sido devueltas por alguna llamada de función o método interno, aún puede crear una representación de diccionario usando row.cursor_description
defrow_to_dict(row):returndict(zip([t[0] for t in row.cursor_description], row))
Sé que esta pregunta es antigua, pero me ayudó a descubrir cómo hacer lo que necesitaba, que es ligeramente diferente de lo que pedía OP, así que pensé en compartir, para ayudar a cualquier otra persona que necesite lo que yo necesitaba: Si desea generalizar completamente una rutina que realiza consultas de selección de SQL, pero necesita hacer referencia a los resultados mediante un número de índice, no un nombre, puede hacerlo con una lista de listas en lugar de un diccionario. Cada fila de datos devueltos se representa en la lista devuelta como una lista de valores de campo (columna). Los nombres de las columnas se pueden proporcionar como la primera entrada de la lista devuelta, por lo que analizar la lista devuelta en la rutina de llamada puede ser realmente fácil y flexible. De esta manera, la rutina que realiza la llamada a la base de datos no necesita saber nada sobre los datos que está manejando. Aquí hay una rutina de este tipo:
defread_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 inrange(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 listfor 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 connectionreturn results # return the list of lists
Me gustan las respuestas de @bryan y @ foo-stack. Si está trabajando con postgresql y está usando psycopg2, podría usar algunos beneficios de psycopg2 para lograr lo mismo especificando que el cursorfactory sea a DictCursoral crear su cursor desde la conexión, así:
cur = conn.cursor( cursor_factory=psycopg2.extras.DictCursor )
Así que ahora puede ejecutar su consulta SQL y obtendrá un diccionario para obtener sus resultados, sin la necesidad de mapearlos a mano.
cur.execute( sql_query )
results = cur.fetchall()
for row in results:
print row['row_no']
Tenga en cuenta que tendrá que hacerlo import psycopg2.extraspara que funcione.
¡Suponiendo que conozca los nombres de las columnas! Además, aquí hay tres soluciones diferentes,
¡probablemente quieras ver la última!
colnames = ['city', 'area', 'street']
data = {}
counter = 0for row in x.fetchall():
ifnot counter in data:
data[counter] = {}
colcounter = 0for colname in colnames:
data[counter][colname] = row[colcounter]
colcounter += 1
counter += 1
Esa es una versión indexada, no es la solución más hermosa, pero funcionará. Otra sería indexar el nombre de la columna como clave de diccionario con una lista dentro de cada clave que contiene los datos en orden de número de fila. haciendo:
colnames = ['city', 'area', 'street']
data = {}
for row in x.fetchall():
colindex = 0for col in colnames:
ifnot col in data:
data[col] = []
data[col].append(row[colindex])
colindex += 1
Al escribir esto, entiendo que hacer for col in colnamespodría ser reemplazado por, for colindex in range(0, len())pero entiendes la idea. El último ejemplo sería útil cuando no se obtienen todos los datos, sino una fila a la vez, por ejemplo:
Usando dict para cada fila de datos
deffetchone_dict(stuff):
colnames = ['city', 'area', 'street']
data = {}
for colindex inrange(0, colnames):
data[colnames[colindex]] = stuff[colindex]
return data
row = x.fetchone()
print fetchone_dict(row)['city']
Obtener nombres de tablas (creo ... gracias a Foo Stack): ¡ una solución más directa de Beargle a continuación!
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]]
Gracias, pero ¿existe una solución generalizada para cuando no conozco los nombres de mis columnas?
Foo Stack
Sí, se llama sintaxis SQL. Puede consultar en su base de datos los nombres de la tabla contra la que está consultando. stackoverflow.com/questions/4645456/…
Torxed
He escrito un pequeño y agradable recopilador de esquemas generalizados:
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 Los nombres de las columnas ya se devuelven en cursor.description . No se necesita una consulta separada.
Respuestas:
Si usted no sabe columnas antes de tiempo, utilizar Cursor.description para construir una lista de nombres de columna y cremallera con cada fila para producir una lista de diccionarios. El ejemplo asume que la conexión y la consulta están construidas:
>>> 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'}]
fuente
cursor.description
. esto me ahorró un montón de tiempo.Usando el resultado de @ Beargle con bottlepy, pude crear esta consulta muy concisa que expone el punto final:
@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()]}
fuente
Aquí hay una versión de formato corto que podría usar
>>> 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()]
Como ya sabrá, cuando agrega * a una lista, básicamente elimina la lista, dejando las entradas individuales de la lista como parámetros para la función que está llamando. Al usar la cremallera, seleccionamos la entrada 1 an y la juntamos como la cremallera de tus pantalones.
entonces usando
zip(*[(a,1,2),(b,1,2)]) # interpreted by python as zip((a,1,2),(b,1,2))
usted obtiene
[('a', 'b'), (1, 1), (2, 2)]
Dado que la descripción es una tupla con tuplas, donde cada tupla describe el encabezado y el tipo de datos de cada columna, puede extraer el primero de cada tupla con
>>> columns = zip(*cursor.description)[0]
equivalente a
>>> columns = [column[0] for column in cursor.description]
fuente
TypeError: 'zip' object is not subscriptable
así que no puedo usar elzip(*description)[0]
truco.columns
variable, pero multiplicó la complejidad de la función al calcular los nombres de las columnas para cada fila por separadoPrincipalmente saliendo de la respuesta @Torxed, creé un conjunto completo de funciones generalizadas para encontrar el esquema y los datos en un diccionario:
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))
Siéntase libre de hacer todo el código de golf en esto para reducir las líneas; pero mientras tanto, ¡funciona!
;)
fuente
Para situaciones en las que el cursor no está disponible, por ejemplo, cuando las filas han sido devueltas por alguna llamada de función o método interno, aún puede crear una representación de diccionario usando row.cursor_description
def row_to_dict(row): return dict(zip([t[0] for t in row.cursor_description], row))
fuente
Sé que esta pregunta es antigua, pero me ayudó a descubrir cómo hacer lo que necesitaba, que es ligeramente diferente de lo que pedía OP, así que pensé en compartir, para ayudar a cualquier otra persona que necesite lo que yo necesitaba: Si desea generalizar completamente una rutina que realiza consultas de selección de SQL, pero necesita hacer referencia a los resultados mediante un número de índice, no un nombre, puede hacerlo con una lista de listas en lugar de un diccionario. Cada fila de datos devueltos se representa en la lista devuelta como una lista de valores de campo (columna). Los nombres de las columnas se pueden proporcionar como la primera entrada de la lista devuelta, por lo que analizar la lista devuelta en la rutina de llamada puede ser realmente fácil y flexible. De esta manera, la rutina que realiza la llamada a la base de datos no necesita saber nada sobre los datos que está manejando. Aquí hay una rutina de este tipo:
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
fuente
Me gustan las respuestas de @bryan y @ foo-stack. Si está trabajando con postgresql y está usando
psycopg2
, podría usar algunos beneficios de psycopg2 para lograr lo mismo especificando que el cursorfactory sea aDictCursor
al crear su cursor desde la conexión, así:cur = conn.cursor( cursor_factory=psycopg2.extras.DictCursor )
Así que ahora puede ejecutar su consulta SQL y obtendrá un diccionario para obtener sus resultados, sin la necesidad de mapearlos a mano.
cur.execute( sql_query ) results = cur.fetchall() for row in results: print row['row_no']
Tenga en cuenta que tendrá que hacerlo
import psycopg2.extras
para que funcione.fuente
¡Suponiendo que conozca los nombres de las columnas! Además, aquí hay tres soluciones diferentes,
¡probablemente quieras ver la última!
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
Esa es una versión indexada, no es la solución más hermosa, pero funcionará. Otra sería indexar el nombre de la columna como clave de diccionario con una lista dentro de cada clave que contiene los datos en orden de número de fila. haciendo:
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
Al escribir esto, entiendo que hacer
for col in colnames
podría ser reemplazado por,for colindex in range(0, len())
pero entiendes la idea. El último ejemplo sería útil cuando no se obtienen todos los datos, sino una fila a la vez, por ejemplo:Usando dict para cada fila de datos
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']
Obtener nombres de tablas (creo ... gracias a Foo Stack): ¡
una solución más directa de Beargle a continuación!
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]]
fuente
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]]