DuckDBинструментыSQLаналитика

DuckDB для аналитика: быстрая локальная аналитика

2026-07-11 10 мин

DuckDB — это аналитическая СУБД, которая живёт прямо внутри твоего процесса (как SQLite), но заточена не под транзакции, а под аналитику: колоночное хранение, векторизованное выполнение, чтение Parquet и CSV прямо из SQL. Один файл, ноль серверов, ноль настройки. Ставится одной командой, работает в Python, R и в консоли. Для аналитика это способ прогонять тяжёлые GROUP BY по файлам на ноутбуке за секунды — без поднятия PostgreSQL и без сорока строк pandas. Дальше — как это устроено, чем отличается от привычных инструментов и где заходит идеально.

Что такое DuckDB простыми словами?

Представь SQLite, но не для мобильных приложений и справочников, а для аналитики. SQLite хранит данные построчно и отлично отвечает на запросы вида «дай мне строку по id». DuckDB хранит данные поколоночно и отлично отвечает на запросы вида «просуммируй выручку по странам за квартал». Это и есть ключевое различие: OLTP против OLAP, точечные операции против агрегаций по миллионам строк.

При этом архитектурно DuckDB — такой же встраиваемый движок. Нет отдельного сервера, к которому надо подключаться по сети. Нет пользователей, портов, конфигов. Ты делаешь pip install duckdb, импортируешь библиотеку — и у тебя внутри Python-процесса живёт полноценная колоночная база с векторизованным исполнением запросов. Вся «установка» выглядит так:

pip install duckdb
import duckdb

# Запрос прямо по файлу — без сервера, без CREATE TABLE, без импорта
duckdb.sql("SELECT count(*) FROM 'events.parquet'").show()

Три вещи делают DuckDB быстрым на аналитике. Первое — колоночное хранение: для SUM(revenue) движок читает с диска только колонку revenue, а не всю строку целиком. Второе — векторизованное выполнение: операции идут батчами по тысячам значений, а не по одной строке в цикле, и хорошо ложатся на современный CPU. Третье — параллелизм: тяжёлый запрос сам раскидывается по всем ядрам ноутбука. В сумме на агрегациях по десяткам и сотням миллионов строк DuckDB работает в разы, а иногда и в десятки раз быстрее, чем построчные движки. Насколько именно — зависит от данных и запроса, так что цифрам из чужих бенчмарков верь осторожно и меряй на своих.

Чем DuckDB отличается от SQLite?

Оба — встраиваемые, оба в одном файле, оба без сервера. Но задачи у них разные, и это видно в первом же тяжёлом запросе.

SQLite построчный (row-store). Он гениален, когда нужно достать, вставить или обновить конкретные записи: это OLTP-нагрузка. Как только ты пишешь GROUP BY country по таблице на 50 миллионов строк, SQLite начинает честно читать каждую строку целиком, включая колонки, которые тебе не нужны. Отсюда минуты ожидания.

DuckDB поколоночный (column-store) и векторизованный. Тот же GROUP BY он выполнит, прочитав только нужные колонки и распараллелив работу. Плюс из коробки он понимает диалект, близкий к PostgreSQL: оконные функции, CTE, QUALIFY, LIST/STRUCT, богатый набор функций для дат и строк. Писать привычный аналитический SQL получается почти без адаптации.

Практический вывод простой. Нужно хранилище состояния для приложения, много мелких чтений и записей — бери SQLite. Нужно быстро посчитать метрики по большому выгруженному датасету на своей машине — бери DuckDB. Они не конкуренты, а инструменты для разных слоёв работы. И самое приятное: аналитический SQL, который ты пишешь в DuckDB, — джойны, группировки, оконные функции — переносится в PostgreSQL почти дословно.

Как читать Parquet и CSV прямо из SQL?

Это фича, ради которой многие и приходят в DuckDB. Тебе не нужно сначала загружать файл в таблицу — можно писать SELECT прямо по файлу, будто он уже таблица.

SELECT country, count(*) AS users
FROM read_csv_auto('users.csv')
GROUP BY country
ORDER BY users DESC;
read_csv_auto сам определит разделители, типы колонок и заголовок. С Parquet ещё удобнее — формат хранит схему внутри, поэтому типы подтягиваются точно:
SELECT
    date_trunc('day', ts) AS day,
    sum(revenue)          AS revenue
FROM 'logs/2026-*.parquet'
GROUP BY 1
ORDER BY 1;

