Curso SQL · Northwind
Tema 3 de 64
Bloque 1 · Antes de escribir SQL PostgreSQL 17 northwind

Qué es una base de datos relacional

¿Qué es una base de datos relacional?

1. Qué es

Una base de datos relacional guarda información en tablas, y las tablas se conectan entre sí.

Una tabla tiene tres reglas que una hoja de cálculo no cumple:

  1. Filas y columnas, con encabezados.
  2. Cada columna tiene un solo tipo de dato. Si precio es numérico, no puedes escribir "consultar" en una celda.
  3. Cada encabezado es distinto. No hay dos columnas llamadas igual.

2. Por qué no una hoja de cálculo

Imagina que llevas los pedidos de tu negocio en Excel. Una fila por pedido:

pedido cliente teléfono ciudad producto cantidad
1001 Panadería Sol 555-1234 Lima Café 10
1002 Panadería Sol 555-1234 Lima Azúcar 5
1003 Panadería Sol 555-9999 Lima Café 8

Tres problemas, y los tres son el mismo:

  1. El teléfono está repetido tres veces. Si cambia, hay que cambiarlo en todas — y en la fila 1003 ya se te escapó una.
  2. No sabes cuál es el correcto. ¿555-1234 o 555-9999? La hoja no tiene forma de decírtelo.
  3. Si borras los pedidos, pierdes el teléfono. El dato del cliente vivía dentro del pedido.

El modelo relacional resuelve los tres de un golpe: cada cosa se guarda una sola vez, en su propia tabla.

clientes                          pedidos
+----+----------------+------+    +------+----+------------+
| id | nombre         | tel  |    | id   | id_cliente | fecha |
+----+----------------+------+    +------+----+------------+
| 7  | Panadería Sol  | 555… |◄───| 1001 | 7  | 2026-03-01 |
+----+----------------+------+    | 1002 | 7  | 2026-03-02 |
                                   +------+----+------------+

El teléfono existe una vez. Los pedidos no lo guardan: guardan a quién apuntan.

3. Las tres piezas del vocabulario

3.1 Clave primaria (PRIMARY KEY)

La columna que identifica a cada fila sin ambigüedad. No se repite y no puede estar vacía.

En Northwind, customers.customer_id es la clave primaria de los clientes.

3.2 Clave foránea (FOREIGN KEY)

Una columna que apunta a la clave primaria de otra tabla. orders.customer_id apunta a customers.customer_id.

Y aquí está la parte que importa: el motor la hace cumplir. Si intentas registrar un pedido de un cliente que no existe, lo rechaza:

INSERT INTO
    orders (customer_id, employee_id, order_date)
VALUES
    ('ZZZZZ', 1, '1997-01-01');
ERROR: insert or update on table "orders" violates foreign key constraint

No es una advertencia. Es una negativa. Tu base no puede quedar en un estado incoherente.

Esto distingue a una base de datos relacional de un almacén analítico como BigQuery, donde las claves foráneas son solo documentación y nadie las comprueba.

3.3 Cardinalidad

Cuántas filas de una tabla se corresponden con cuántas de la otra.

Tipo Ejemplo en Northwind
Uno a muchos Un cliente tiene muchos pedidos
Muchos a muchos Un pedido lleva muchos productos, y un producto aparece en muchos pedidos

El "muchos a muchos" no se puede guardar directo — hace falta una tabla en medio. En Northwind es order_details, y por eso existe.

4. Ejemplos sobre Northwind

Cuántos pedidos tiene cada cliente — un "uno a muchos" en acción. No te detengas en la sintaxis del JOIN; llega completa en el bloque 5. Aquí solo mira el resultado: un cliente, muchos pedidos.

SELECT
    c.company_name,
    count(o.order_id) AS pedidos
FROM
    customers c
    LEFT JOIN orders o USING (customer_id)
GROUP BY
    c.company_name
ORDER BY
    pedidos DESC
LIMIT 3;
    company_name    | pedidos
--------------------+---------
 Save-a-lot Markets |      31
 Ernst Handel       |      30
 QUICK-Stop         |      28

Y la tabla del medio, order_details, que resuelve el "muchos a muchos":

SELECT
    order_id,
    count(*) AS lineas
FROM
    order_details
GROUP BY
    order_id
ORDER BY
    lineas DESC
LIMIT 3;
 order_id | lineas
----------+--------
    11077 |     25
    10657 |      6

El pedido 11077 tiene 25 productos distintos. Sin order_details, esos 25 no cabrían en una fila de orders.

5. Errores comunes

  1. Creer que "relacional" significa "las tablas se relacionan". Significa que los datos se guardan como relaciones (tablas). Que se conecten es consecuencia, no definición.
  2. Meter varios valores en una celda. "Café, Azúcar, Harina" en una columna es exactamente lo que el modelo relacional viene a evitar. Eso son tres filas.
  3. Confundir clave primaria con "la primera columna". Es la que identifica, esté donde esté. En order_details son dos columnas juntas: order_id y product_id.

6. Ejercicios

  1. Sin ejecutar nada: si un cliente cambia de teléfono, ¿cuántas filas hay que modificar en Northwind? ¿Y en la hoja de cálculo del punto 2?
  2. ¿Qué tabla tiene una clave primaria formada por dos columnas? ¿Por qué tiene sentido que sea así?
  3. Encuentra en Northwind otra relación "muchos a muchos" distinta de la de pedidos y productos.

7. Pregunta de negocio

El gerente quiere saber si hay clientes registrados que nunca hayan comprado nada, para llamarlos.

No te digo qué columnas ni qué tablas. Piensa primero qué significa "nunca compró" en términos de las dos tablas.

? RespuestaRespóndela antes de abrir

"nunca compró" significa que su customer_id no aparece ni una sola vez en orders.customer_id. No hay una columna compro: sí/no en customers — el dato no está guardado, se deduce de una ausencia en la otra tabla.

Ese es el salto mental del modelo relacional: hay preguntas que no se responden mirando una tabla, sino mirando qué filas de una no tienen pareja en la otra. En Northwind son 2 clientes.

La sintaxis para escribirlo —conservar las filas sin pareja y filtrarlas— es el LEFT JOIN y el anti-join del bloque 5 (left-right-y-full-join, anti-join-y-semi-join). Aquí basta con haber formulado la idea correctamente.

✓ SolucionesResuélvelos antes de abrir

1. Una sola fila, en customers. Los pedidos no guardan el teléfono, solo apuntan al cliente. En la hoja de cálculo habría que cambiar todas las filas de ese cliente — y basta olvidar una para que la hoja quede mintiendo.

2. Dos: order_details (order_id, product_id) y employee_territories (employee_id, territory_id). Ambas son tablas del medio de un "muchos a muchos". Lo que identifica una línea de pedido no es el pedido ni el producto por separado, sino la combinación: el producto 42 dentro del pedido 10248.

3. employee_territories: un empleado cubre varios territorios y un territorio lo atienden varios empleados.