Volver al inicio
Performance Optimization

Optimización de Reportes SICOSS

Cómo reduje un proceso crítico de 40 minutos a menos de 60 segundos transformando consultas SQL masivas y migrando lógica a Python.

Anatomía del Problema (Legacy)

Tras una auditoría técnica profunda, identifiqué dos cuellos de botella que impedían el escalamiento del sistema:

1. SQL Correlacionado (Carga Excesiva de DB)

El sistema original utilizaba subconsultas anidadas de forma correlacionada. Esto significa que por cada registro de liquidación, el motor de base de datos debía recalcular jerarquías de grupos de forma redundante.

-- Representación anonimizada del problema SQL
-- Ejecución redundante de sub-arrays por fila
SELECT 
    payroll.id,
    (SELECT ARRAY( 
        SELECT DISTINCT group_id 
        FROM generic_groups_table 
        WHERE group_id IN (
            SELECT group_id FROM concepts_relation 
            WHERE concept_id = payroll.concept_id
        )
    )) AS category_groups
FROM payroll_registry AS payroll;

2. El problema N+1 en PHP

La lógica de negocio procesaba los legajos de forma secuencial, realizando nuevas peticiones de datos dentro de un bucle de miles de iteraciones.

// Representación anonimizada del flujo ineficiente
foreach ($employees as $employee) {
    // Alarma técnica: Consulta a DB dentro de un ciclo masivo
    $details = $db->query('SELECT * FROM details WHERE emp_id = ' . $employee['id']);
    
    foreach ($details as $row) {
        $this->processBusinessRules($row);
    }
}

Métricas de Impacto

Tiempo Total de Proceso
40m → 58s
Optimización SQL
15m → 7s
Confiabilidad
100% Integra

La Solución

La solución se aplicó en dos etapas críticas:

1. Optimización SQL con CTEs

Reemplacé las subconsultas recurrentes por Common Table Expressions (CTEs). Esto permitió a PostgreSQL precalcullar los grupos una sola vez en memoria y unirlos mediante un Join altamente eficiente.

-- Solución mediante CTE (Pre-procesamiento en memoria)
WITH processed_groups AS (
    SELECT concept_id, array_agg(DISTINCT group_id) as groups
    FROM concepts_relation
    GROUP BY concept_id
)
SELECT payroll.*, pg.groups
FROM payroll_registry payroll
LEFT JOIN processed_groups pg ON pg.concept_id = payroll.concept_id;

2. Procesamiento Vectorial con Python

Finalmente, la lógica de validación se migró a un motor en Python utilizando Pandas. Al tratar el millón de registros de forma vectorial (como un todo en memoria), eliminé la sobrecarga del bucle secuencial de PHP.

Conclusión

Este proyecto no solo ahorró tiempo, sino que demostró la importancia de la observabilidad y el análisis de planes de ejecución. La solución fue validada mediante Jupyter Notebooks, asegurando que el 100% de los 1.2M de registros generados coincidieran exactamente con el sistema original, garantizando integridad total de los datos contables.