Обрати внимание на маску 2026-*.parquet — DuckDB умеет читать сразу пачку файлов по glob-шаблону, как одну большую таблицу. Это спасает, когда выгрузка приезжает партициями по дням. Отдельный бонус: для Parquet работает projection pushdown — если в SELECT указаны две колонки из тридцати, с диска прочитаются только эти две. На широких таблицах разница в скорости огромная.

Когда датасет большой и запрашивать его будешь много раз, есть смысл материализовать в постоянную базу — это один файл .duckdb:

con = duckdb.connect('analytics.duckdb')
con.sql("CREATE TABLE events AS SELECT * FROM 'logs/2026-*.parquet'")
con.sql("SELECT country, sum(revenue) FROM events GROUP BY 1").show()

Обратная операция — сохранить результат обратно в Parquet — делается через COPY:

COPY (
    SELECT * FROM events WHERE country = 'RU'
) TO 'ru_events.parquet' (FORMAT parquet);

DuckDB или pandas — что выбрать для обработки данных?

Мой практичный ответ: считать агрегаты и джойны большими датасетами удобнее в DuckDB, а тонкую построчную доработку и визуализацию оставлять pandas. Это не «или-или», а разделение труда.

Разберём на примере. Скользящую среднюю выручку по дням в pandas ты напишешь чисто. Но фильтр, группировку и джойн трёх файлов на несколько миллионов строк pandas делает в памяти и целиком — на ноутбуке с 8 ГБ это легко упирается в потолок и роняет ядро. DuckDB тот же расчёт выполнит потоково, при нехватке памяти сольёт часть на диск (larger-than-memory), и не свалится. Плюс SQL для многих аналитиков просто читаемее, чем цепочка из merge, groupby, agg, reset_index.

Сравни две записи одной задачи — сумма по пользователю:

# pandas
import pandas as pd
orders = pd.read_parquet('orders.parquet')
res = (orders
       .groupby('user_id', as_index=False)['amount']
       .sum()
       .sort_values('amount', ascending=False))
# DuckDB — тот же результат
import duckdb
res = duckdb.sql("""
    SELECT user_id, sum(amount) AS amount
    FROM 'orders.parquet'
    GROUP BY user_id
    ORDER BY amount DESC
""").df()

Обе версии рабочие. Но на больших данных вторая обычно быстрее и надёжнее по памяти, а на очень больших — работает там, где pandas уже падает. При этом pandas никуда не уходит: часто финальный .df() из DuckDB нужен именно чтобы дальше построить график или докрутить логику в Python. Если хочется прокачать сам pandas параллельно, у нас есть Python-тренажёр с задачами на реальных датасетах.

Как соединить DuckDB с pandas в одном пайплайне?

Самое приятное — эти два инструмента не надо мостить руками. DuckDB видит переменные-DataFrame'ы прямо в твоём Python-скоупе и обращается к ним как к таблицам.

import duckdb
import pandas as pd

orders = pd.read_parquet('orders.parquet')
users  = pd.read_csv('users.csv')

# orders и users — обычные DataFrame'ы, но DuckDB видит их как таблицы
report = duckdb.sql("""
    SELECT u.country,
           count(DISTINCT o.user_id) AS buyers,
           sum(o.amount)             AS revenue
    FROM orders o
    JOIN users  u USING (user_id)
    GROUP BY u.country
    ORDER BY revenue DESC
""").df()

Здесь нет ни CREATE TABLE, ни INSERT, ни копирования данных. Ты просто пишешь SQL по объектам, которые уже лежат в памяти, а на выходе получаешь снова DataFrame через .df(). Так удобно строить гибридные пайплайны: тяжёлый джойн и агрегацию отдаёшь SQL-движку DuckDB, а лёгкую доводку и графики — pandas и matplotlib. Ровно этот же SQL с JOIN ... USING, count(DISTINCT ...) и группировкой пригодится и на собесе — потренировать формулировки можно на вопросах для аналитика и в SQL-справочнике.

Отдельно отмечу консольный режим. У DuckDB есть CLI, и это отличный способ быстро глянуть незнакомый Parquet, не открывая ноутбук с Python:

duckdb
D SELECT * FROM 'suspicious_export.parquet' LIMIT 5;
D DESCRIBE SELECT * FROM 'suspicious_export.parquet';
D SUMMARIZE SELECT * FROM 'suspicious_export.parquet';
DESCRIBE покажет схему, SUMMARIZE — статистику по колонкам: min, max, среднее, число уникальных, долю пропусков. За минуту понимаешь, что за файл тебе прислали.

