2026, Jan 08 18:02

Эффективная загрузка данных из SQLite в Python: словарь списков без лишних проходов

Покажем, как читать данные из SQLite в Python сразу в словарь списков: пакетное fetchmany, транспонирование zip и ускорение в несколько раз без pandas.

При извлечении данных из SQLite напрашивается вариант: собрать список словарей — по одному словарю на строку. Это удобно и наглядно, но подходит не всегда. Если дальше вас ждут структуры с колонками как главным измерением, лучше сразу преобразовать результат в словарь списков. Ниже разберём небольшой пример, посмотрим, что даёт привычный подход, и перейдём к более эффективной колонковой загрузке с пакетным чтением.

Постановка задачи

Возьмём простую таблицу из my_db.db:

idx   speed   duration
 0     420       50
 1     380       40
 2     390       45

Загрузить её в Python в виде списка словарей несложно:

import sqlite3
base_path = folder_path  # только для примера; предполагается, что folder_path определён
db_conn = sqlite3.connect(base_path + 'my_db.db')
db_conn.row_factory = sqlite3.Row
db_cur = db_conn.cursor()
db_cur.execute('SELECT * FROM my_table')
row_objs = db_cur.fetchall()
rows_as_dicts = [dict(r) for r in row_objs]
db_cur.close()
db_conn.close()

Получим:

rows_as_dicts = [
  {'idx': 0, 'speed': 420, 'duration': 50},
  {'idx': 1, 'speed': 380, 'duration': 40},
  {'idx': 2, 'speed': 390, 'duration': 45}
]

Но целевая структура — словарь списков, организованный по столбцам:

cols_as_lists = {
  'idx': [0, 1, 2],
  'speed': [420, 380, 390],
  'duration': [50, 40, 45]
}

Что происходит на самом деле

Подход «список словарей» идёт по строкам и для каждой записи создаёт отдельный словарь, снова и снова обращаясь к одним и тем же ключам в Python. Если вам нужна колонковая раскладка, появляется лишняя работа: либо второй проход для разворота строк в столбцы, либо поиск по словарю для каждой ячейки во время загрузки. И то и другое добавляет накладные расходы. Альтернатива — собирать колонковую структуру сразу, во время чтения курсором.

Эффективный способ собрать словарь списков

Идея проста: читать результат порциями и для каждой порции одним шагом транспонировать данные из построчного вида в поколоночный. Связка fetchmany и компактного конвейера итераторов убирает повторные обращения к словарям на каждую ячейку и опирается на плотные проходы по данным. Такой способ показывал примерно пятикратное ускорение: он избегает повторных словарных обращений для каждого столбца в каждой строке и эффективнее использует итераторы.

import sqlite3
import collections
cn = sqlite3.connect(base_path + 'my_db.db')
cr = cn.cursor()
cr.execute('SELECT * FROM my_table')
# 'col_names' должен содержать имена столбцов в том же порядке, что и вывод SELECT
col_names = columns  # предполагается, что эта последовательность доступна вместе с курсором
store = collections.defaultdict(list)
while batch := cr.fetchmany(CHUNK_SIZE):
    for col_values, col_name in zip(zip(*batch), col_names):
        store[col_name].extend(col_values)
cr.close()
cn.close()
cols_as_lists = dict(store)

Так мы получаем нужный словарь списков прямо на этапе чтения — без промежуточного списка словарей строк.

Почему это важно

Для таблиц с большим числом строк поколоночная сборка уменьшает накладные расходы на стороне Python и заметно ускоряет загрузку. Плюс она сразу совпадает с потребителями колонкового формата, так что второй проход не нужен. Как отмечал один из практиков, в недавнем тесте прямая загрузка через pandas оказалась на порядок медленнее описанного подхода. Ещё одно наблюдение: можно идти из SQL сразу в pandas DataFrame, а DataFrame концептуально близок к словарю списков. Насколько такой компромисс уместен, зависит от ваших ограничений и нагрузки.

В недавнем тесте прямая загрузка через pandas была на порядок медленнее описанного подхода.

Можно перейти напрямую из SQL к pandas DataFrame, где DataFrame эквивалентен словарю списков.

Что стоит запомнить

Если вашему конвейеру выгоден формат «словарь списков», формируйте его сразу из курсора. Пакетное чтение с простым шагом транспонирования сохраняет код компактным и избавляет от поисков по ячейкам. Выбирая инструменты, учитывайте модель выполнения: удобство построчной обработки или пропускную способность поколоночной. Если важна скорость, попробуйте подход с пакетными итерациями на своих данных и держите его под рукой.