← Volver a Aprende con CDIA

Estructura de datos y bases de datosSQL en la práctica

IntermedioDatos y bases de datos10 min de lectura

SQL en la práctica

El idioma con el que le preguntamos cosas a los datos: SELECT, WHERE, JOIN y GROUP BY con ejemplos que puedes copiar, y el orden real en que la máquina ejecuta una consulta.

SQL tiene una cualidad rara entre los lenguajes de programación: le dices qué quieres, no cómo obtenerlo. No escribes bucles ni te preocupas de recorrer filas; describes el resultado que buscas y el motor se encarga de encontrarlo de la forma más rápida que pueda. Por eso lleva décadas siendo la lengua franca de los datos, y por eso vale la pena que cualquiera que trabaje con datos —analista, científico o ingeniero— lo tenga en los dedos.

Vamos a construirlo pieza por pieza sobre dos tablas: clientes y pedidos, como las de la lección anterior.

SELECT: pedir columnas

La consulta más básica elige columnas de una tabla:

SELECT nombre, ciudad
FROM clientes;

SELECT * trae todas las columnas, cómodo para explorar pero mala costumbre en producción: pides más de lo que necesitas.

WHERE: filtrar filas

Casi nunca queremos toda la tabla. WHERE se queda solo con las filas que cumplen una condición:

SELECT nombre, ciudad
FROM clientes
WHERE ciudad = 'Concepción'
  AND creado_en >= '2026-01-01';

Piensa en SELECT como las columnas (vertical) y en WHERE como las filas (horizontal). Entre los dos recortas la tabla a exactamente el rectángulo que te interesa.

ORDER BY y LIMIT: ordenar y cortar

SELECT nombre, total
FROM pedidos
ORDER BY total DESC
LIMIT 10;

Los diez pedidos más caros, de mayor a menor. LIMIT es tu mejor amigo cuando exploras una tabla que no conoces: mira diez filas antes de lanzarte sobre diez millones.

JOIN: unir tablas

Aquí SQL muestra los dientes. Un JOIN combina filas de dos tablas siguiendo la relación entre sus claves. Queremos cada pedido con el nombre de quien lo hizo:

SELECT c.nombre, p.total
FROM pedidos p
JOIN clientes c ON c.id = p.cliente_id;

La condición ON c.id = p.cliente_id es exactamente el enlace clave primaria ↔ clave foránea del que hablamos antes. Vale la pena distinguir dos sabores:

  • INNER JOIN (el JOIN a secas): solo las filas que casan en ambas tablas. Un cliente sin pedidos no aparece.
  • LEFT JOIN: todas las filas de la tabla izquierda, tengan o no pareja a la derecha. Ideal para preguntar justamente quién no tiene pedidos (los que salen con la columna derecha vacía).

Esta diferencia es fuente eterna de bugs silenciosos: un INNER JOIN donde querías un LEFT no da error, simplemente te oculta filas y tus números salen más bajos de lo que deberían. Cuando un total no cuadra, el JOIN es el primer sospechoso.

GROUP BY: de filas a resúmenes

GROUP BY es donde SQL se vuelve una herramienta de análisis. Agrupa filas que comparten un valor y calcula algo sobre cada grupo con funciones de agregación: COUNT, SUM, AVG, MIN, MAX.

¿Cuánto ha gastado cada cliente y cuántos pedidos ha hecho?

SELECT c.nombre,
       COUNT(*)     AS num_pedidos,
       SUM(p.total) AS gasto_total
FROM clientes c
JOIN pedidos p ON p.cliente_id = c.id
GROUP BY c.nombre
HAVING SUM(p.total) > 100000
ORDER BY gasto_total DESC;

Fíjate en HAVING: es como WHERE, pero se aplica después de agrupar. WHERE filtra filas antes de agrupar; HAVING filtra grupos ya calculados. Confundirlos es otro clásico.

El orden que la máquina lee (no el que escribes)

Este detalle aclara media docena de confusiones de golpe. Aunque escribimos SELECT primero, el motor ejecuta las cláusulas en otro orden:

FROM WHERE GROUP BY HAVING SELECT ORDER BY
El orden real de ejecución: primero se traen y filtran filas, luego se agrupan, y solo al final se eligen columnas y se ordena. Por eso un alias de SELECT no puede usarse en WHERE.

Primero se toman las tablas (FROM/JOIN), se filtran filas (WHERE), se agrupan (GROUP BY), se filtran grupos (HAVING), y recién ahí se eligen las columnas (SELECT) y se ordena (ORDER BY). Entender esto explica por qué no puedes usar en WHERE un alias que definiste en SELECT: cuando corre WHERE, ese alias todavía no existe.

El puente con Python

En un proyecto de datos, SQL y Python conviven. Lo idiomático es dejar que la base haga el trabajo pesado —filtrar y agregar millones de filas es su especialidad— y traer a Python solo el resultado ya reducido:

import pandas as pd
import sqlite3

con = sqlite3.connect("tienda.db")
df = pd.read_sql_query("""
    SELECT c.ciudad, SUM(p.total) AS ventas
    FROM pedidos p
    JOIN clientes c ON c.id = p.cliente_id
    GROUP BY c.ciudad
""", con)
# df llega ya agregado: una fila por ciudad, listo para graficar o modelar

Traer la tabla completa a un DataFrame y agrupar en pandas también funciona, pero mueve por la red datos que no necesitas. Regla de oro: filtra y agrega lo más cerca posible de donde viven los datos.

En resumen

Con seis palabras —SELECT, FROM, WHERE, JOIN, GROUP BY, ORDER BY— ya puedes responder la gran mayoría de las preguntas que se le hacen a una base de datos. Las claves para no tropezar: el JOIN correcto (INNER vs LEFT), la diferencia entre WHERE y HAVING, y tener presente el orden real de ejecución. Con esto cerramos el mundo de los datos estructurados. Falta un tipo de dato que las tablas clásicas no manejan bien —el significado— y para eso nacieron las bases de datos vectoriales.