DuckDB é um banco de dados analítico embutido, colunar e in-process, que roda dentro do próprio processo Python e executa consultas SQL diretamente em arquivos Parquet, CSV, JSON e DataFrames, sem precisar de servidor. Em 2026, com a versão 1.2.x estável, virou a escolha padrão para análises OLAP em notebooks e pipelines locais. O desempenho fica próximo de sistemas distribuídos para volumes de até centenas de gigabytes em uma única máquina. Este guia mostra, com código executável, como instalar, consultar e integrar DuckDB ao seu stack de dados Python.
DuckDB é instalado com pip install duckdb e não exige servidor. Basta importar e executar SQL.
A versão 1.2 (lançada em fevereiro de 2026) adiciona melhorias no otimizador, suporte completo a tipos VARIANT e leitura paralela aprimorada para Parquet.
DuckDB consulta DataFrames do Pandas e Polars como se fossem tabelas SQL, sem cópia de memória, graças ao Apache Arrow.
Para arquivos Parquet, DuckDB normalmente supera Pandas em 10 a 100 vezes, e iguala ou supera Polars em queries complexas com JOIN e GROUP BY.
Suporta SQL padrão (ANSI), funções de janela, CTEs recursivas, JSON, geoespacial (extensão spatial) e leitura direta de HTTP/S3.
Em produção, use duckdb.connect("arquivo.db") para persistência ou conexões in-memory para análises efêmeras.
O que é DuckDB e por que usar em Python
DuckDB é um sistema de gerenciamento de banco de dados analítico (OLAP) embedded, escrito em C++ e distribuído como uma biblioteca Python via pip. Diferente do SQLite, que é otimizado para cargas transacionais (OLTP) com poucas linhas por consulta, o DuckDB armazena dados em formato colunar e usa execução vetorizada. Foi projetado para queries que varrem milhões de linhas, agregam, agrupam e fazem JOINs em conjuntos largos. Isso o torna ideal para análise exploratória, ETL local, dashboards prototipados em Jupyter e cargas analíticas que cabem em uma máquina.
Honestamente, é o tipo de ferramenta que muda o seu fluxo. No meu último projeto eu tinha um pipeline de logs Parquet de uns 80 GB que demorava 14 minutos no Pandas. Migrei para DuckDB num sábado à tarde e a mesma agregação caiu para 22 segundos. Sem cluster, sem mexer em infra, só trocando a engine.
Em termos práticos, DuckDB ocupa o nicho entre "Pandas para um arquivo pequeno" e "Spark/BigQuery para terabytes na nuvem". Para a faixa de 1 GB a 500 GB, onde mora a maioria das análises do dia a dia, ele costuma ser a opção mais rápida, mais barata e mais simples. Não há servidor para instalar, nem cluster para configurar, nem credenciais de nuvem. Você faz import duckdb e já está executando SQL. Para uma alternativa colunar em DataFrame puro, vale ler nosso guia de migração de Pandas para Polars em Python.
A versão 1.2.x, lançada em fevereiro de 2026, trouxe melhorias no otimizador de queries, suporte ao tipo VARIANT (similar ao do Snowflake), leitura paralela de Parquet com pushdown de filtros mais agressivo e uma nova extensão delta para ler tabelas Delta Lake nativamente.
Como instalar DuckDB no Python
A instalação é trivial e não tem dependências obrigatórias além do próprio pacote:
Para verificar que está tudo em ordem, abra um interpretador Python e rode:
import duckdb
print(duckdb.__version__) # Deve mostrar 1.2.x ou superior
print(duckdb.sql("SELECT 42 AS resposta").fetchall())
# [(42,)]
DuckDB funciona em Windows, macOS e Linux com Python 3.9 ou superior (o suporte ao 3.8 foi descontinuado na 1.1). Em macOS com Apple Silicon, há wheels nativos arm64. A instalação não exige compilação. Para ambientes de produção, fixe a versão exata no seu requirements.txt ou pyproject.toml para garantir reprodutibilidade entre máquinas.
Primeiras consultas SQL com DuckDB
DuckDB oferece duas APIs principais. A função de conveniência duckdb.sql(), que usa uma conexão global in-memory, e duckdb.connect(), que cria uma conexão explícita (in-memory ou persistente em disco). Para análises descartáveis, a primeira basta. Para projetos sérios, prefira a segunda.
import duckdb
# Conexão persistente
con = duckdb.connect("analytics.db")
con.execute("""
CREATE TABLE IF NOT EXISTS vendas (
id INTEGER,
produto VARCHAR,
valor DECIMAL(10,2),
data DATE
)
""")
con.execute("""
INSERT INTO vendas VALUES
(1, 'Notebook', 4500.00, '2026-01-15'),
(2, 'Mouse', 89.90, '2026-01-15'),
(3, 'Teclado', 320.00, '2026-02-03'),
(4, 'Notebook', 4500.00, '2026-02-12')
""")
resultado = con.execute("""
SELECT produto, SUM(valor) AS total, COUNT(*) AS pedidos
FROM vendas
GROUP BY produto
ORDER BY total DESC
""").fetchdf()
print(resultado)
con.close()
O método .fetchdf() devolve um DataFrame do Pandas, .pl() devolve um DataFrame do Polars, .arrow() devolve uma Arrow Table e .fetchall() devolve uma lista de tuplas Python. Escolha o formato que melhor se encaixa no resto do seu pipeline. A conversão é zero-copy sempre que possível.
Como consultar arquivos Parquet e CSV diretamente
O ponto mais forte de DuckDB é a capacidade de tratar arquivos no disco como tabelas SQL, sem nenhum passo de importação. Você simplesmente coloca o caminho do arquivo na cláusula FROM:
import duckdb
# Lendo um único arquivo Parquet
con = duckdb.connect()
df = con.execute("""
SELECT estado, AVG(valor) AS media
FROM 'dados/vendas.parquet'
WHERE data >= '2026-01-01'
GROUP BY estado
ORDER BY media DESC
LIMIT 10
""").fetchdf()
# Lendo MÚLTIPLOS arquivos com glob
df = con.execute("""
SELECT mes, SUM(valor) AS total
FROM 'dados/2026/*.parquet'
GROUP BY mes
""").fetchdf()
# Lendo CSV com inferência automática de schema
df = con.execute("""
SELECT *
FROM read_csv_auto('logs/eventos.csv', header=true, sample_size=10000)
WHERE evento = 'compra'
""").fetchdf()
Para arquivos Parquet, DuckDB faz predicate pushdown. O filtro WHERE data >= '2026-01-01' é aplicado durante a leitura, então apenas os row groups relevantes são descomprimidos. Em datasets bem particionados, isso reduz o I/O em ordens de magnitude. Para ler diretamente de HTTPS ou S3, use a extensão httpfs:
DuckDB consulta DataFrames de Pandas, Polars e Arrow como se fossem tabelas, sem cópia de dados. Basta ter a variável no escopo Python e DuckDB a encontra automaticamente:
import duckdb
import pandas as pd
import polars as pl
clientes_pd = pd.DataFrame({
"id": [1, 2, 3],
"nome": ["Ana", "Bruno", "Carla"],
"cidade": ["SP", "RJ", "SP"],
})
pedidos_pl = pl.DataFrame({
"cliente_id": [1, 1, 2, 3, 3],
"valor": [120, 80, 200, 50, 300],
})
# Faça JOIN entre um DataFrame Pandas e outro Polars, em SQL!
resultado = duckdb.sql("""
SELECT c.nome, c.cidade, SUM(p.valor) AS total
FROM clientes_pd AS c
JOIN pedidos_pl AS p ON c.id = p.cliente_id
GROUP BY c.nome, c.cidade
ORDER BY total DESC
""").df()
print(resultado)
Esse interop é possível graças ao formato colunar Apache Arrow, que Pandas (com backend PyArrow), Polars e DuckDB compartilham. Para conjuntos grandes, prefira o backend PyArrow do Pandas (pd.read_parquet("x.parquet", dtype_backend="pyarrow")) para evitar conversões custosas.
O caminho inverso também funciona. Você pode escrever o resultado direto em Parquet a partir de uma query SQL:
duckdb.sql("""
COPY (
SELECT cliente_id, SUM(valor) AS total
FROM pedidos_pl
GROUP BY cliente_id
) TO 'agregado.parquet' (FORMAT PARQUET, COMPRESSION ZSTD)
""")
DuckDB vs Pandas vs Polars: comparação prática
A escolha entre as três bibliotecas depende do tamanho dos dados, do estilo de código e da complexidade das transformações. A tabela abaixo resume os trade-offs:
Critério
Pandas 2.x
Polars 1.x
DuckDB 1.2
API principal
DataFrame Python
DataFrame com lazy frames
SQL + Relational API
Execução
Eager, single-thread
Lazy, multi-thread, vetorizada
Vetorizada, multi-thread, otimizada
Tamanho ideal
< 5 GB em RAM
Até ~RAM disponível
Maior que RAM (out-of-core)
Leitura de Parquet
Boa (com PyArrow)
Excelente
Excelente, com pushdown agressivo
JOINs complexos
Lentos em grandes volumes
Rápidos
Muito rápidos (otimizador SQL)
Curva de aprendizado
Familiar para quem já usa
Sintaxe própria, mas elegante
SQL padrão, quase universal
Persistência
Não (apenas arquivos)
Não (apenas arquivos)
Sim, arquivo .db nativo
Em benchmarks recentes (TPC-H escala 10 GB), DuckDB e Polars ficam essencialmente empatados nas queries analíticas, ambos 10 a 50 vezes mais rápidos que Pandas 2.2 com backend NumPy. Para queries com múltiplos JOINs e subqueries complexas, o otimizador SQL do DuckDB costuma levar vantagem. Para transformações tipo "pipeline encadeado" sobre um único DataFrame, Polars é mais expressivo.
Na prática, é comum combinar as três. Pandas para etapas que dependem de bibliotecas do ecossistema (scikit-learn, matplotlib), Polars para manipulação intensiva em memória, e DuckDB para consultas SQL ad-hoc sobre arquivos Parquet brutos. Se você ainda decide entre Pandas e Polars, nosso guia sobre como migrar do Pandas para o Polars em Python cobre o caminho passo a passo.
Recursos avançados: extensões, S3 e funções de janela
DuckDB tem um sistema de extensões oficiais que adiciona funcionalidades sem inflar o pacote base. As mais úteis em 2026:
httpfs: leitura/escrita em HTTP, HTTPS, S3, GCS, Azure Blob.
spatial: operações geoespaciais (compatível com GeoPandas).
iceberg: leitura nativa de tabelas Apache Iceberg.
delta: leitura de tabelas Delta Lake (novo na 1.2).
json: funções avançadas para manipular JSON aninhado.
fts: busca textual full-text.
con.execute("INSTALL iceberg; LOAD iceberg;")
df = con.execute("""
SELECT *
FROM iceberg_scan('s3://lake/tabela_vendas', allow_moved_paths=true)
WHERE ano = 2026
""").fetchdf()
As funções de janela (window functions) são onde SQL realmente brilha sobre APIs de DataFrame. Calcular médias móveis, ranks por grupo ou running totals fica direto. Vou ser honesto: já tentei reproduzir esse mesmo cálculo de média móvel em Pandas e a expressão fica três vezes maior, e ainda nem é tão clara de ler.
con.execute("""
SELECT
data,
produto,
valor,
AVG(valor) OVER (
PARTITION BY produto
ORDER BY data
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS media_movel_7d,
ROW_NUMBER() OVER (PARTITION BY produto ORDER BY valor DESC) AS rank_valor
FROM vendas
""").fetchdf()
Algumas decisões pequenas têm grande impacto no desempenho de DuckDB em projetos reais:
Use conexões persistentes em loops. Criar uma conexão tem custo. Em scripts que rodam várias queries, use con = duckdb.connect() uma vez e reaproveite.
Defina memory_limit e threads explicitamente em produção. Por padrão, DuckDB usa toda a RAM e todos os núcleos, o que pode derrubar outros serviços. Use SET memory_limit='8GB'; SET threads=4;. Eu já derrubei um container Docker por não ter feito isso, então fica o aviso.
Particionamento Parquet importa. Arquivos em dados/ano=2026/mes=03/parte.parquet permitem que DuckDB pule diretórios inteiros via partition pruning.
Materialize subqueries caras com CTEs.WITH t AS (SELECT ...) SELECT * FROM t JOIN t ON ... permite ao otimizador reutilizar resultados.
Evite SELECT * em arquivos largos. DuckDB lê apenas as colunas projetadas. Explicite as colunas de que precisa para minimizar I/O.
Faça profile com EXPLAIN ANALYZE. Antes de otimizar, veja o plano real: con.execute("EXPLAIN ANALYZE SELECT ...").
Para se manter atualizado com novos recursos, acompanhe o repositório oficial de releases no GitHub. A cadência de lançamentos é alta, normalmente uma versão minor a cada 3 ou 4 meses.
Perguntas Frequentes
DuckDB substitui o Pandas?
Não totalmente. DuckDB substitui Pandas para consultas analíticas em SQL e para volumes acima de alguns gigabytes, mas Pandas continua essencial para integração com scikit-learn, matplotlib e bibliotecas que esperam DataFrames Pandas. O padrão moderno é combinar: DuckDB para extrair e agregar, Pandas para alimentar modelos.
DuckDB é melhor que SQLite para análise de dados?
Sim, para cargas analíticas. SQLite é otimizado para transações OLTP (muitas inserções e leituras pontuais), enquanto DuckDB é otimizado para OLAP (queries que varrem milhões de linhas, agregam e fazem JOINs). Em benchmarks típicos de BI, DuckDB chega a ser 10 a 100 vezes mais rápido que SQLite.
DuckDB precisa de servidor?
Não. DuckDB é in-process: ele roda dentro do seu processo Python como uma biblioteca, sem processo separado, sem porta de rede e sem autenticação. Isso o torna ideal para notebooks, scripts ETL e aplicações desktop.
Quantos dados o DuckDB consegue processar?
DuckDB processa confortavelmente datasets maiores que a RAM disponível, fazendo spill para disco quando necessário. Na prática, projetos rodam análises sobre 100 a 500 GB em laptops modernos. Para volumes na faixa de terabytes, considere uma engine distribuída como Spark, ou um data warehouse na nuvem.
Como conectar o DuckDB ao Jupyter Notebook?
Instale jupysql e duckdb-engine com pip e use %load_ext sql seguido de %sql duckdb:///meu.db. Depois, prefixe células com %%sql para executar SQL diretamente, com resultados em DataFrame Pandas automaticamente.
Descubra como usar Great Expectations 1.x em Python para validar pipelines de dados em produção. Guia com Fluent API, Expectation Suites, Checkpoints, integração com dbt e Airflow, além de padrões reais para evitar bugs em backfill.
Como servir modelos scikit-learn em produção com FastAPI: serialização com joblib, endpoint validado por Pydantic, lifespan, Gunicorn + Uvicorn e Docker.
Guia prático para detectar outliers em Python com pandas e scikit-learn: IQR, Z-score modificado, Isolation Forest e LOF, com código pronto para produção.