Pular para conteúdo

sidra-sql — referência

Parte da documentação do sidra-sql. Instalação, configuração e o exemplo rápido ficam na página principal.

Governança de Dados: Tratamento de Revisões

IBGE frequentemente publica revisões de dados. O warehouse preserva o histórico em vez de sobrescrever, via SCD Type II nas colunas ativo + modificacao — o mecanismo de desativar-e-inserir está detalhado em Bulk Load & Versionamento de Revisões. Na prática, isso permite duas consultas:

Consultando Snapshots Históricos

from datetime import date
from sqlalchemy import select, and_
from sidra_sql.database import get_engine
from sidra_sql.config import Config
from sidra_sql.models import Dados

engine = get_engine(Config())

# Data as it existed on 2024-06-01
snapshot_date = date(2024, 6, 1)

with engine.connect() as conn:
    rows = conn.execute(
        select(Dados).where(
            and_(
                Dados.tabela_sidra_id == "1620",
                Dados.modificacao <= snapshot_date,
                Dados.ativo == True,
            )
        )
    ).fetchall()

Trilha de Auditoria

from sqlalchemy import select, and_
from sidra_sql.models import Dados

# Todas as versões de um ponto de dados específico
with engine.connect() as conn:
    history = conn.execute(
        select(Dados).where(
            and_(
                Dados.tabela_sidra_id == "1620",
                Dados.localidade_id == 6,
                Dados.periodo_id == 123,
            )
        ).order_by(Dados.modificacao)
    ).fetchall()

for row in history:
    status = "ATIVO" if row.ativo else "SUBSTITUÍDO"
    print(f"{row.modificacao}: {row.v}  [{status}]")

Ingestão Streaming

A carga usa COPY FROM STDIN em dois passes (resolve dimensões em memória, depois faz o streaming dos fatos para staging) — ~400k linhas/s, 10M linhas em segundos contra horas com INSERT tradicional. O mecanismo, os diagramas e o comparativo de performance estão em Bulk Load & Versionamento de Revisões.

Em código, toda a carga fica atrás de um único helper:

from sidra_sql.database import get_engine, load_dados
from sidra_sql.storage import Storage
from sidra_sql.config import Config

config = Config()
engine = get_engine(config)
storage = Storage.default(config)

# data_files é a lista retornada por Fetcher.download_periods(plan)
load_dados(engine, storage, data_files)  # COPY FROM STDIN, two-pass

Referência de API

CLI

Executar pipelines

sidra-sql run <alias> [pipeline_id] [--force-metadata]

Executa pipeline(s) de um plugin instalado. Omita pipeline_id para executar todos os pipelines do plugin. --force-metadata re-busca metadados da tabela SIDRA mesmo se já estiverem em cache.

sidra-sql run-path <path> [--force-metadata]

Executa um pipeline diretamente a partir de um diretório, sem plugin registrado.

sidra-sql transform <alias> <pipeline_id>

Executa apenas a etapa de transformação SQL de um pipeline (sem fetch nem load).

Gerenciar plugins

sidra-sql plugin install <url> [--alias ALIAS]
sidra-sql plugin update [alias]
sidra-sql plugin remove <alias>
sidra-sql plugin list
sidra-sql plugin validate [alias] [--plugin-dir PATH]
sidra-sql plugin scaffold <name>
    [-d/--description TEXT]
    [--version TEXT]          # padrão: 1.0.0
    [-o/--output-dir PATH]    # padrão: diretório atual
    [--git-init/--no-git-init]  # padrão: --git-init

Cria a estrutura de arquivos (manifest.toml, fetch.toml, transform.toml, .sql, README.md) para um novo plugin, com git init opcional.

sidra-sql plugin add-pipeline <pipeline_id>
    [-d/--description TEXT]
    [-p/--path PATH]          # caminho relativo ao plugin (padrão: pipeline_id)
    [--plugin-dir PATH]       # diretório raiz do plugin (padrão: diretório atual)

