1. Qué es
Te sientan frente a una base de datos que no conoces. Cuarenta tablas, nombres en inglés, nadie que te explique nada. Esto pasa el primer día de casi cualquier trabajo con datos.
Este tema es el método para orientarte sin depender de que alguien te lo cuente.
2. Las cuatro preguntas
Ante cualquier tabla nueva, en este orden:
| # | Pregunta | Qué te dice |
|---|---|---|
| 1 | ¿Cuál es el grano? | Qué representa una fila |
| 2 | ¿Cuál es la clave primaria? | Qué identifica esa fila |
| 3 | ¿A qué apunta y quién le apunta? | Cómo se conecta con el resto |
| 4 | ¿Qué pregunta de negocio responde? | Para qué existe |
2.1 El grano es la más importante
Grano = qué representa una fila. Y se dice en una frase con sujeto:
orders→ "un pedido"order_details→ "un producto dentro de un pedido"customers→ "un cliente"
Si no puedes decirlo en una frase, todavía no entendiste la tabla.
Por qué importa tanto: el grano determina qué puedes sumar sin equivocarte. orders.freight es el flete del pedido. Si unes orders con order_details y sumas freight, lo estás sumando una vez por cada línea del pedido. El pedido 11077 tiene 25 líneas: su flete contaría 25 veces.
Ese error no da ningún mensaje. Solo da un número más grande.
3. Cómo obtener las respuestas
3.1 Con DBeaver
Doble clic en la tabla y ahí están las pestañas: Columns, Constraints, Foreign Keys, References. La pestaña ER Diagram dibuja el esquema solo.
3.2 Con psql
\dt lista las tablas
\d orders describe una tabla: columnas, tipos, PK, FK y quién le apunta
\d+ orders lo mismo, más tamaño y comentarios
\d orders es el comando que más vas a usar en tu vida con PostgreSQL.
3.3 Con SQL puro
Cuando el cliente no ayuda, information_schema siempre está. Es SQL estándar: funciona en PostgreSQL, SQL Server, MySQL y casi cualquier motor.
Todas las conexiones del esquema de un vistazo:
SELECT tc.table_name AS tabla,
kcu.column_name AS columna,
ccu.table_name AS apunta_a
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu
ON kcu.constraint_name = tc.constraint_name
JOIN information_schema.constraint_column_usage ccu
ON ccu.constraint_name = tc.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY'
AND tc.table_schema = 'public'
ORDER BY 1, 2;
tabla | columna | apunta_a
----------------------+--------------+-------------
employee_territories | employee_id | employees
employee_territories | territory_id | territories
employees | reports_to | employees ← apunta a sí misma
order_details | order_id | orders
order_details | product_id | products
orders | customer_id | customers
orders | employee_id | employees
orders | ship_via | shippers
products | category_id | categories
products | supplier_id | suppliers
territories | region_id | region
Once claves foráneas y ahí está el mapa completo. En treinta segundos, sin que nadie te explique nada.
Y mira la tercera fila: employees.reports_to apunta a employees. Una tabla que se apunta a sí misma es siempre una jerarquía — aquí, quién le reporta a quién.
4. Ejemplos sobre Northwind
Comprobar el grano. Si la clave primaria es order_id, cada pedido debe aparecer una sola vez:
SELECT count(*) AS filas, count(DISTINCT order_id) AS pedidos_distintos
FROM orders;
filas | pedidos_distintos
-------+-------------------
830 | 830
Iguales: el grano es "un pedido". Si no fueran iguales, tu idea del grano estaría mal — y toda suma que hagas después, también.
Ver qué puede faltar. Las columnas que admiten NULL te dicen dónde el negocio permite ausencias:
SELECT column_name, is_nullable
FROM information_schema.columns
WHERE table_name = 'orders' AND is_nullable = 'YES'
ORDER BY ordinal_position;
shipped_date admite NULL. Eso no es un descuido: significa "pedido aún no despachado". El esquema te está contando una regla de negocio.
5. Errores comunes
- Empezar por
SELECT *en la tabla más grande. Ver 2.155 filas no te dice nada del modelo. Empieza por las claves foráneas. - Asumir el grano por el nombre. Una tabla llamada
ventaspuede tener una fila por venta, por línea de venta o por venta y día. Compruébalo con elcount(*)contracount(DISTINCT). - Ignorar los
NULL. Cada columna nullable es una regla de negocio escrita en el esquema. - No mirar si una tabla se apunta a sí misma. Es la señal de una jerarquía, y cambia por completo cómo se consulta.
6. Ejercicios
- Describe el grano de las 12 tablas de Northwind, en una frase cada una.
- Comprueba con SQL que el grano de
order_detailses "un producto dentro de un pedido". - ¿Cuántas tablas apuntan a
employees? ¿Qué significa cada una? us_statesno tiene ninguna clave foránea. ¿Se puede unir de todas formas con alguna tabla? ¿Con cuál y por qué columna?
7. Pregunta de negocio
Te acaban de dar acceso a Northwind y tu jefe pregunta: "¿por qué canal llegan los pedidos más caros?". Antes de escribir una sola consulta, ¿qué tablas necesitas y cómo se conectan?
Responde con el camino de tablas, no con SQL.