Extraindo dados do SQL Server para Parquet com Python

Por que extraí dados do SQL Server para Parquet

Trabalho com dados em SQL Server e precisava alimentar ferramentas analíticas sem sobrecarregar o banco transacional. Depois de tentar conexão direta e CSV, cheguei no Apache Parquet — um formato aberto, colunar e comprimido que se tornou o padrão de fato para dados analíticos.

Participe do nosso grupo de Estudos!!! Cadastre-se em  https://gubigdata.com.br/grupo-de-estudos/

O problema: SQL Server como fonte de analytics

O SQL Server é excelente como banco transacional — ACID, integridade referencial, stored procedures. Mas quando comecei a rodar análises sobre grandes volumes de dados, encontrei limitações:

  • Formato orientado a linhas — para ler uma coluna específica de 1 milhão de registros, ele precisa varrer todas as linhas e todas as colunas
  • Licenciamento — a versão Express tem limite de 10 GB por banco e 1 GB de RAM
  • Concorrência — minhas queries analíticas pesadas competiam com o sistema transacional em produção
  • Portabilidade — os dados ficavam presos no ecossistema Microsoft

A pergunta que me fiz não foi “como conectar minha ferramenta ao SQL Server”, mas sim: “como libertar meus dados para que qualquer ferramenta possa consumi-los?”

Por que Parquet e não CSV, JSON ou conexão direta?

CSV: simples mas limitado

CSV não tem tipos. Um campo 123.45 é texto — cabe a quem lê decidir se é string, float ou decimal. Nos meus dados financeiros, essa ambiguidade era inaceitável. Além disso, CSV não comprime bem e não suporta NULLs nativos (é "" ou "NULL"?).

JSON: flexível mas pesado

JSON carrega metadados em cada registro (nome do campo repetido em toda linha). Um arquivo JSON com 1 milhão de registros ficava facilmente 5x maior que o equivalente em Parquet.

Conexão direta: prática mas frágil

Em produção, as queries pesadas travavam o sistema transacional, o SQL Server virava gargalo, e qualquer manutenção no banco derrubava meus dashboards.

Parquet: o melhor dos mundos

O Apache Parquet resolveu todos esses problemas:

  • Formato colunar — se preciso de 3 colunas de uma tabela com 40, ele lê apenas essas 3. Isso mudou completamente a performance das minhas queries analíticas
  • Schema tipado embutido — cada arquivo carrega a definição dos tipos (string, decimal com precisão, date, boolean). Sem ambiguidade
  • Compressão eficiente — dados colunares comprimem muito melhor que dados em linha. Valores repetidos na mesma coluna (como status, datas, flags) comprimem drasticamente
  • NULLs nativos — NULL é NULL, não string vazia
  • Portável — qualquer ferramenta moderna lê Parquet nativamente
  • Imutável — uma vez gerado, o arquivo é uma foto consistente dos dados. Sem surpresas de concorrência

Quem lê Parquet?

Praticamente tudo no ecossistema de dados moderno:

  • ClickHouse — lê Parquet diretamente com SELECT * FROM file('dados.parquet')
  • DuckDB — banco analítico embarcado, ideal para explorar Parquet no notebook ou terminal
  • chDB — engine do ClickHouse embarcada em Python, sem servidor
  • Apache Spark — Parquet é o formato nativo do Spark
  • Pandas / Polars — pd.read_parquet() é mais rápido que read_csv() e preserva tipos
  • Power BI / Tableau — suportam Parquet como fonte de dados
  • AWS Athena / Google BigQuery — consulta Parquet diretamente no storage (S3/GCS)

Ou seja: ao exportar para Parquet, desacopla os dados do SQL Server e os torna consumíveis por qualquer ferramenta, em qualquer cloud, em qualquer linguagem.

As ferramentas

pyodbc — a ponte com o SQL Server

pyodbc é o driver Python para conexões ODBC. Ele se conecta ao SQL Server usando o ODBC Driver 17 ou 18 da Microsoft e retorna os dados em tipos Python nativos. Suporta autenticação Windows (quando o script roda na mesma máquina) e autenticação SQL (user/password para conexões remotas).

