DEV Community

Cover image for Modelagem de Dados no Agro: Unindo fluxo de caixa e Ganho Médio Diário (GMD) no mesmo Banco de Dados
Davi Domingos de Oliveira
Davi Domingos de Oliveira

Posted on

Modelagem de Dados no Agro: Unindo fluxo de caixa e Ganho Médio Diário (GMD) no mesmo Banco de Dados

1. Introdução: O Problema Real do Produtor

Quando se fala em tecnologia no agronegócio, é comum pensarmos imediatamente no "Agro 4.0": tratores autônomos, drones escaneando lavouras e sensores IoT de última geração. Porém, essa é a realidade de uma minoria absoluta. Existe um abismo tecnológico silencioso no campo brasileiro.

A pecuária sozinha representa cerca de 6% a 7% de todo o PIB nacional. No entanto, 77% das propriedades rurais do país pertencem a pequenos e médios produtores. E como a grande maioria desses produtores gerencia a complexidade de uma fazenda — calculando custos de suplementação, flutuação do preço da arroba e o Ganho Médio Diário (GMD) de dezenas ou centenas de animais? Usando cadernos de papel, quadros brancos ou planilhas genéricas que não se conversam.

Para nós, desenvolvedores, o nome disso é Dark Data (dados escuros). São milhares de pontos de dados gerados diariamente que são perdidos ou subutilizados porque não estão estruturados em um banco de dados relacional. O lucro na pecuária moderna está espremido em centavos, e tomar decisões baseadas em dados não estruturados é um gargalo de bilhões de reais.

Neste artigo, quero explorar a arquitetura do Sistema de Gestão de Gado (SGG), um ERP zootécnico que construí para estruturar essa "cauda longa" da pecuária, focando em como resolvi o maior gargalo de performance do projeto.

2. O Desafio Técnico: Python vs. Banco de Dados

2.1. Entendendo o Gargalo (A Matemática do GMD)