Adiciona um novo pipeline a um plugin existente, atualizando manifest.toml.

Configuração

sidra-sql config set <section.option> <value> [--global]
sidra-sql config get <section.option>
sidra-sql config list [--global] [--local]

Gerencia config.ini. Sem --global, lê/escreve o arquivo local. Com --global, usa ~/.config/quantilica/sidra-sql/config.ini. O config list sem flags mostra a visão mesclada (local sobrescreve global).


API Python (resumo)

Os pontos de entrada do pacote, na ordem de um ETL. Assinaturas completas no código-fonte do repositório.

Símbolo Papel
Config() Lê config.ini do diretório atual (storage + conexão PostgreSQL)
Fetcher(config, storage=, max_workers=) Extração: fetch_metadata(tabela), plan_periods(tabela, territories, variables), download_periods(plan)
Storage.default(config) Arquivos JSON locais: write_data, read_data, write_metadata
TomlScript(config, toml_path) ETL declarativo: baixa + carrega as tabelas do fetch.toml
TransformRunner(config, toml_path) Executa o transform.toml + .sql pareado
get_engine, save_agregado, load_dados Helpers de banco: engine, upsert de metadados, bulk load via COPY

Formato de Pipeline TOML

fetch.toml — Configuração de Extração

[[tabelas]]
tabela_sidra = "1620"          # código da tabela SIDRA (obrigatório)
variables = ["116"]             # códigos de variáveis; omitir para todas
territories = {6 = []}          # {nível: [códigos]}; lista vazia = todas
unnest_classifications = true   # expandir todos os cross-produtos de categorias

[[tabelas]]
tabela_sidra = "1737"
variables = ["63"]
territories = {1 = [], 3 = []}
split_variables = true          # uma requisição por variável (evita limites SIDRA)
classifications = {81 = ["allxt"]}

Campos de [[tabelas]]:

Campo Tipo Obrigatório Descrição
tabela_sidra str ✅ Código da tabela SIDRA
territories dict[str, list] ✅ Nível territorial → códigos de unidade
variables list[str] Códigos de variáveis (padrão: todas)
classifications dict[str, list] Classificação → códigos de categorias
unnest_classifications bool Expandir todas as combinações de categorias
split_variables bool Uma requisição por variável

transform.toml — Configuração de Transformação

[[table]]
name = "pib_municipal"          # nome da tabela/view alvo
schema = "analytics"            # schema alvo
strategy = "replace"            # "replace" (tabela) ou "view"
sql = "transform.sql"           # arquivo .sql pareado
description = "PIB Municipal"
primary_key = ["municipio_id", "ano"]
indexes = [
  { name = "idx_pib_ano", columns = ["ano"], unique = false }
]

Pareado com arquivo transform.sql (mesmo nome) contendo consulta SELECT.

manifest.toml — Registro de Plugin

name = "Pipelines Padrão"
description = "Pipelines SIDRA padrão Quantilica"
version = "1.0.0"

[[pipeline]]
id = "pib_municipal"
description = "PIB Municipal da tabela SIDRA 5938 IBGE"
path = "pib_municipal"

Schema Dimensional

O star schema (diagrama acima) vive no schema ibge_sidra: três dimensões — localidade (malha territorial), periodo (tempo normalizado com ano/mes/trimestre/data_inicio/data_fim) e dimensao (variável × até 6 classificações) — mais a tabela-fato enxuta dados (FKs + modificacao + ativo + v) e o registro tabela_sidra (metadados em JSONB). As colunas e constraints completas de cada tabela estão nos modelos SQLAlchemy do repositório.

Exemplo de Consulta Analítica

from sqlalchemy import select
from sidra_sql.models import Dados, Localidade, Periodo, Dimensao