pandas — leitura em chunks

pandas tem uma funcionalidade essencial para tabelas grandes: o parâmetro chunksize no pd.read_sql(). Em vez de carregar 800 mil linhas na memória de uma vez, leio em batches de 50 mil. O uso de memória fica constante independente do tamanho da tabela.

PyArrow — escrita Parquet com tipos precisos

PyArrow é a implementação Python do Apache Arrow. Ele escreve arquivos Parquet com schemas tipados — defino exatamente o tipo de cada coluna. Isso é crítico para dados financeiros: um decimal(26,2) do SQL Server precisa virar decimal128(26,2) no Parquet, não float64 que perde centavos.

O ponto crítico: mapeamento de tipos

A parte mais importante da extração não foi a conexão nem a leitura — foi garantir que os tipos fossem preservados corretamente. Um erro aqui corrompe os dados silenciosamente.

Os casos que merecem atenção:

  • decimal → decimal128 — nunca converta para float. O número 0.10 não tem representação exata em float64. Para valores financeiros, a perda de centavos é inaceitável
  • uniqueidentifier → string — UUIDs do SQL Server são armazenados como 16 bytes binários. No Parquet, a representação mais portável é string lowercase ("a1b2c3d4-...")
  • nvarchar(MAX) → string UTF-8 — o SQL Server armazena nvarchar como UTF-16. O driver ODBC converte para UTF-8 na leitura, mas vale validar caracteres especiais
  • bit → boolean — parece trivial, mas se você deixar o PyArrow inferir, ele pode tratar como inteiro
  • NULLs — o Parquet suporta NULLs nativos em qualquer tipo. Não converta para string vazia ou zero

A solução que foi definir schemas PyArrow explícitos para cada tabela, sem depender de inferência automática. É mais trabalho inicial, mas elimina surpresas.

SQL Server Express

Meu SQL Server era a versão Express, e antes de conseguir conectar remotamente precisei configurar várias coisas:

  • TCP/IP vem desabilitado — o Express só aceita conexões locais via shared memory. Precisei habilitar TCP/IP no Configuration Manager e definir a porta 1433
  • Autenticação só Windows — para conectar do Linux/WSL2, precisei habilitar o modo misto (SQL + Windows Auth)
  • Firewall bloqueia — o script rodava no WSL2, e precisei criar regra no firewall do Windows para a porta 1433
  • Instância nomeada — o Express usa localhost\SQLEXPRESS em vez da porta padrão. Sem o SQL Browser rodando, precisei configurar porta fixa

Documentei todos os passos de configuração no repositório do projeto do github AQUI.

Resultado

Teste com 6 tabelas e 1,3 milhão de registros:

  • Tempo de extração: 28 segundos
  • Tamanho em Parquet: 250 MB (com compressão Snappy)
  • Memória: constante em ~200 MB (leitura em chunks de 50k linhas)
  • Tipos preservados: decimal com precisão exata, UUIDs, datas, booleanos

A partir desses Parquet, os dados podem ser carregados no ClickHouse, consultados com DuckDB, ou consumidos por qualquer ferramenta analítica — sem depender do SQL Server estar online.

Quando usar essa abordagem

Faz sentido quando:

  • Você precisa alimentar ferramentas analíticas sem sobrecarregar o SQL Server
  • Está migrando para uma stack de dados moderna (ClickHouse, DuckDB, Spark, cloud)
  • Precisa de snapshots consistentes dos dados para auditoria ou reprocessamento
  • Quer desacoplar a camada de analytics do banco transacional
  • Precisa compartilhar datasets com times que não têm acesso ao SQL Server

Não faz sentido quando:

  • Você precisa de dados em tempo real (Parquet é batch, não streaming)
  • O volume é pequeno (menos de 10 mil linhas) e a consulta é ocasional
  • O SQL Server já atende bem suas necessidades analíticas

Cadastre-se no nosso grupo de estudos!

Código e documentação

O script completo, com schemas de exemplo, documentação de setup do SQL Server Express, e instruções de uso, está disponível no GitHub. O README inclui um guia de como adaptar para suas próprias tabelas.

Alessandro Binhara