Где DuckDB реально выручает аналитика

Соберу сценарии, в которых я тянусь именно за DuckDB, а не за тяжёлой инфраструктурой.

Разовый ad-hoc по большой выгрузке. Прислали дамп на 200 МБ Parquet, надо срочно посчитать конверсию по когортам. Поднимать под это ClickHouse или ETL — оверкилл. DuckDB считает на месте.

Локальная проверка гипотезы перед прод-запросом. Прежде чем гонять тяжёлый SQL по проду, я прогоняю логику на семпле в DuckDB — быстрее и никого не нагружаю. Диалект близок к PostgreSQL, так что запрос потом почти без правок уезжает в хранилище.

Считать продуктовые метрики по логам. Дневные активные, retention, воронка — всё это агрегаты по событиям, ровно то, где колоночный движок силён. Если сами определения метрик подзабылись, загляни в разбор метрики DAU — а сам расчёт по Parquet-логам ложится в DuckDB в один запрос:

SELECT
    date_trunc('day', ts) AS day,
    count(DISTINCT user_id) AS dau
FROM 'events/*.parquet'
WHERE event = 'app_open'
GROUP BY 1
ORDER BY 1;

Оконные функции по большим данным. ROW_NUMBER, LAG, running total по сессиям — DuckDB тянет их на объёмах, где pandas уже задыхается:

SELECT user_id, ts,
       row_number() OVER (PARTITION BY user_id ORDER BY ts) AS visit_n,
       lag(ts)      OVER (PARTITION BY user_id ORDER BY ts) AS prev_ts
FROM read_parquet('sessions.parquet');

Лёгкий слой трансформаций в проекте. Небольшие витрины можно собирать локально в один .duckdb-файл, версионировать SQL в репозитории и не держать под это отдельное хранилище.

Какие ограничения у DuckDB?

Честно про границы, чтобы не выстрелить себе в ногу.

DuckDB — не сервер и не заточен под высокую конкурентную запись. По умолчанию это модель «много читателей или один писатель». Строить на нём бэкенд с сотнями одновременных пишущих клиентов — не его задача, для этого есть PostgreSQL и полноценные хранилища.

Это не замена корпоративному DWH. Когда данные измеряются терабайтами, живут в кластере и их дёргают десятки дашбордов одновременно, нужен ClickHouse, распределённое хранилище или облачный warehouse. DuckDB блистает на «одна машина, большой, но обозримый датасет».

Быстрая эволюция. Проект активно развивается, и между версиями иногда меняется поведение или синтаксис расширений. Фиксируй версию в зависимостях проекта, чтобы пайплайн не поехал после случайного апгрейда.

И общий принцип: DuckDB — это про интерактивную аналитику и трансформации, а не про OLTP и не про онлайн-сервинг фичей. Держи его в правильном слое, и он окупится.

Как начать и где потренироваться

Стартовать можно за пять минут. pip install duckdb, затем import duckdb — и сразу пиши SELECT по своему CSV или Parquet, без создания таблиц. Дальше по нарастанию: попробуй read_csv_auto, потом glob-маску по папке файлов, потом .df() для перехода в pandas, потом постоянную базу через connect(). Для консольных проверок держи под рукой DESCRIBE и SUMMARIZE — они экономят кучу времени на разведке данных.

Но главный навык — не сам DuckDB, а SQL, который ты в нём пишешь. Джойны, агрегации, оконные функции и CTE работают одинаково и в DuckDB, и в PostgreSQL, и в аналитических хранилищах, и именно их спрашивают на собеседованиях. Прокачать эту базу можно системно: пройти курс по SQL с нуля, разобрать тестовые задания уровня реальных собесов и подтянуть Python через Python-справочник.

DuckDB — редкий случай, когда мощный инструмент не требует ни настройки, ни инфраструктуры, ни разрешения от админов. Поставил, направил на файл, получил ответ за секунды. Освой его как рабочий нож для локальной аналитики — и перестанешь ждать по десять минут там, где всё считается за пять секунд.

Хочешь довести SQL до автоматизма перед собесом? В Pro открыты все 425 SQL-задач, 402 Python-задачи, кейсы и метрики с автопроверкой — а первые задачи каждого раздела бесплатны без регистрации. Начни с тренажёра, а когда застрянешь — возвращайся к разбору и прогоняй задачу заново.

Отработай SQL на живых данных
545 SQL-задач в браузере с автопроверкой — первые открыты без регистрации.
Открыть SQL-тренажёр →