Error SQL · cardinalidad

Un JOIN devuelve filas duplicadas: cómo encontrar la multiplicación

Cuando una fila de una tabla coincide con varias de otra, el JOIN produce una fila por combinación válida. Antes de añadir DISTINCT, identifica qué relación está multiplicando el resultado.

Lectura: 16–20 minResultado comprobadoPráctica con pistas

El síntoma: esperabas una fila y aparecen varias

Un JOIN no “duplica” filas de forma arbitraria. Produce todas las parejas que cumplen la condición de unión. Si un pedido tiene tres líneas en detalle_pedido, ese pedido puede aparecer tres veces al unir ambas tablas.

La pregunta correcta no es “¿cómo quito duplicados?”, sino “¿qué granularidad quiero en el resultado y cuántas coincidencias tiene cada clave?”.

Ejemplo: dos pedidos se convierten en cinco filas

Los pedidos 101 y 106 son dos pedidos distintos, pero tienen cinco líneas de detalle en total:

SQL
SELECT p.id AS pedido_id,
       c.nombre AS cliente,
       dp.producto_id
FROM pedidos AS p
JOIN clientes AS c
  ON c.id = p.cliente_id
JOIN detalle_pedido AS dp
  ON dp.pedido_id = p.id
WHERE p.id IN (101, 106)
ORDER BY p.id, dp.id;
Resultado comprobado: una fila por línea del pedido
pedido_idclienteproducto_id
101Ana Torres1
101Ana Torres2
101Ana Torres5
106Sara Gómez4
106Sara Gómez8

El resultado es correcto si quieres una fila por línea de detalle. Es incorrecto únicamente si tu intención era una fila por pedido.

Diagnóstico en tres preguntas

  1. ¿Cuál es la unidad del resultado? Pedido, cliente, producto, línea de pedido… Escríbela con palabras.
  2. ¿La clave de unión es única en cada lado? pedidos.id es única, pero detalle_pedido.pedido_id puede repetirse.
  3. ¿Cuántas coincidencias tiene cada clave? Cuenta antes de intentar “limpiar” el resultado.
Comprobación de multiplicidad
SELECT pedido_id,
       COUNT(*) AS lineas
FROM detalle_pedido
GROUP BY pedido_id
HAVING COUNT(*) > 1
ORDER BY lineas DESC, pedido_id;
Pedidos con más de una línea en la base educativa
pedido_idlineas
1013
1133
1062
1072
1102
1142
1172

La solución depende de la intención

Si quieres una fila por pedido, agrega las líneas

Una fila por pedido
SELECT p.id AS pedido_id,
       c.nombre AS cliente,
       SUM(dp.cantidad * dp.precio_unitario) AS total_lineas
FROM pedidos AS p
JOIN clientes AS c
  ON c.id = p.cliente_id
JOIN detalle_pedido AS dp
  ON dp.pedido_id = p.id
WHERE p.id IN (101, 106)
GROUP BY p.id, c.nombre
ORDER BY p.id;
Granularidad corregida: una fila por pedido
pedido_idclientetotal_lineas
101Ana Torres128,40
106Sara Gómez1088,00

Si solo quieres saber si existe una coincidencia, no necesitas traer todas

En algunos problemas, EXISTS expresa mejor la intención que unir una tabla solo para comprobar presencia. Eso evita introducir varias filas del lado dependiente en el resultado principal.

Por qué DISTINCT no es el primer remedio

Añadir DISTINCT sin diagnosticar la relación

DISTINCT elimina filas idénticas en las columnas seleccionadas. Puede ocultar una multiplicación, pero no cambia la cardinalidad de la relación. Si más adelante añades una columna de detalle, las filas volverán a diferenciarse. Decide primero qué representa una fila.

Práctica correctiva

Intermedia · JOIN y granularidad

Una fila por pedido con número de líneas

Devuelve los pedidos 101 y 106 con el nombre del cliente y el número de líneas de detalle. El resultado debe tener exactamente dos filas.

Qué debes recordar

  • Un JOIN produce una fila por combinación que cumple ON.
  • Una relación uno-a-muchos aumenta naturalmente el número de filas.
  • Define la granularidad deseada antes de corregir.
  • Cuenta coincidencias y usa agregación o EXISTS cuando expresen mejor la intención.
  • No uses DISTINCT como parche automático.

Conceptos relacionados

Fuentes técnicas consultadas