Comment puis-je obtenir un dict à partir d'une requête sqlite?

118
db = sqlite.connect("test.sqlite")
res = db.execute("select * from table")

Avec l'itération, j'obtiens des listes correspondant aux lignes.

for row in res:
    print row

Je peux obtenir le nom des colonnes

col_name_list = [tuple[0] for tuple in res.description]

Mais y a-t-il une fonction ou un paramètre pour obtenir des dictionnaires au lieu d'une liste?

{'col1': 'value', 'col2': 'value'}

ou je dois faire moi-même?

Meloun
la source
3
@ vy32: Cette question date de juillet 2010, celle à laquelle vous vous êtes lié est celle de novembre 2010. Alors c'est la dupe. Et comme on pouvait s'y attendre, le commentaire inverse a été mis sur celui-là :-)
anéroïde

Réponses:

158

Vous pouvez utiliser row_factory , comme dans l'exemple de la documentation:

import sqlite3

def dict_factory(cursor, row):
    d = {}
    for idx, col in enumerate(cursor.description):
        d[col[0]] = row[idx]
    return d

con = sqlite3.connect(":memory:")
con.row_factory = dict_factory
cur = con.cursor()
cur.execute("select 1 as a")
print cur.fetchone()["a"]

ou suivez les conseils donnés juste après cet exemple dans la documentation:

Si le renvoi d'un tuple ne suffit pas et que vous souhaitez un accès basé sur le nom aux colonnes, vous devez envisager de définir row_factory sur le type sqlite3.Row hautement optimisé. Row fournit à la fois un accès par nom basé sur un index et insensible à la casse aux colonnes avec presque aucune surcharge de mémoire. Ce sera probablement mieux que votre propre approche basée sur un dictionnaire personnalisé ou même une solution basée sur db_row.

Alex Martelli
la source
Si les noms de vos colonnes contiennent des caractères spéciaux, par exemple, SELECT 1 AS "dog[cat]"le cursorn'aura pas la description correcte pour créer un dict.
Crazometer
J'ai défini connection.row_factory = sqlite3.Rowet j'ai essayé connection.row_factory = dict_factorycomme indiqué, mais cur.fetchall()me donne toujours une liste de tuples - une idée pourquoi cela ne fonctionne pas?
displayname
@displayname, la documentation ne déclare pas "Il essaie d'imiter un tuple dans la plupart de ses fonctionnalités.". Je suis presque sûr que c'est en quelque sorte similaire à ce que vous pouvez obtenir collections.namedtuple. Quand j'utilise, cur.fetchmany()j'obtiens des entrées comme <sqlite3.Row object at 0x...>.
ony
Même 7 ans plus tard, cette réponse est le copier-coller le plus utile des documents que j'ai trouvés sur SO. Merci!
WillardSolutions
40

Je pensais répondre à cette question même si la réponse est en partie mentionnée dans les réponses d'Adam Schmideg et d'Alex Martelli. Pour que d'autres comme moi qui ont la même question, trouvent la réponse facilement.

conn = sqlite3.connect(":memory:")

#This is the important part, here we are setting row_factory property of
#connection object to sqlite3.Row(sqlite3.Row is an implementation of
#row_factory)
conn.row_factory = sqlite3.Row
c = conn.cursor()
c.execute('select * from stocks')

result = c.fetchall()
#returns a list of dictionaries, each item in list(each dictionary)
#represents a row of the table
Gandalf
la source
21
fetchall()Semble actuellement renvoyer des sqlite3.Rowobjets. Cependant ceux - ci peuvent être convertis en un dictionnaire en utilisant simplement dict(): result = [dict(row) for row in c.fetchall()].
Gonçalo Ribeiro
21

Même en utilisant la classe sqlite3.Row, vous ne pouvez toujours pas utiliser le formatage de chaîne sous la forme de:

print "%(id)i - %(name)s: %(value)s" % row

