Ejecutas SELECT, WHERE, ORDER BY y LIMIT sobre 6.000 líneas de facturas reales de una distribuidora chilena, y descubres con tus propias manos que un filtro escrito "bien" puede borrar 380 millones de pesos del informe.
El notebook de esta clase: https://gaa1104.vercel.app/notebooks/clase-02.ipynb
Ing. en Administración · mención Finanzas · Prof. Tomás Fernández
La primera consulta se lee en números. Graficar lo que todavía no sabes leer es decorar.
La forma la decide el trabajo que hace el dato —magnitud, identidad, polaridad, cambio en el tiempo, distribución—, no el gusto. Regla del curso: un solo eje.
Lo que quedó planteado ayer (se proyecta la pregunta, NO la respuesta):
«¿Qué hace que una planilla deje de servir: el volumen o la forma?»
Tres preguntas, en silencio, 90 segundos. Nadie habla, nadie levanta la mano todavía:
Par (2 min) → plenaria (3 min). Dar el tiempo completo. Contar hasta cinco antes de recoger la primera respuesta; el silencio incómodo es donde piensan.
Cerrar la pregunta colgada con ellos, no para ellos: la forma. El volumen se compra con un computador más grande; la forma no se arregla con plata.
Esto se dice completo, mirando a la sala, sin adorno. Es la primera lección de oficio del semestre y hay que darla de frente.
«Tengo que contarles algo. Este curso lo diseñé para las carreras de Comercio Exterior y Gestión Logística. El caso que vieron ayer era una importadora: contenedores, navieras, aranceles.
Ustedes son Ingeniería en Administración, mención Finanzas.
Podría haberlo dejado así y ustedes habrían aprendido igual el método. Pero habrían pasado el semestre traduciendo. Así que cambié el caso.»
Y de inmediato, el giro convertido en contenido:
«Y esto que acaba de pasar es exactamente el trabajo. El dato no se modela "bien" en abstracto. Se modela para la pregunta de quien decide. Cambió quién decide: cambió el caso. Lo que no cambia es el método — y el método es todo el semestre.»
El caso nuevo, en tres frases:
Decirlo explícito (honestidad sobre el dato): los datos son sintéticos, construidos por mí con patologías reales de empresa. Los clientes no existen. Las regiones y los problemas sí.
Dónde vive esto adentro de una empresa (30 segundos, no más):
«Esta planilla no la genera un analista. Sale del ERP — Softland, Defontana, SAP Business One — y llega por correo, en CSV, un lunes por la mañana. Casi nunca te van a dar acceso directo a la base de datos. Este archivo, tal como está, es lo que vas a recibir.»
Tres argumentos en lámina. No es un sermón anti-Excel — Excel es una herramienta correcta para problemas chicos, y en la sesión 10 lo vamos a comparar con argumentos y con una matriz de decisión.
La promesa del día, dicha tal cual:
«SQL se lee casi como inglés. No es programar: es preguntar bien. En 40 minutos van a estar respondiendo preguntas que en Excel les tomarían media hora de tabla dinámica.»
Regla del curso, se anuncia hoy y se cumple: desde esta sesión no se entrega nada en .xlsx. Excel vuelve una sola vez, en la sesión 10, como objeto de análisis.
Tres caminos, en orden. El profesor ya probó los tres antes de la clase (checklist de ARRANQUE.md).
Camino A — DuckDB instalado:
duckdb
Camino B — Python (el que casi siempre funciona):
python3 -m pip install duckdb python3 >>> import duckdb; con = duckdb.connect()
Camino C — la consola del curso, sin instalar nada:
python3 herramientas/consola-sql.py datos/finanzas
Detecta si hay DuckDB; si no lo hay, cae sola a sqlite3, que viene dentro de Python y siempre está. Nadie se queda fuera de la clase por un permiso de administrador.
👀 Se mira — las dos celdas de este bloque vienen resueltas. Córrelas y quédate con el par 6.000 líneas · 2.951 facturas: es el número que explica la mitad de los errores del semestre.
Cargar los datos. Proyectar en grande, lento, una sola vez:
CREATE OR REPLACE TABLE ventas AS SELECT * FROM read_csv_auto('datos/finanzas/ventas_extracto.csv'); SELECT COUNT(*) AS lineas, COUNT(DISTINCT numero_factura) AS facturas FROM ventas; -- 6000 líneas · 2951 facturas
Dejar que vean que cargó en menos de un segundo. Comentario al pasar, sin triunfalismo: "Excel abriría esto en lo que te tomas un café."
DESCRIBE ventas; -- 22 columnas
Recorrer las columnas en voz alta, rápido, agrupadas por a quién le importan:
| Grupo | Columnas | De quién es la pregunta |
|---|---|---|
| Identidad | fecha_emision, numero_factura, rut_cliente, cliente | Administración de ventas |
| Comercial | segmento, region_cliente, canal, vendedor, centro_costo | Gerencia comercial |
| Producto | sku, descripcion_producto, linea_negocio, cantidad | Control de gestión |
| Plata | precio_unitario, costo_unitario, moneda, tipo_cambio, descuento_factura, flete_despacho | Finanzas |
| Cartera | condicion_pago_dias, fecha_vencimiento, fecha_pago | Tesorería |
Pararse, estirar, subir la luz un segundo. Clase nocturna: el cuerpo también aprende.
Entregas: las cuatro consultas escritas y corridas en tu máquina, con el número que te dio cada una anotado al lado. 32 minutos, guiado paso a paso.
Estas consultas vienen escritas, pero no las copies con el ratón: tecléalas. Corre cada una, mira el resultado y anótalo antes de pasar a la siguiente. Es la única clase del semestre en que se escribe SQL copiándolo: se hace para que las cuatro palabras se te queden en los dedos.
El profesor proyecta, corre en vivo y se equivoca a propósito una vez (comillas dobles en un texto) para normalizar el error de sintaxis: "esto les va a pasar cincuenta veces esta noche y no significa nada".
-- 🔨 ACTIVIDAD · 1 de 4 · tecléala, córrela y mira las 22 columnas SELECT * FROM ventas LIMIT 10;
-- 🔨 ACTIVIDAD · 2 de 4 · ¿ves algo raro en la columna `cliente`? SELECT numero_factura, cliente, linea_negocio, cantidad * precio_unitario AS venta_linea FROM ventas WHERE moneda = 'CLP' ORDER BY venta_linea DESC LIMIT 10;
Este resultado hay que dejarlo en pantalla y callarse. En el top 10 aparece Ferretería Los Andes escrita de tres formas distintas: Ferretería Los Andes Ltda., Ferretería Los Andes LTDA, Ferretería Los Andes Ltda. (con doble espacio).
Preguntar, sin ayudar: «¿Ven algo raro?» Contar hasta cinco. Lo van a encontrar solos. Cuando alguien lo diga, es de esa persona el hallazgo — nombrarlo: "lo encontró [nombre], no yo".
-- 🔨 ACTIVIDAD · 3 de 4 · anota este número, lo vamos a desarmar SELECT COUNT(*) FROM ventas WHERE moneda = 'CLP' AND segmento = 'Minería'; -- 664
«664 líneas del segmento Minería. Perfecto. Ahora, ¿cuánto vendimos a Minería?»
SELECT ROUND(SUM(cantidad * precio_unitario)) AS venta_clp FROM ventas WHERE moneda = 'CLP' AND segmento = 'Minería'; -- 980.768.001
«980 millones. Ese es el número que mandarías. Ahora mira cómo está escrito el segmento en el archivo.»
SELECT segmento, COUNT(*) AS n FROM ventas GROUP BY segmento ORDER BY n DESC;
Aparecen 23 formas de escribir 5 segmentos: Minería, MINERÍA, MINERIA, minería, Minería (con espacio al final). El filtro exacto sólo tomó la primera.
SELECT COUNT(*) AS lineas, ROUND(SUM(cantidad * precio_unitario)) AS venta_clp FROM ventas WHERE moneda = 'CLP' AND segmento IN ('Minería','MINERÍA','MINERIA','minería','Minería '); -- 911 líneas · 1.360.516.886
El número del día, escrito en la pizarra:
CLP 379.748.885 desaparecidos. El 27,9 % de las ventas a Minería, en 247 líneas que el filtro nunca vio.
«Nadie se equivocó. La consulta estaba bien escrita, corrió sin error y devolvió un número. Y si mandas ese informe, le acabas de decir al directorio que el segmento minero vende un 28 % menos de lo que vende.»
Dejar que asiente. Y no resolverlo hoy:
«¿Se arregla escribiendo con más cuidado? ¿Y cuando sean 400 clientes en vez de 18? Esa es la clase del próximo martes.»
-- 🔨 ACTIVIDAD · 4 de 4 · las cuatro palabras juntas en una sola consulta SELECT numero_factura, cliente, region_cliente, cantidad * precio_unitario AS venta_linea FROM ventas WHERE moneda = 'CLP' AND segmento IN ('Minería','MINERÍA','MINERIA','minería','Minería ') ORDER BY venta_linea DESC LIMIT 5;
Mapa de traducción, en lámina, lo copian:
| En Excel hacías… | En SQL escribes |
|---|---|
| Filtro en el encabezado | WHERE |
| Ordenar de mayor a menor | ORDER BY … DESC |
| Mirar sólo las primeras filas | LIMIT |
| Elegir qué columnas ver | SELECT |
| Crear una columna calculada | cantidad * precio_unitario AS venta_linea |
Hilo central del semestre. Se instala hoy y vuelve todas las clases.
«Encontramos que "Minería" está escrito de cinco formas. Ahora te hago la pregunta difícil: ¿MINERIA y Minería son el mismo segmento?»
Dejar que respondan que sí. Y entonces:
«Probablemente sí. Pero tú no lo sabes. Lo sabe la persona que carga esos datos. Y hay un caso donde no son lo mismo: si la empresa reclasificó a un cliente en 2023 y el sistema viejo escribía en mayúsculas, esas dos formas son dos épocas distintas del mismo cliente. Juntarlas te borra el cambio de clasificación.»
La regla del curso, se escribe en la pizarra:
Una consulta SQL sin una conversación previa es una opinión con formato de tabla.
A quién le preguntas, en esta empresa:
| Duda del dato | El rol que tiene la respuesta |
|---|---|
¿MINERIA y Minería son lo mismo? | Jefe de Administración de Ventas |
| ¿Por qué hay cantidades negativas? | Contador / Cuentas por cobrar |
| ¿Este precio en 0 es error o es una muestra? | Gerencia comercial |
| ¿El descuento es de la factura o de la línea? | Control de gestión |
| ¿Por qué el sector Público paga a 90 días? | Tesorería |
«Ninguno de ellos sabe SQL. Ninguno de ustedes conoce el negocio como ellos. Por eso este trabajo se hace de a dos, y por eso el que sólo sabe la herramienta se queda estancado.»
Y el cierre del hilo, dirigido a su carrera:
«Ustedes tienen la ventaja rara: van a saber las dos cosas. Ese es el perfil que las empresas no encuentran.»
Construida con ellos, no dictada. Preguntar y escribir en la pizarra lo que ellos digan:
SELECT, WHERE, ORDER BY, LIMIT.Preguntas que quedan planteadas (se escriben en la pizarra y no se responden hoy):
→ Si el segmento está escrito de 23 formas, ¿cuántas formas tendrá el nombre del cliente? ¿Y el RUT?
→ ¿Se puede hacer que elGROUP BYjunte solo lo que es lo mismo, sin escribir a mano las cinco variantes?
Puente al martes, dicho corto:
«El próximo martes le hacemos preguntas de negocio de verdad: cuánto vendimos por segmento, cuál es el margen por línea, quién nos debe. Y ahí el problema de las cinco Minerías deja de ser una curiosidad y se transforma en el problema.»
Entregas: las cinco consultas corridas, con una frase de respuesta cada una, en tu notebook o en un archivo .sql. 15 minutos. Se revisan al inicio de la clase 03.
Estas las escribes tú: no vienen resueltas. El profesor circula por la sala — este bloque es diagnóstico, no relleno.
cantidad. ¿Qué línea de negocio domina? ¿Te sorprende?canal = 'Licitación'? ¿Y cuánto suman en pesos?precio_unitario máximo y el mínimo de toda la tabla. El mínimo va a llamarte la atención — anota qué crees que significa y a quién le preguntarías.SELECT DISTINCT region_cliente …). ¿Cuántas tiene Chile? ¿Qué te dice esa diferencia?Bonus (si vas rápido): cuenta las líneas con fecha_pago IS NULL. ¿Qué significa una factura sin fecha de pago: es un error del archivo o es plata en la calle?
# 🔨 ACTIVIDAD 1 · Las 10 líneas con mayor `cantidad`. # Escribe tu consulta entre las comillas y descomenta la última línea. consulta_1 = """ SELECT """ respuesta_1 = "" # la línea de negocio que domina, en tus palabras # bd.sql(consulta_1)
# 🔨 ACTIVIDAD 2 · Cuántas líneas son de `canal = 'Licitación'` y cuánto suman en pesos. consulta_2 = """ SELECT """ respuesta_2 = "" # las dos cifras, y qué te dicen del canal # bd.sql(consulta_2)
# 🔨 ACTIVIDAD 3 · El `precio_unitario` máximo y el mínimo de toda la tabla. consulta_3 = """ SELECT """ respuesta_3 = "" # qué crees que significa el mínimo, y a quién le preguntarías # bd.sql(consulta_3)
# 🔨 ACTIVIDAD 4 · Las 5 líneas más grandes de Ferretería Los Andes. # Ojo: el nombre está escrito de más de una forma. consulta_4 = """ SELECT """ respuesta_4 = "" # cuántas formas de escribir ese cliente encontraste # bd.sql(consulta_4)
# 🔨 ACTIVIDAD 5 · Las regiones distintas que declara el archivo. consulta_5 = """ SELECT """ respuesta_5 = "" # cuántas dice el archivo, cuántas tiene Chile, y qué te dice eso # bd.sql(consulta_5)
Ticket T02 (3 min antes de terminar, se proyecta): a) ¿Para qué sirve LIMIT? Una frase. b) Escribe la consulta que devuelve las 5 líneas de mayor venta bruta. c) 1–5 y qué te falta.
Despedida por nombre. Recordar Anki: 10 tarjetas de S1–S2 en el sitio del curso.
Tres minutos. No suma nota. Sirve para armar la apertura de la próxima clase.
El notebook de esta clase:
https://gaa1104.vercel.app/notebooks/clase-02.ipynb