Un "modelo financiero", en un archivo Excel (.xlsx) de 80 Megabytes inflado a base de macros en VBA, miles de BUSCARV cruzados y pestañas ocultas, se acaba corrompiendo. A Excel le pasa como a los políticos, se corromper por el éxito.
Llevo años diciendo que usar la hoja de cálculo de Microsoft como base de datos o motor de reglas de negocio es una bomba de relojería, pero en el mundo corporativo, la gente se aferra a Excel como si fuera una religión. Ayer decidí explicar que esto tiene que acabar: vamos a automatizar esto como informáticos de verdad.
El infierno de las macros y el BUSCARV
El problema de Excel no es la herramienta en sí (es fantástica para hacer prototipos y matemáticas rápidas), sino el ciclo de vida del software. Un Excel gigante no tiene control de versiones (¿quién no ha visto archivos llamados Reporte_Final_V3_definitivo_ESTE.xlsx?), no separa los datos de la presentación, y sus fórmulas son cajas negras inescrutables que no se pueden testear.
Me senté con los que entienden la Excel, reconstruimos la lógica de lo que hacía su famoso fichero a partir de una copia de seguridad antigua, y lo pasé a código.
Python al rescate
La lógica de negocio era sorprendentemente sencilla: extraer datos de tres tablas distintas, cruzarlos y aplicar un cálculo de comisiones. En lugar de hacer que Excel sufra cruzando 500.000 filas en memoria, delegué el trabajo pesado a nuestro Data Warehouse y escribí un script en Python para generar el reporte de forma automática.
Usando la librería estándar csv (y la maravillosa conexión pyodbc para SQL Server), el proceso es transparente, rápido e infalible:
import pyodbc
import csv
# Conexión directa a nuestro SQL Server
conn = pyodbc.connect('DRIVER={SQL Server};SERVER=servidor_dw;DATABASE=Analitica;UID=uid;PWD=secreto')
cursor = conn.cursor()
# El motor de base de datos hace el trabajo pesado
query = """
SELECT
c.Nombre,
SUM(v.Importe) as TotalVentas,
SUM(v.Importe) * 0.05 as ComisionCalculada
FROM Fact_Ventas v
JOIN Dim_Comerciales c ON v.IdComercial = c.IdComercial
WHERE v.Fecha >= '2012-01-01'
GROUP BY c.Nombre
"""
cursor.execute(query)
# Volcamos directamente a un CSV limpio que no se corrompe
with open('reporte_comisiones_generado.csv', 'wb') as f:
writer = csv.writer(f)
writer.writerow([column[0] for column in cursor.description]) # Cabeceras
for row in cursor.fetchall():
writer.writerow(row)
print "Reporte generado en 0.5 segundos. Adiós VBA."
Reflexión: La eterna batalla
Sé que esta es una batalla perdida a medias. Los usuarios de negocio necesitan la flexibilidad de toquetear las celdas, y nosotros los informáticos queremos rigidez y control. Herramientas visuales como QlikView o Tableau están ayudando a crear un puente, pero el instinto de "descargar a Excel" está grabado a fuego en el ADN corporativo. Supongo que seguiremos rescatando .xlsx corruptos hasta el fin de los tiempos, pero al menos es necesario poner límites.