Afin de surmonter cela, j'utilise une fonction d'assistance qui prend la ligne et la convertit en dictionnaire. Je ne l'utilise que lorsque l'objet dictionnaire est préférable à l'objet Row (par exemple pour des choses comme le formatage de chaîne où l'objet Row ne prend pas également en charge nativement l'API de dictionnaire). Mais utilisez l'objet Row toutes les autres fois.

def dict_from_row(row):
    return dict(zip(row.keys(), row))       
bbengfort
la source
9
sqlite3.Row implémente le protocole de mappage. Vous pouvez juste faireprint "%(id)i - %(name)s: %(value)s" % dict(row)
Mzzzzzz
9

Après vous être connecté à SQLite: con = sqlite3.connect(.....)il suffit de lancer:

con.row_factory = sqlite3.Row

Voila!

Lukasz Czerwinski
la source
8

À partir de la PEP 249 :

Question: 

   How can I construct a dictionary out of the tuples returned by
   .fetch*():

Answer:

   There are several existing tools available which provide
   helpers for this task. Most of them use the approach of using
   the column names defined in the cursor attribute .description
   as basis for the keys in the row dictionary.

   Note that the reason for not extending the DB API specification
   to also support dictionary return values for the .fetch*()
   methods is that this approach has several drawbacks:

   * Some databases don't support case-sensitive column names or
     auto-convert them to all lowercase or all uppercase
     characters.

   * Columns in the result set which are generated by the query
     (e.g.  using SQL functions) don't map to table column names
     and databases usually generate names for these columns in a
     very database specific way.

   As a result, accessing the columns through dictionary keys
   varies between databases and makes writing portable code
   impossible.

Alors oui, faites-le vous-même.

Ignacio Vazquez-Abrams
la source
> varie selon les bases de données - comme quoi, sqlite 3.7 et 3.8?
Nucular
@ user1123466: ... Comme entre SQLite, MySQL, Postgres, Oracle, MS SQL Server, Firebird ...
Ignacio Vazquez-Abrams
3

Version plus courte:

db.row_factory = lambda c, r: dict([(col[0], r[idx]) for idx, col in enumerate(c.description)])
M. Utku ALTINKAYA
la source
3

Le plus rapide sur mes tests:

conn.row_factory = lambda c, r: dict(zip([col[0] for col in c.description], r))
c = conn.cursor()

%timeit c.execute('SELECT * FROM table').fetchall()
19.8 µs ± 1.05 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)

contre:

conn.row_factory = lambda c, r: dict([(col[0], r[idx]) for idx, col in enumerate(c.description)])
c = conn.cursor()

%timeit c.execute('SELECT * FROM table').fetchall()
19.4 µs ± 75.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Tu décides :)

Ran Aroussi
la source
2

Comme mentionné par la réponse de @ gandalf, il faut utiliser conn.row_factory = sqlite3.Row, mais les résultats ne sont pas directement des dictionnaires. Il faut ajouter un "cast" supplémentaire dictdans la dernière boucle:

import sqlite3
conn = sqlite3.connect(":memory:")
conn.execute('create table t (a text, b text, c text)')
conn.execute('insert into t values ("aaa", "bbb", "ccc")')
conn.execute('insert into t values ("AAA", "BBB", "CCC")')
conn.row_factory = sqlite3.Row
c = conn.cursor()
c.execute('select * from t')
for r in c.fetchall():
    print(dict(r))

# {'a': 'aaa', 'b': 'bbb', 'c': 'ccc'}
# {'a': 'AAA', 'b': 'BBB', 'c': 'CCC'}
Basj
la source
1

Similaire aux solutions mentionnées précédemment, mais les plus compactes:

db.row_factory = lambda C, R: { c[0]: R[i] for i, c in enumerate(C.description) }
Falko
la source
Cela a fonctionné pour moi, où la réponse ci-dessus db.row_factory = sqlite3.Rown'a pas fonctionné pour moi (car cela a abouti à une erreur de type JSON)
Phillip
1

Je pense que vous étiez sur la bonne voie. Gardons cela très simple et complétons ce que vous essayiez de faire:

import sqlite3
db = sqlite3.connect("test.sqlite3")
cur = db.cursor()
res = cur.execute("select * from table").fetchall()
data = dict(zip([c[0] for c in cur.description], res[0]))

