DuckDB com Python permite executar SQL diretamente sobre arquivos CSV, Parquet e DataFrames, sem instalar nem administrar um servidor. A resposta direta é: instale o pacote duckdb, abra uma conexão e consulte a fonte com read_csv, read_parquet ou pelo nome de um DataFrame disponível no processo. Isso é especialmente útil em análise local, notebooks, pipelines e scripts que precisam combinar a expressividade do SQL com o ecossistema Python.

O DuckDB é um banco analítico embutido e colunar. “Embutido” significa que ele roda dentro do processo Python; “analítico” indica que foi projetado para varreduras, agregações e junções, não para milhares de pequenas transações concorrentes. Se você está começando na área, consulte também nosso guia de Python para Data Science e a introdução ao pandas para análise de dados.

Instalação e primeira consulta

Crie um ambiente isolado e fixe as dependências do projeto. O artigo sobre ambientes virtuais com venv mostra por que isso evita conflitos. Depois, instale os pacotes necessários:

python -m pip install duckdb pandas pyarrow

Uma conexão sem caminho vive em memória. Use-a com um gerenciador de contexto para liberar recursos mesmo quando ocorrer uma exceção:

import duckdb

with duckdb.connect() as con: total = con.execute(""" SELECT region, sum(valor) AS receita FROM read_csv('dados/vendas.csv', header = true) GROUP BY region ORDER BY receita DESC """).fetchall()

print(total)

fetchall() devolve tuplas Python. Para continuar a exploração de forma tabular, use .df() para pandas ou .arrow() para Arrow. Não materialize milhões de linhas sem necessidade: agregue, filtre e selecione colunas no SQL antes de buscar o resultado.

Consultar CSV com tipos previsíveis

read_csv detecta cabeçalho, delimitador e tipos, mas inferência não deve substituir um contrato de dados. Datas ambíguas, identificadores com zeros à esquerda e colunas mistas podem ser interpretados incorretamente. Em rotinas estáveis, informe opções e tipos relevantes:

sql = """
SELECT
    pedido_id,
    CAST(data_pedido AS DATE) AS data_pedido,
    valor
FROM read_csv(
    ?,
    header = true,
    delim = ';',
    columns = {
        'pedido_id': 'VARCHAR',
        'data_pedido': 'VARCHAR',
        'valor': 'DECIMAL(12,2)'
    }
)
WHERE valor >= ?
"""

with duckdb.connect() as con: pedidos = con.execute(sql, ['dados/pedidos.csv', 100]).df()

Os marcadores ? separam valores do texto SQL e evitam concatenação insegura. Parâmetros servem para caminhos e valores escalares, mas nomes de tabela ou coluna exigem composição controlada por uma lista permitida. Para detalhes sobre formatos tabulares no Python, veja manipulação de CSV e outros arquivos.

Por que Parquet combina com DuckDB

Parquet armazena dados por coluna e inclui metadados estatísticos. Assim, uma consulta que pede três colunas não precisa decodificar todas as demais, e filtros podem eliminar grupos de linhas. A documentação oficial explica a leitura e escrita de Parquet no DuckDB.

with duckdb.connect() as con:
    resumo = con.execute("""
        SELECT date_trunc('month', data) AS mes,
               categoria,
               avg(valor) AS ticket_medio
        FROM read_parquet('lake/vendas/**/*.parquet', hive_partitioning = true)
        WHERE data >= DATE '2026-01-01'
        GROUP BY ALL
        ORDER BY mes, categoria
    """).df()

O glob reúne vários arquivos com o mesmo esquema. hive_partitioning interpreta diretórios como ano=2026/mes=07 como colunas. Antes de confiar no conjunto, inspecione esquemas e trate evolução de colunas deliberadamente; arquivos incompatíveis podem exigir union_by_name = true, mas essa opção não corrige significados conflitantes.

SQL sobre DataFrames

O DuckDB encontra um DataFrame pandas pelo nome no escopo Python. O registro explícito, porém, deixa dependências claras e funciona melhor em funções:

import pandas as pd
import duckdb

clientes = pd.DataFrame({ 'cliente_id': [1, 2, 3], 'segmento': ['B2B', 'B2C', 'B2B'], })

with duckdb.connect() as con: con.register('clientes_df', clientes) ativos = con.execute(""" SELECT c.segmento, count(*) AS pedidos FROM read_parquet('dados/pedidos.parquet') p JOIN clientes_df c USING (cliente_id) WHERE p.status = 'pago' GROUP BY c.segmento """).df() con.unregister('clientes_df')

Esse padrão cruza uma dimensão pequena em memória com fatos no Parquet, sem exportar o DataFrame. Não altere o objeto em outra thread durante a consulta. Para operações vetoriais que permanecem em Python, o guia de NumPy complementa o fluxo.

Persistência, views e exportação

Troque duckdb.connect() por duckdb.connect('analytics.duckdb') para manter tabelas entre execuções. Uma view sobre arquivos guarda a consulta, não uma cópia dos dados. Uma tabela criada com CREATE TABLE AS materializa o resultado no banco.

with duckdb.connect('analytics.duckdb') as con:
    con.execute("""
        CREATE OR REPLACE VIEW vendas AS
        SELECT * FROM read_parquet('dados/vendas/*.parquet')
    """)
    con.execute("""
        COPY (
            SELECT categoria, sum(valor) AS total
            FROM vendas GROUP BY categoria
        ) TO 'saida/resumo.parquet' (FORMAT PARQUET)
    """)

Use transações quando várias alterações precisarem ser atômicas. Evite compartilhar a mesma conexão de modo desordenado entre threads e processos. A referência oficial da API Python do DuckDB detalha conexões, resultados e tipos; a página de importação de CSV lista as opções do leitor.

Erros comuns

  • Usar SELECT * sempre: aumenta leitura, transferência e acoplamento ao esquema. Nomeie apenas as colunas necessárias.
  • Confiar cegamente na inferência do CSV: valide tipos, nulos, delimitador, codificação e formato de datas.
  • Interpolar entradas em SQL: use parâmetros para valores e listas permitidas para identificadores.
  • Buscar tudo para pandas cedo demais: faça filtros, joins e agregações no DuckDB antes de materializar.
  • Tratar DuckDB como servidor OLTP: ele não substitui um banco transacional multiusuário para a aplicação.
  • Ignorar caminhos relativos: resolva caminhos a partir de uma base conhecida para que scripts funcionem fora do notebook.

Checklist para uma análise confiável

  1. Crie ambiente virtual e registre versões das dependências.
  2. Defina esquema, formato de datas e política de valores nulos.
  3. Selecione colunas e aplique filtros antes de materializar resultados.
  4. Parametrize valores externos e valide identificadores dinâmicos.
  5. Confira contagens, chaves de junção e duplicidades com consultas de controle.
  6. Escolha conexão em memória para trabalho descartável ou arquivo para persistência.
  7. Exporte resultados em Parquet quando tipos e eficiência de leitura importarem.
  8. Teste o script com arquivos vazios, esquemas alterados e dados inválidos.

DuckDB é uma escolha direta quando os dados estão em arquivos ou objetos Python e o trabalho é analítico. Comece com uma consulta pequena, torne tipos e caminhos explícitos e deixe o mecanismo reduzir os dados antes de entregá-los ao pandas. Esse desenho mantém o código simples, reproduzível e mais fácil de revisar.