O GMD (Ganho Médio Diário), é a estimativa de crescimento diário de um animal, geralmente medido em Kg.
Numa tabela estruturada como:

        CREATE TABLE IF NOT EXISTS pesagens (
        id INT AUTO_INCREMENT PRIMARY KEY,
        animal_id INT NOT NULL,
        data_pesagem DATE NOT NULL,
        peso DECIMAL(10, 2) NOT NULL,
        deleted_at DATETIME NULL DEFAULT NULL,
        FOREIGN KEY (animal_id) REFERENCES animais(id)
Enter fullscreen mode Exit fullscreen mode

O cálculo do GMD é dado como (Último Peso - Primeiro Peso)/Dias entre o primeiro e o último peso.

Numa fazenda, essa estatística afeta diretamente a tomada de decisão. É essa métrica que diz ao pecuarista se a dieta de nutrição está funcionando ou se o animal está apenas dando prejuízo.

O problema real começa quando tentamos transformar essa fórmula simples em código escalável

2.2. A Armadilha de Processar no Backend (O Risco do Python)

A minha primeira ideia foi usar o MySQL apenas para guardar as informações e usar o backend em Python para lidar com os cálculos, algo semelhante a:

cursor.execute("SELECT animal_id, peso, data_pesagem FROM pesagens WHERE deleted_at IS NULL")
todas_pesagens = cursor.fetchall() # <--- O GARGALO COMEÇA AQUI

rebanho_gmd = {}

# 2. O Python tentando fazer o trabalho do Banco de Dados
for registro in todas_pesagens:
    animal_id = registro['animal_id']

    if animal_id not in rebanho_gmd:
        rebanho_gmd[animal_id] = {'pesos': [], 'datas': []}

    rebanho_gmd[animal_id]['pesos'].append(registro['peso'])
    rebanho_gmd[animal_id]['datas'].append(registro['data_pesagem'])

# 3. Calculando o GMD animal por animal com loops (Alto consumo de CPU)
resultados = []
for animal_id, dados in rebanho_gmd.items():
    peso_inicial = min(dados['pesos'])
    peso_final = max(dados['pesos'])

    data_inicial = min(dados['datas'])
    data_final = max(dados['datas'])

    dias = (data_final - data_inicial).days

    if dias > 0:
        gmd = (peso_final - peso_inicial) / dias
        resultados.append({'animal_id': animal_id, 'gmd': gmd})
Enter fullscreen mode Exit fullscreen mode

Porém, após a primeira bateria de testes, percebi que essa escolha era ineficiente, pois o backend ficaria responsável por boa parte dos cálculos brutos, aumentando drasticamente o consumo de RAM pelo servidor. A consequência direta disso seria a lentidão e o travamento do sistema à medida que o projeto escalasse para mais fazendas e rebanhos maiores.

3. A Solução: Inteligência no Banco de Dados

Para resolver o problema do alto consumo de RAM que mostrei no código anterior, decidi inverter a lógica. Em vez de trazer milhares de linhas brutas para o Python e forçar a aplicação a processá-las, por que não fazer a matemática onde os dados já estão armazenados?

Foi assim que estruturei a arquitetura "Performance-First" do Sistema de Gestão de Gado (SGG), delegando o processamento pesado ao MySQL.

3.1. Views SQL Otimizadas e Window Functions

A peça central dessa solução foi a criação de Views SQL. Uma View funciona como uma "tabela virtual". Em vez de calcular o Ganho Médio Diário (GMD) no backend em tempo real, eu ensino a fórmula matemática ao banco de dados.

Para garantir que o cálculo considerasse a ordem cronológica exata das pesagens (mesmo que um animal perdesse peso no período), utilizei Common Table Expressions (CTEs) e Window Functions (ROW_NUMBER).

Veja como estruturei a View v_gmd_analitico no meu banco para resolver esse cálculo complexo de forma automática:

CREATE OR REPLACE VIEW v_gmd_analitico AS
WITH PesagensOrdenadas AS (
    SELECT 
        animal_id, data_pesagem, peso,
        ROW_NUMBER() OVER(PARTITION BY animal_id ORDER BY data_pesagem ASC) as rn_asc,
        ROW_NUMBER() OVER(PARTITION BY animal_id ORDER BY data_pesagem DESC) as rn_desc
    FROM pesagens
    WHERE deleted_at IS NULL
),
PrimeiraUltima AS (
    SELECT 
        animal_id,
        MAX(CASE WHEN rn_asc = 1 THEN data_pesagem END) AS data_inicial,
        MAX(CASE WHEN rn_asc = 1 THEN peso END) AS peso_inicial,
        MAX(CASE WHEN rn_desc = 1 THEN data_pesagem END) AS data_final,
        MAX(CASE WHEN rn_desc = 1 THEN peso END) AS peso_final
    FROM PesagensOrdenadas
    GROUP BY animal_id
)
SELECT 
    a.user_id, a.id as animal_id, a.brinco,
    p.peso_final,
    (p.peso_final - p.peso_inicial) as ganho_total,
    DATEDIFF(p.data_final, p.data_inicial) as dias,
    CASE 
        WHEN DATEDIFF(p.data_final, p.data_inicial) > 0 
        THEN (p.peso_final - p.peso_inicial) / DATEDIFF(p.data_final, p.data_inicial)
        ELSE 0 
    END as gmd
FROM PrimeiraUltima p
JOIN animais a ON p.animal_id = a.id
WHERE p.data_inicial <> p.data_final
  AND a.deleted_at IS NULL;

Enter fullscreen mode Exit fullscreen mode

"A mágica acontece aqui: Quando o Flask precisa processar os indicadores do pecuarista, ele não faz mais laços de repetição infinitos e não consome RAM desnecessária. A listagem principal consulta diretamente a tabela base de animais de forma paginada, enquanto a View é acionada cirurgicamente para as métricas complexas. O banco já resolve o agrupamento e previne divisões por zero, entregando o dado zootécnico pronto para extração de médias globais (ex: SELECT AVG(gmd) FROM v_gmd_analitico) ou para a montagem instantânea da ficha detalhada de cada animal.

3.2. Índices Compostos para Buscas Instantâneas

Ter a View resolveu o problema do cálculo do GMD, mas e quando o produtor quisesse buscar os custos de um lote específico? Para evitar que o MySQL fizesse um Full Table Scan (varrer a tabela inteira a cada clique), a modelagem precisava ser inteligente.

Implementei índices compostos estruturais, como o idx_pesagens_otimizada e o idx_custos_busca. Isso garantiu que, mesmo que o cliente cadastre milhares de animais ao longo dos anos, os filtros de busca continuem respondendo em milissegundos.

3.3. Paginação Server-Side e Pool de Conexões

Para fechar o "pacote" de alta performance, apliquei duas últimas regras na comunicação entre a API e o Banco:

  1. Server-Side Pagination: O painel principal nunca carrega todos os animais de uma vez. Utilizo LIMIT e OFFSET diretamente nas queries do MySQL, enviando para o navegador apenas o que cabe na tela.
  2. Connection Pooling: Usando a biblioteca mysql-connector-python, configurei um pool de conexões logo no setup inicial do banco. Isso evita o overhead gigantesco de abrir e fechar uma nova conexão TCP a cada nova requisição do usuário.

4. Resultados e Impacto Real

Na engenharia de software, a melhor arquitetura não é a que usa o maior número de ferramentas da moda, mas a que resolve o gargalo do usuário de forma inteligente. Ao adotar essa abordagem focada no banco de dados, o impacto foi imediato em duas frentes:

4.1. O Impacto no Negócio (O fim do Dark Data)

O pecuarista que antes gastava cerca de 2 horas cruzando dados de pesagens de cadernos para planilhas lentas no Excel, agora tem o cenário completo da fazenda. O painel entrega a análise de GMD do rebanho e o fluxo de caixa consolidados em cerca de 3 minutos. O dado escuro finalmente virou decisão.

4.2. O Impacto Técnico (Estabilidade do Servidor)

O meu servidor Flask agora "respira aliviado". O consumo de RAM da aplicação despencou, porque o Python parou de baixar milhares de linhas pela rede para fazer matemática básica. Com a View SQL resolvendo o cálculo, os índices otimizando os filtros e a paginação segurando o tráfego, o sistema se tornou verdadeiramente escalável. A fazenda pode dobrar ou triplicar o número de animais cadastrados, e o painel continuará abrindo em milissegundos.

5. Conclusão

Sistemas genéricos falham no agronegócio porque tentam tratar o peso de um boi como se fosse apenas o "estoque de uma loja". A pecuária é um organismo vivo, e seus indicadores, como o Ganho Médio Diário, exigem uma arquitetura desenhada para eles.

No fim das contas, bons sistemas resolvem problemas reais — o resto é exercício. Partir da dor do pecuarista para chegar à solução técnica de uma View avançada é o que diferencia código de prateleira de um software com propósito.

Quer analisar o meu código ou ver essa arquitetura rodando ao vivo?

Top comments (0)