Jak mogę uzyskać dict z zapytania sqlite?


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

Przy iteracji otrzymuję listy odpowiadające wierszom.

for row in res:
    print row

Mogę uzyskać nazwy kolumn

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

Ale czy jest jakaś funkcja lub ustawienie do pobierania słowników zamiast listy?

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

czy muszę zrobić sam?



3
@ vy32: To pytanie pochodzi z lipca 2010 r., to, z którym utworzyłeś łącze, to listopad 2010 r. Więc to jest oszustwo. I jak można by się spodziewać, do tego dodano komentarz odwrotny :-)
aneroid

Odpowiedzi:


159

Możesz użyć row_factory , jak w przykładzie w dokumentach:

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

lub postępuj zgodnie z radą podaną zaraz po tym przykładzie w dokumentacji:

Jeśli zwrócenie krotki nie wystarczy i chcesz uzyskać dostęp do kolumn na podstawie nazw, powinieneś rozważyć ustawienie row_factory na wysoce zoptymalizowany typ sqlite3.Row. Row zapewnia dostęp do kolumn na podstawie indeksów i bez rozróżniania wielkości liter i nazw, prawie bez narzutu pamięci. Prawdopodobnie będzie lepsze niż Twoje własne podejście oparte na niestandardowym słowniku lub nawet rozwiązanie oparte na db_row.


Jeśli nazwy kolumn zawierają znaki specjalne, np. SELECT 1 AS "dog[cat]"Wtedy cursornie będą miały poprawnego opisu do utworzenia dykta.
Crazometer,

Ustawiłem connection.row_factory = sqlite3.Rowi próbowałem, connection.row_factory = dict_factoryjak pokazano, ale cur.fetchall()nadal wyświetla mi listę krotek - masz pojęcie, dlaczego to nie działa?
nazwa wyświetlana

@displayname, czy dokumentacja nie stwierdza: „W większości funkcji próbuje naśladować krotkę”. Jestem pewien, że jest to w jakiś sposób podobne do tego, z czego można uzyskać collections.namedtuple. Kiedy używam cur.fetchmany(), otrzymuję wpisy takie jak <sqlite3.Row object at 0x...>.
ony

Nawet 7 lat później ta odpowiedź jest najbardziej pomocną metodą kopiowania i wklejania z dokumentów, które znalazłem na SO. Dzięki!
WillardSolutions

40

Pomyślałem, że odpowiem na to pytanie, chociaż odpowiedź jest częściowo wymieniona zarówno w odpowiedziach Adama Schmidega, jak i Alexa Martellego. Aby inni tacy jak ja, którzy mają to samo pytanie, mogli łatwo znaleźć odpowiedź.

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

21
Obecnie fetchall()wydaje się zwracać sqlite3.Rowobiekty. Jednakże mogą one zostać przekształcone do słownika po prostu za pomocą dict(): result = [dict(row) for row in c.fetchall()].
Gonçalo Ribeiro

21

Nawet używając klasy sqlite3.Row - nadal nie możesz używać formatowania ciągów w postaci:

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

Aby to obejść, używam funkcji pomocniczej, która pobiera wiersz i konwertuje go na słownik. Używam tego tylko wtedy, gdy obiekt słownika jest lepszy od obiektu Row (np. W przypadku formatowania ciągów, gdzie obiekt Row nie obsługuje również natywnie interfejsu API słownika). Ale we wszystkich innych przypadkach używaj obiektu Row.

def dict_from_row(row):
    return dict(zip(row.keys(), row))       

9
sqlite3.Row implementuje protokół mapowania. Możesz po prostu zrobićprint "%(id)i - %(name)s: %(value)s" % dict(row)
Mzzzzzz

9

Po połączeniu się z SQLite: con = sqlite3.connect(.....)wystarczy uruchomić:

con.row_factory = sqlite3.Row

Voila!


8

Od 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.

Więc tak, zrób to sam.


> różni się między bazami danych - na przykład co, sqlite 3.7 i 3.8?
Nucular

@ user1123466: ... Tak jak między SQLite, MySQL, Postgres, Oracle, MS SQL Server, Firebird ...
Ignacio Vazquez-Abrams

3

Krótsza wersja:

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

3

Najszybciej w moich testach:

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)

vs:

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)

Ty decydujesz :)


2

Jak wspomniano w odpowiedzi @ gandalfa, trzeba użyć conn.row_factory = sqlite3.Row, ale wyniki niebezpośrednio słownikami. W dictostatniej pętli trzeba dodać dodatkowe „rzutowanie” :

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

1

Podobne jak wyżej wymienione rozwiązania, ale najbardziej kompaktowe:

db.row_factory = lambda C, R: { c[0]: R[i] for i, c in enumerate(C.description) }

To zadziałało dla mnie, gdzie powyższa odpowiedź db.row_factory = sqlite3.Rownie zadziałała dla mnie (ponieważ spowodowała JSON TypeError)
Phillip

1

Myślę, że byłeś na dobrej drodze. Utrzymajmy to bardzo prosto i dokończmy to, co próbowałeś zrobić:

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)

Wadą jest to .fetchall(), że jest to morderstwo w zużyciu pamięci , jeśli twój stół jest bardzo duży. Ale w przypadku trywialnych aplikacji zajmujących się zaledwie kilkoma tysiącami wierszy tekstu i kolumn liczbowych to proste podejście jest wystarczające.

W przypadku poważnych spraw powinieneś zajrzeć do fabryk rzędowych, zgodnie z propozycjami w wielu innych odpowiedziach.


0

Lub możesz przekonwertować sqlite3.Rows na słownik w następujący sposób. To da słownik z listą dla każdego wiersza.

    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

0

Ogólna alternatywa, wykorzystująca tylko trzy linie

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

Ale jeśli twoje zapytanie nic nie zwróci, spowoduje błąd. W tym przypadku...

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

lub

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

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)

Wynik jest zdecydowanie prawdziwy, ale nie znam najlepszego.


0

Słowniki w Pythonie zapewniają dowolny dostęp do swoich elementów. Tak więc każdy słownik z „nazwami”, chociaż z jednej strony może być pouczający (czyli jakie są nazwy pól) „anuluje porządkowanie” pól, co może być niepożądane.

Najlepszym podejściem jest umieszczenie nazwisk na osobnej liście, a następnie, w razie potrzeby, samodzielne połączenie ich z wynikami.

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

Pamiętaj również, że we wszystkich podejściach nazwy są nazwami podanymi w zapytaniu, a nie nazwami w bazie danych. Wyjątkiem jestSELECT * FROM

Jeśli Twoim jedynym zmartwieniem jest uzyskanie wyników za pomocą słownika, zdecydowanie użyj conn.row_factory = sqlite3.Row(już podano w innej odpowiedzi).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.