# PIB por estado: tabela 5938 (PIB dos Municípios, anual, com níveis N1, N2, N3 e N6),
# variável 37 (PIB a preços correntes), apenas registros ativos
query = (
    select(
        Localidade.d1n.label("estado"),
        Periodo.codigo.label("periodo"),
        Dimensao.d2n.label("variavel"),
        Dados.v.label("valor"),
    )
    .join(Localidade, Dados.localidade_id == Localidade.id)
    .join(Periodo,    Dados.periodo_id    == Periodo.id)
    .join(Dimensao,   Dados.dimensao_id   == Dimensao.id)
    .where(
        Dados.ativo == True,
        Localidade.nc == "N3",          # estados
        Dados.tabela_sidra_id == "5938",
        Dimensao.d2c == "37",           # PIB a preços correntes
    )
    .order_by(Periodo.codigo.desc())
)

with engine.connect() as conn:
    for row in conn.execute(query):
        print(f"{row.estado} | {row.periodo} | {row.valor}")

Performance

Cache Local

Dados baixados são armazenados como arquivos JSON sob data_dir. Re-executar um pipeline pula arquivos que já existem em disco:

Primeira execução:  baixa do IBGE (segundos a minutos)
Re-execução:        verifica cache local, pula arquivos existentes (overhead <1s)

Force re-download deletando o diretório de dados. A flag --force-metadata renova apenas os metadados.

Melhores Práticas para Governança de Dados

1. Use Pipelines Declarativos (TOML) para Reprodutibilidade

# pipelines/annual_snapshot/fetch.toml
[[tabelas]]
tabela_sidra = "1620"
variables = ["116"]
territories = {6 = [], 3 = []}

[[tabelas]]
tabela_sidra = "1737"
variables = ["63"]
territories = {1 = []}

Vantagens:

  • ✅ Não-desenvolvedores podem manter pipelines
  • ✅ Controle de versão (TOML em git)
  • ✅ Reprodutível entre máquinas
  • ✅ Desacoplado de mudanças de código

2. Documente Datas de Snapshot para Reprodutibilidade Acadêmica

import json
from datetime import datetime

# Registrar quando você construiu o dataset
metadata = {
    "snapshot_date": datetime.now().isoformat(),
    "pipeline": "pipelines/analysis/fetch.toml",
    "sidra_sql_version": "1.2.0",
}
with open("data/metadata.json", "w") as f:
    json.dump(metadata, f, indent=2)

Então consulte com Dados.modificacao <= snapshot_date para reproduzir o dataset exato.

3. Use Star Schema para Ferramentas BI

Conecte PostgreSQL diretamente a:

  • Tableau (conexão ODBC para PostgreSQL)
  • Power BI (conector nativo PostgreSQL)
  • Looker (SQL runner)
  • Metabase (consultas SQL em warehouse)

Schema normalizado é otimizado para workloads OLAP de ferramentas BI.


Resolução de Problemas

Download Falha com Erro de Rede

Fetcher retenta automaticamente (até 5 vezes, backoff exponencial: 5s → 10s → 20s…). Se todas as tentativas falharem, verifique disponibilidade da API SIDRA ou reduza max_workers.

Tabela Não Encontrada

Códigos de tabela SIDRA devem ser strings, não inteiros. Use "1620", não 1620.

metadata = fetcher.fetch_metadata("1620")   # ✅
metadata = fetcher.fetch_metadata(1620)     # ❌ por convenção (o código converte com int())

ID de Variável Desconhecido

Procure no catálogo SIDRA IBGE diretamente em sidra.ibge.gov.br para procurar códigos de variáveis e classificações para uma tabela dada.

Arquivo de Configuração Não Encontrado

Config() lê config.ini do diretório de trabalho atual. Execute Python da raiz do projeto, ou defina o caminho explicitamente:

import os
os.chdir("/path/to/project")
config = Config()

Schema Já Existe

TransformRunner com strategy = "replace" descarta e recria a tabela alvo. strategy = "view" usa CREATE OR REPLACE VIEW e é não-destrutivo.