DuckDB com Python: Guia Completo de Análise de Dados em 2026

DuckDB é um banco analítico embutido que executa SQL direto em Parquet, CSV e DataFrames. Guia com instalação, queries e otimizações para 2026.

DuckDB Python: Guia de Análise 2026

Atualizado: 09 de junho de 2026

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:

pip install duckdb
# Opcional, recomendado para integração ampla:
pip install "duckdb[pandas,polars,pyarrow]"

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:

con.execute("INSTALL httpfs; LOAD httpfs;")
df = con.execute("""
    SELECT *
    FROM 's3://meu-bucket/dados/2026/*.parquet'
    LIMIT 100
""").fetchdf()

Integração com Pandas e Polars

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érioPandas 2.xPolars 1.xDuckDB 1.2
API principalDataFrame PythonDataFrame com lazy framesSQL + Relational API
ExecuçãoEager, single-threadLazy, multi-thread, vetorizadaVetorizada, multi-thread, otimizada
Tamanho ideal< 5 GB em RAMAté ~RAM disponívelMaior que RAM (out-of-core)
Leitura de ParquetBoa (com PyArrow)ExcelenteExcelente, com pushdown agressivo
JOINs complexosLentos em grandes volumesRápidosMuito rápidos (otimizador SQL)
Curva de aprendizadoFamiliar para quem já usaSintaxe própria, mas eleganteSQL padrão, quase universal
PersistênciaNã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()

Consulte a documentação oficial de window functions para a lista completa de operadores suportados.

Boas práticas e otimização de desempenho

Algumas decisões pequenas têm grande impacto no desempenho de DuckDB em projetos reais:

  1. 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.
  2. 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.
  3. Particionamento Parquet importa. Arquivos em dados/ano=2026/mes=03/parte.parquet permitem que DuckDB pule diretórios inteiros via partition pruning.
  4. Materialize subqueries caras com CTEs. WITH t AS (SELECT ...) SELECT * FROM t JOIN t ON ... permite ao otimizador reutilizar resultados.
  5. Evite SELECT * em arquivos largos. DuckDB lê apenas as colunas projetadas. Explicite as colunas de que precisa para minimizar I/O.
  6. 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.

Editorial Team
Sobre o Autor Editorial Team

Our team of expert writers and editors.