print(data)

L'inconvénient est que .fetchall(), ce qui est un meurtre sur votre consommation de mémoire , si votre table est très grande. Mais pour les applications triviales ne traitant que quelques milliers de lignes de texte et de colonnes numériques, cette approche simple est suffisante.

Pour les choses sérieuses, vous devriez vous pencher sur les usines en ligne, comme proposé dans de nombreuses autres réponses.

Tammi
la source
0

Ou vous pouvez convertir les sqlite3.Rows en dictionnaire comme suit. Cela donnera un dictionnaire avec une liste pour chaque ligne.

    def from_sqlite_Row_to_dict(list_with_rows):
    ''' Turn a list with sqlite3.Row objects into a dictionary'''
    d ={} # the dictionary to be filled with the row data and to be returned

    for i, row in enumerate(list_with_rows): # iterate throw the sqlite3.Row objects            
        l = [] # for each Row use a separate list
        for col in range(0, len(row)): # copy over the row date (ie. column data) to a list
            l.append(row[col])
        d[i] = l # add the list to the dictionary   
    return d
Andere
la source
0

Une alternative générique, en utilisant seulement trois lignes

def select_column_and_value(db, sql, parameters=()):
    execute = db.execute(sql, parameters)
    fetch = execute.fetchone()
    return {k[0]: v for k, v in list(zip(execute.description, fetch))}

con = sqlite3.connect('/mydatabase.db')
c = con.cursor()
print(select_column_and_value(c, 'SELECT * FROM things WHERE id=?', (id,)))

Mais si votre requête ne renvoie rien, cela entraînera une erreur. Dans ce cas...

def select_column_and_value(self, sql, parameters=()):
    execute = self.execute(sql, parameters)
    fetch = execute.fetchone()

    if fetch is None:
        return {k[0]: None for k in execute.description}

    return {k[0]: v for k, v in list(zip(execute.description, fetch))}

ou

def select_column_and_value(self, sql, parameters=()):
    execute = self.execute(sql, parameters)
    fetch = execute.fetchone()

    if fetch is None:
        return {}

    return {k[0]: v for k, v in list(zip(execute.description, fetch))}
macabeus
la source
0
import sqlite3

db = sqlite3.connect('mydatabase.db')
cursor = db.execute('SELECT * FROM students ORDER BY CREATE_AT')
studentList = cursor.fetchall()

columnNames = list(map(lambda x: x[0], cursor.description)) #students table column names list
studentsAssoc = {} #Assoc format is dictionary similarly


#THIS IS ASSOC PROCESS
for lineNumber, student in enumerate(studentList):
    studentsAssoc[lineNumber] = {}

    for columnNumber, value in enumerate(student):
        studentsAssoc[lineNumber][columnNames[columnNumber]] = value


print(studentsAssoc)

Le résultat est certainement vrai, mais je ne connais pas le meilleur.

Emrah Tuncel
la source
0

Les dictionnaires en python fournissent un accès arbitraire à leurs éléments. Donc, tout dictionnaire avec des "noms" bien qu'il puisse être informatif d'une part (c'est-à-dire quels sont les noms de champs) "désordonne" les champs, ce qui pourrait être indésirable.

La meilleure approche consiste à obtenir les noms dans une liste séparée, puis à les combiner vous-même avec les résultats, si nécessaire.

try:
         mycursor = self.memconn.cursor()
         mycursor.execute('''SELECT * FROM maintbl;''')
         #first get the names, because they will be lost after retrieval of rows
         names = list(map(lambda x: x[0], mycursor.description))
         manyrows = mycursor.fetchall()

         return manyrows, names

Rappelez-vous également que les noms, dans toutes les approches, sont les noms que vous avez fournis dans la requête, et non les noms de la base de données. L'exception est laSELECT * FROM

Si votre seul souci est d'obtenir les résultats à l'aide d'un dictionnaire, utilisez définitivement le conn.row_factory = sqlite3.Row(déjà indiqué dans une autre réponse).

ilias iliadis
la source