El RIGHT JOIN es el espejo exacto del LEFT JOIN: conserva todas las filas de la tabla derecha, casen o no con la izquierda. Conceptualmente no aporta nada nuevo —de hecho, en esta lección demostrarás que cualquier RIGHT JOIN puede reescribirse como un LEFT JOIN—, y sin embargo merece una lección propia por tres razones muy prácticas: te lo vas a encontrar en código ajeno, hay situaciones concretas en las que aparece de forma natural, y mezclarlo con LEFT JOIN en una cadena de tres tablas produce consultas que casi nadie sabe leer.
De paso, aquí aparecen por fin los cinco empleados de TiendaVerde que nunca han gestionado un pedido.
Contenido
- La regla del
RIGHT JOINy el diagrama de conjuntos - Todos los empleados con los pedidos que gestionaron
- La equivalencia formal:
A RIGHT JOIN B≡B LEFT JOIN A - Por qué casi todo el mundo prefiere
LEFT - Cuándo un
RIGHT JOINsí resulta natural - El anti-join en versión
RIGHT - El peligro real: mezclar
LEFTyRIGHTen una cadena - Soporte por motor
- Errores Comunes y Consejos
- Ejercicios
- Conclusión
- La regla del
RIGHT JOIN y el diagrama de conjuntos
RIGHT JOIN y el diagrama de conjuntosflowchart LR
subgraph R[" "]
direction LR
A(("solo en la izquierda<br/>❌ fuera"))
I(("casan<br/>✅ resultado"))
B(("solo en la derecha<br/>✅ se conserva<br/>con NULL a la izquierda"))
end
style A fill:#f8f8f8,stroke:#bbb,stroke-dasharray: 4 4
style I fill:#d9f0d9,stroke:#2b7a2b,stroke-width:3px
style B fill:#d9f0d9,stroke:#2b7a2b,stroke-width:3px
Compara los tres diagramas del módulo hasta ahora:
| Tipo | Izquierda huérfana | Casan | Derecha huérfana |
|---|---|---|---|
INNER JOIN |
❌ | ✅ | ❌ |
LEFT JOIN |
✅ | ✅ | ❌ |
RIGHT JOIN |
❌ | ✅ | ✅ |
Todo lo aprendido en 03-03 se aplica igual, con los lados intercambiados:
RIGHT JOIN=RIGHT OUTER JOIN; la palabraOUTERes opcional.- Los
NULLlos fabrica el motor, ahora en las columnas de la tabla izquierda. - El paso
1ddel orden lógico reintroduce las filas derechas huérfanas dentro delFROM, antes delWHERE. - Y por tanto una condición en el
WHEREsobre una columna de la tabla izquierda degrada elRIGHT JOINaINNER JOIN. Es exactamente la trampa de 03-03, reflejada.
- Todos los empleados con los pedidos que gestionaron
La pregunta: "dame los ocho empleados, con los pedidos que ha gestionado cada uno". Como queremos que aparezcan todos los empleados, la tabla que debe conservarse entera es empleados. Si la escribimos a la derecha, el operador es RIGHT JOIN:
SELECT e.id AS empleado_id,
e.nombre || ' ' || e.apellidos AS empleado,
e.puesto,
pe.id AS pedido_id,
pe.fecha_pedido,
pe.estado
FROM pedidos AS pe
RIGHT JOIN empleados AS e ON pe.empleado_id = e.id
ORDER BY e.id, pe.id;| empleado_id | empleado | puesto | pedido_id | fecha_pedido | estado |
|---|---|---|---|---|---|
| 1 | Rosa Alcázar Vives | Directora general | (null) | (null) | (null) |
| 2 | Andrés Company Talens | Responsable de ventas | (null) | (null) | (null) |
| 3 | Beatriz Nadal Ripoll | Responsable de logística | (null) | (null) | (null) |
| 4 | Óscar Peris Blasco | Comercial | 2 | 2025-03-12 | entregado |
| 4 | Óscar Peris Blasco | Comercial | 6 | 2025-05-23 | cancelado |
| 4 | Óscar Peris Blasco | Comercial | 10 | 2025-08-03 | entregado |
| 4 | Óscar Peris Blasco | Comercial | 16 | 2025-12-19 | enviado |
| 5 | Laia Puig Sanchis | Comercial | 4 | 2025-04-19 | entregado |
| 5 | Laia Puig Sanchis | Comercial | 8 | 2025-06-28 | entregado |
| 5 | Laia Puig Sanchis | Comercial | 12 | 2025-10-01 | entregado |
| 5 | Laia Puig Sanchis | Comercial | 18 | 2026-01-27 | pagado |
| 6 | Marc Estévez Roig | Atención al cliente | 14 | 2025-11-14 | entregado |
| 6 | Marc Estévez Roig | Atención al cliente | 20 | 2026-02-21 | pendiente |
| 7 | Irene Salvador Mira | Operaria de almacén | (null) | (null) | (null) |
| 8 | Daniel Vercher Lluch | Analista de datos | (null) | (null) | (null) |
15 filas: los 10 pedidos con comercial más 5 empleados sin ningún pedido asignado. La cuenta es la de 03-03, reflejada:
Y el resultado se lee como un retrato del organigrama:
- Óscar y Laia son los dos comerciales: cuatro pedidos cada uno.
- Marc, de atención al cliente, gestionó dos.
- Irene Salvador Mira (operaria de almacén) y Daniel Vercher Lluch (analista de datos) no han gestionado ninguno, y es lo esperable: sus puestos no venden.
- Rosa, Andrés y Beatriz tampoco, por la misma razón: dirección y responsables de área no atienden pedidos directamente.
Un INNER JOIN habría devuelto 10 filas y tres empleados. Los otros cinco no habrían existido para el informe, y una consulta de "actividad por empleado" que omite a cinco de ocho personas no es un informe: es un problema.
- La equivalencia formal:
A RIGHT JOIN B ≡ B LEFT JOIN A
A RIGHT JOIN B ≡ B LEFT JOIN AEscribamos la misma pregunta de la otra manera: poniendo empleados a la izquierda y usando LEFT JOIN.
SELECT e.id AS empleado_id,
e.nombre || ' ' || e.apellidos AS empleado,
e.puesto,
pe.id AS pedido_id,
pe.fecha_pedido,
pe.estado
FROM empleados AS e
LEFT JOIN pedidos AS pe ON pe.empleado_id = e.id
ORDER BY e.id, pe.id;| empleado_id | empleado | puesto | pedido_id | fecha_pedido | estado |
|---|---|---|---|---|---|
| 1 | Rosa Alcázar Vives | Directora general | (null) | (null) | (null) |
| 2 | Andrés Company Talens | Responsable de ventas | (null) | (null) | (null) |
| 3 | Beatriz Nadal Ripoll | Responsable de logística | (null) | (null) | (null) |
| 4 | Óscar Peris Blasco | Comercial | 2 | 2025-03-12 | entregado |
| 4 | Óscar Peris Blasco | Comercial | 6 | 2025-05-23 | cancelado |
| 4 | Óscar Peris Blasco | Comercial | 10 | 2025-08-03 | entregado |
| 4 | Óscar Peris Blasco | Comercial | 16 | 2025-12-19 | enviado |
| 5 | Laia Puig Sanchis | Comercial | 4 | 2025-04-19 | entregado |
| 5 | Laia Puig Sanchis | Comercial | 8 | 2025-06-28 | entregado |
| 5 | Laia Puig Sanchis | Comercial | 12 | 2025-10-01 | entregado |
| 5 | Laia Puig Sanchis | Comercial | 18 | 2026-01-27 | pagado |
| 6 | Marc Estévez Roig | Atención al cliente | 14 | 2025-11-14 | entregado |
| 6 | Marc Estévez Roig | Atención al cliente | 20 | 2026-02-21 | pendiente |
| 7 | Irene Salvador Mira | Operaria de almacén | (null) | (null) | (null) |
| 8 | Daniel Vercher Lluch | Analista de datos | (null) | (null) | (null) |
Resultado idéntico, fila a fila. No se parece: es el mismo.
La regla general, que puedes aplicar mecánicamente:
A RIGHT JOIN B ON <cond>≡B LEFT JOIN A ON <cond>Para convertir un
RIGHT JOINenLEFT JOIN: intercambia el orden de las dos tablas y cambia la palabra. La condiciónONse queda exactamente igual.
flowchart LR
A["FROM pedidos<br/>RIGHT JOIN empleados<br/>ON pe.empleado_id = e.id"] -->|"intercambiar tablas<br/>+ cambiar la palabra"| B["FROM empleados<br/>LEFT JOIN pedidos<br/>ON pe.empleado_id = e.id"]
Lo único que puede cambiar entre ambas versiones es el orden de las columnas si usaras SELECT *, y el orden de las filas si no hubiera ORDER BY. El conjunto de datos es el mismo.
De aquí sale una conclusión honesta: el RIGHT JOIN es prescindible. No existe ninguna consulta que solo pueda escribirse con RIGHT JOIN. Y sin embargo hay que conocerlo, porque existe en el código que heredarás.
- Por qué casi todo el mundo prefiere
LEFT
LEFTSi son equivalentes, ¿por qué prácticamente todas las guías de estilo profesionales recomiendan LEFT?
| Motivo | Explicación |
|---|---|
| Se lee en el orden en que se piensa | "Todos los empleados, con sus pedidos" empieza por empleados. En la versión RIGHT, la tabla protagonista está enterrada en la última línea |
| La tabla principal es la primera | El lector identifica de un vistazo la granularidad del resultado: una fila por... lo que sea que esté en el FROM |
| Consistencia en cadenas largas | Con cinco tablas, si todas las uniones externas son LEFT, el FROM se lee de arriba abajo como un camino. Si alternas, hay que ir y volver |
| Menos carga mental al depurar | Cuando algo falla, buscar "qué tabla conservo entera" es inmediato si siempre es la primera |
| Es lo que espera el equipo | El LEFT JOIN es abrumadoramente mayoritario en la práctica. Un RIGHT JOIN hace que quien revisa el código se detenga a comprobar si es intencionado |
Convención del curso: escribiremos
LEFT JOINsiempre que podamos elegir, y colocaremos como primera tabla delFROMaquella cuyas filas deben aparecer todas. ElRIGHT JOINaparece en este curso para que sepas leerlo y reescribirlo, no como estilo recomendado.
- Cuándo un
RIGHT JOIN sí resulta natural
RIGHT JOIN sí resulta naturalDicho lo anterior, hay dos situaciones en las que escribir un RIGHT JOIN es razonable.
5.1. Añadir una tabla al final de una consulta ya escrita
Imagina que tienes esta consulta funcionando en producción, con su SELECT de doce columnas y sus filtros:
SELECT lp.id, lp.cantidad, lp.precio_unitario, ...
FROM lineas_pedido AS lp
JOIN pedidos AS pe ON lp.pedido_id = pe.id
WHERE pe.fecha_pedido >= DATE '2025-01-01'
AND pe.fecha_pedido < DATE '2026-01-01';Y te piden que el informe incluya también los productos que no se vendieron. La reescritura "correcta" obliga a poner productos como primera tabla y reordenar todo el FROM. Añadir una línea al final es mucho menos invasivo:
Es un uso legítimo, con una condición: documéntalo. Un comentario de una línea (-- RIGHT para conservar los productos sin ventas) evita que el siguiente lector piense que es una errata.
5.2. Traducir un requisito escrito de forma mecánica
Cuando el requisito llega redactado como "las líneas de pedido, y además todos los productos aunque no tengan líneas", escribirlo en ese mismo orden es lo más fiel:
SELECT p.id AS producto_id,
p.nombre AS producto,
lp.id AS linea_id,
lp.cantidad
FROM lineas_pedido AS lp
RIGHT JOIN productos AS p ON lp.producto_id = p.id
ORDER BY p.id, lp.id;Devuelve 50 filas: las 47 líneas más los 3 productos nunca vendidos. Es exactamente el mismo resultado que el productos LEFT JOIN lineas_pedido de 03-03.
Aun así, la práctica habitual es traducir primero el requisito y reescribirlo después como LEFT antes de subirlo al repositorio.
- El anti-join en versión
RIGHT
RIGHTEl patrón de 03-03 funciona igual, cambiando qué lado se comprueba. Para responder a "¿qué empleados nunca han gestionado un pedido?":
SELECT e.id AS empleado_id,
e.nombre || ' ' || e.apellidos AS empleado,
e.puesto,
e.fecha_contratacion
FROM pedidos AS pe
RIGHT JOIN empleados AS e ON pe.empleado_id = e.id
WHERE pe.id IS NULL
ORDER BY e.id;| empleado_id | empleado | puesto | fecha_contratacion |
|---|---|---|---|
| 1 | Rosa Alcázar Vives | Directora general | 2024-09-01 |
| 2 | Andrés Company Talens | Responsable de ventas | 2024-10-15 |
| 3 | Beatriz Nadal Ripoll | Responsable de logística | 2024-11-02 |
| 7 | Irene Salvador Mira | Operaria de almacén | 2025-04-07 |
| 8 | Daniel Vercher Lluch | Analista de datos | 2025-06-16 |
5 filas. Y la misma consulta escrita como LEFT, que es como la escribiríamos en el curso:
-- ✅ Preferida
SELECT e.id AS empleado_id,
e.nombre || ' ' || e.apellidos AS empleado,
e.puesto,
e.fecha_contratacion
FROM empleados AS e
LEFT JOIN pedidos AS pe ON pe.empleado_id = e.id
WHERE pe.id IS NULL
ORDER BY e.id;Resultado idéntico. Fíjate en el detalle que se mantiene entre las dos versiones: la columna comprobada con IS NULL es siempre la clave primaria de la tabla opcional (pe.id), no la del lado que se conserva. La palabra LEFT o RIGHT cambia; la lógica del anti-join, no.
Interpretación del resultado: que cinco de los ocho empleados no tengan pedidos no es un problema de datos. Solo dos comerciales y una persona de atención al cliente gestionan ventas; la dirección, la logística y el análisis de datos no. Es una consulta que confirma el modelo de negocio, no que lo contradiga.
- El peligro real: mezclar
LEFT y RIGHT en una cadena
LEFT y RIGHT en una cadenaAquí está la razón de peso para tener disciplina de estilo. Considera esta consulta, escrita por alguien que quería "todos los clientes, con sus pedidos y el empleado de cada pedido":
-- ⚠️ INCORRECTA: no hace lo que su autor cree
SELECT c.nombre AS cliente,
pe.id AS pedido_id,
e.nombre AS empleado
FROM clientes AS c
LEFT JOIN pedidos AS pe ON pe.cliente_id = c.id
RIGHT JOIN empleados AS e ON pe.empleado_id = e.id
ORDER BY e.id, pe.id;| cliente | pedido_id | empleado |
|---|---|---|
| (null) | (null) | Rosa |
| (null) | (null) | Andrés |
| (null) | (null) | Beatriz |
| Carlos | 2 | Óscar |
| Ana | 6 | Óscar |
| Camille | 10 | Óscar |
| Javier | 16 | Óscar |
| Javier | 4 | Laia |
| Sofia | 8 | Laia |
| Julien | 12 | Laia |
| Ana | 18 | Laia |
| Diego | 14 | Marc |
| Camille | 20 | Marc |
| (null) | (null) | Irene |
| (null) | (null) | Daniel |
15 filas, y ni rastro de Núria, Hugo o Inés. El LEFT JOIN que se escribió para conservarlos no ha servido de nada.
Por qué
Los JOIN se resuelven de izquierda a derecha:
flowchart TD
A["clientes (15)"] --> B["clientes LEFT JOIN pedidos<br/>= 23 filas<br/>(incluye a Núria, Hugo, Inés)"]
B --> C["ese resultado de 23 filas<br/>RIGHT JOIN empleados"]
C --> D["RIGHT conserva TODOS los empleados<br/>pero descarta las filas izquierdas<br/>sin empleado que case"]
D --> E["15 filas:<br/>10 emparejamientos + 5 empleados solos<br/>❌ Núria, Hugo e Inés desaparecen"]
El RIGHT JOIN conserva enteramente la tabla derecha (empleados) y descarta las filas izquierdas que no casan. Y las filas de Núria, Hugo e Inés tienen pe.empleado_id = NULL, así que no casan con ningún empleado y se van. Igual que las 10 filas de pedidos web.
Es la misma lógica del "LEFT seguido de INNER" de 03-03, pero disfrazada: aquí el RIGHT actúa sobre la acumulación de todo lo anterior, no sobre la tabla que tiene al lado.
La reescritura
La consulta de arriba, en realidad, responde a "todos los empleados con los pedidos que gestionaron y sus clientes". Escrita solo con LEFT queda transparente:
-- ✅ CORRECTA: lo mismo, legible
SELECT c.nombre AS cliente,
pe.id AS pedido_id,
e.nombre AS empleado
FROM empleados AS e
LEFT JOIN pedidos AS pe ON pe.empleado_id = e.id
LEFT JOIN clientes AS c ON pe.cliente_id = c.id
ORDER BY e.id, pe.id;Mismas 15 filas. Ahora el FROM se lee de arriba abajo: empleados → sus pedidos → el cliente de cada pedido, y la primera tabla anuncia sin ambigüedad que el informe tiene una fila por empleado como mínimo.
Y si lo que de verdad quería el autor era conservar a los tres clientes sin pedidos, la consulta es otra:
-- ✅ CORRECTA para "todos los clientes"
FROM clientes AS c
LEFT JOIN pedidos AS pe ON pe.cliente_id = c.id
LEFT JOIN empleados AS e ON pe.empleado_id = e.idQue devuelve 23 filas, como viste en 03-03.
Regla de oro: en una cadena de tres o más tablas, no mezcles
LEFTyRIGHT. Elige el lado que quieres conservar, ponlo como primera tabla delFROMy usa soloLEFT JOINa partir de ahí. Una consulta conLEFTyRIGHTalternados es correcta para el motor e ilegible para las personas, que es la peor combinación posible.
Comparativa de las tres versiones sobre las mismas tres tablas:
| Consulta | Filas | Qué conserva |
|---|---|---|
clientes LEFT pedidos RIGHT empleados |
15 | Todos los empleados. Los clientes sin pedido se pierden |
empleados LEFT pedidos LEFT clientes |
15 | Todos los empleados (idéntica a la anterior, legible) |
clientes LEFT pedidos LEFT empleados |
23 | Todos los clientes, incluidos los tres sin pedidos |
- Soporte por motor
| Motor | RIGHT JOIN |
Nota |
|---|---|---|
| PostgreSQL | ✅ Desde siempre | Sin restricciones |
| MySQL / MariaDB | ✅ | Sin restricciones |
| SQL Server | ✅ | La sintaxis antigua *= está eliminada desde 2012 |
| Oracle | ✅ | La sintaxis antigua (+) sigue existiendo pero está desaconsejada |
| SQLite | ✅ solo desde 3.39 (junio de 2022) | Antes había que reescribirlo como LEFT JOIN |
Ese último caso es un argumento adicional a favor del LEFT JOIN: un LEFT JOIN funciona en cualquier motor y en cualquier versión, incluidos los SQLite embebidos en aplicaciones móviles antiguas. Escribir siempre LEFT es también una decisión de portabilidad.
Nota de dialecto: en Oracle antiguo,
A RIGHT JOIN Bse escribía poniendo el(+)en el lado contrario al que se conserva:WHERE a.id(+) = b.id. Esa notación es una fuente inagotable de errores y no debe usarse en código nuevo, aunque la encontrarás en sistemas heredados.
Errores Comunes y Consejos
- Confundir qué lado se conserva. En
A RIGHT JOIN B, lo que se conserva entero es B, la tabla que va después de la palabra. Es lo contrario de lo que sugiere la lectura de izquierda a derecha. - Mezclar
LEFTyRIGHTen la misma cadena. ElRIGHTactúa sobre todo lo acumulado hasta ese punto, no sobre la tabla contigua, y puede anular en silencio unLEFTanterior. - Poner una condición sobre la tabla izquierda en el
WHEREde unRIGHT JOIN. Lo degrada aINNER JOIN, exactamente igual que en 03-03 pero con los lados cambiados. - Hacer el anti-join contra la columna equivocada. En un
RIGHT JOIN, la columna que se comprueba conIS NULLes la clave primaria de la tabla izquierda (la opcional), no la de la que se conserva. - Usar
RIGHT JOINsin comentar por qué. Quien revise el código asumirá que es un descuido. Un comentario de una línea lo resuelve. - Escribir
RIGHT JOINen código que debe correr sobre SQLite antiguo. No existe antes de la versión 3.39. - Consejo: aprende la conversión mecánica. Intercambia las dos tablas, cambia
RIGHTporLEFT, deja elONintacto. Funciona siempre y es la forma más rápida de entender unRIGHT JOINajeno. - Consejo: al revisar código, reescribe mentalmente todo
RIGHTcomoLEFTantes de razonar sobre él. Es más rápido que intentar seguir la lógica invertida. - Consejo: decide el lado protagonista antes de escribir una línea. "¿Qué sustantivo debe salir completo en el informe?" Esa tabla va primera, y todo lo demás es
LEFT JOIN.
Ejercicios
Ejercicio 1
Escribe con RIGHT JOIN la consulta que devuelve todos los productos con las líneas de venta que hayan tenido: id y nombre del producto, id de la línea y cantidad. Después reescríbela con LEFT JOIN y comprueba que el resultado es idéntico.
¿Cuántas filas devuelve y de dónde sale ese número?
Ejercicio 2
Recursos humanos quiere el listado de empleados que nunca han gestionado un pedido, con su puesto, su ciudad y el nombre de pila de su jefe.
- Escríbelo con
RIGHT JOINpara la parte de los pedidos. - Reescríbelo entero con
LEFT JOIN. - Explica por qué el
JOINcon la tabla de jefes debe serLEFTy noINNER.
(Pista: unir empleados consigo misma para obtener el jefe es un SELF JOIN; se explica a fondo en 03-06, pero aquí basta con usar dos alias distintos de la misma tabla.)
Ejercicio 3
Analiza esta consulta sin ejecutarla:
SELECT p.nombre AS producto, lp.id AS linea_id, cat.nombre AS categoria
FROM lineas_pedido AS lp
RIGHT JOIN productos AS p ON lp.producto_id = p.id
LEFT JOIN categorias AS cat ON p.categoria_id = cat.id
WHERE lp.cantidad > 2;- ¿Cuál crees que era la intención de quien la escribió?
- ¿Qué hace realmente? ¿Aparecen los productos nunca vendidos?
- Corrígela para que devuelva todos los productos, mostrando solo las líneas de más de 2 unidades y
NULLpara el resto.
Soluciones
Solución 1
Con RIGHT JOIN:
SELECT p.id AS producto_id,
p.nombre AS producto,
lp.id AS linea_id,
lp.cantidad
FROM lineas_pedido AS lp
RIGHT JOIN productos AS p ON lp.producto_id = p.id
ORDER BY p.id, lp.id;Con LEFT JOIN (la forma preferida):
SELECT p.id AS producto_id,
p.nombre AS producto,
lp.id AS linea_id,
lp.cantidad
FROM productos AS p
LEFT JOIN lineas_pedido AS lp ON lp.producto_id = p.id
ORDER BY p.id, lp.id;Ambas devuelven 50 filas:
Las tres filas huérfanas son:
| producto_id | producto | linea_id | cantidad |
|---|---|---|---|
| 13 | Velas de cera de soja (pack 2) | (null) | (null) |
| 19 | Desodorante natural en barra 50 g | (null) | (null) |
| 20 | Cápsulas de espirulina 120 uds | (null) | (null) |
La transformación aplicada es la mecánica de la sección 3: se intercambian lineas_pedido y productos, RIGHT pasa a LEFT, y la condición ON lp.producto_id = p.id no se toca.
Solución 2
1. Con RIGHT JOIN en la parte de pedidos:
SELECT e.id AS empleado_id,
e.nombre || ' ' || e.apellidos AS empleado,
e.puesto,
e.ciudad,
jefe.nombre AS jefe
FROM pedidos AS pe
RIGHT JOIN empleados AS e ON pe.empleado_id = e.id
LEFT JOIN empleados AS jefe ON e.jefe_id = jefe.id
WHERE pe.id IS NULL
ORDER BY e.id;2. Reescrito solo con LEFT:
-- ✅ Preferida
SELECT e.id AS empleado_id,
e.nombre || ' ' || e.apellidos AS empleado,
e.puesto,
e.ciudad,
jefe.nombre AS jefe
FROM empleados AS e
LEFT JOIN pedidos AS pe ON pe.empleado_id = e.id
LEFT JOIN empleados AS jefe ON e.jefe_id = jefe.id
WHERE pe.id IS NULL
ORDER BY e.id;| empleado_id | empleado | puesto | ciudad | jefe |
|---|---|---|---|---|
| 1 | Rosa Alcázar Vives | Directora general | Valencia | (null) |
| 2 | Andrés Company Talens | Responsable de ventas | Valencia | Rosa |
| 3 | Beatriz Nadal Ripoll | Responsable de logística | Valencia | Rosa |
| 7 | Irene Salvador Mira | Operaria de almacén | Valencia | Beatriz |
| 8 | Daniel Vercher Lluch | Analista de datos | Valencia | Rosa |
5 filas.
3. Por qué el JOIN con los jefes debe ser LEFT: porque Rosa Alcázar Vives no tiene jefe (jefe_id IS NULL, es la directora general). Con un INNER JOIN contra empleados AS jefe, su fila no encontraría pareja y desaparecería del resultado, que pasaría de 5 a 4 filas. Es exactamente el caso del "LEFT seguido de INNER" de 03-03: un solo INNER en la cadena elimina la fila que más interesa. La jerarquía completa de empleados se estudia en 03-06.
Solución 3
1. La intención era, con toda probabilidad, "todos los productos con sus líneas de más de 2 unidades", conservando el catálogo entero gracias al RIGHT JOIN.
2. Qué hace realmente. La condición lp.cantidad > 2 está en el WHERE y afecta a una columna de la tabla izquierda, que es justo la opcional en un RIGHT JOIN. Los tres productos nunca vendidos llegan al WHERE con lp.cantidad = NULL, y NULL > 2 no es TRUE. Desaparecen. El RIGHT JOIN se degrada a INNER JOIN y la consulta devuelve solo las líneas de más de 2 unidades con su producto: no aparece ningún producto nunca vendido.
Es la trampa de 03-03 vista en el espejo: allí la condición peligrosa era sobre la tabla derecha de un LEFT; aquí, sobre la izquierda de un RIGHT.
3. Corrección. Hay que mover la condición al ON y, ya puestos, reescribir todo con LEFT poniendo productos como tabla protagonista:
-- ✅ CORRECTA
SELECT p.nombre AS producto,
cat.nombre AS categoria,
lp.id AS linea_id,
lp.cantidad
FROM productos AS p
LEFT JOIN categorias AS cat ON p.categoria_id = cat.id
LEFT JOIN lineas_pedido AS lp
ON lp.producto_id = p.id
AND lp.cantidad > 2
ORDER BY p.id, lp.id;Ahora los 20 productos aparecen siempre; los que no tengan ninguna línea de más de 2 unidades muestran *(null)* en linea_id y cantidad. Observa además que el JOIN con categorias se ha escrito como LEFT por prudencia y coherencia de estilo: aquí no cambia nada porque todo producto tiene categoría, pero mantiene la regla de "abierta una rama con LEFT, sigue con LEFT".
Conclusión
El RIGHT JOIN ya no te sorprenderá en ningún código:
- Conserva todas las filas de la tabla derecha, la que va después de la palabra
JOIN, rellenando conNULLlas columnas de la izquierda.RIGHT JOIN=RIGHT OUTER JOIN. - Has visto por fin a los cinco empleados sin pedidos: Rosa, Andrés, Beatriz, Irene y Daniel. 15 filas frente a las 10 del
INNER JOIN. - Conoces la equivalencia formal:
A RIGHT JOIN B≡B LEFT JOIN A. Intercambia las tablas, cambia la palabra, deja elONintacto. Ninguna consulta necesita unRIGHT JOIN. - Sabes por qué se prefiere
LEFT: la tabla protagonista se lee primero, las cadenas largas se leen de arriba abajo, y funciona en todos los motores y versiones (SQLite no admiteRIGHThasta la 3.39). - Sabes cuándo un
RIGHTes defendible: al añadir una tabla al final de una consulta ya escrita o al traducir un requisito literalmente. Siempre con un comentario que lo justifique. - El anti-join en versión
RIGHTfunciona igual, comprobando conIS NULLla clave primaria de la tabla izquierda. - Y sobre todo: no mezcles
LEFTyRIGHTen una cadena. ElRIGHTactúa sobre todo lo acumulado y puede anular en silencio unLEFTanterior, como en el ejemplo donde Núria, Hugo e Inés desaparecían pese alLEFT JOINescrito para conservarlos.
En la lección siguiente, FULL OUTER JOIN, cerraremos la familia de los joins externos con el que conserva los huérfanos de ambos lados a la vez. Verás por qué en un esquema con integridad referencial —como el de TiendaVerde— casi nunca hay huérfanos en los dos lados, y por qué su verdadero terreno de juego es la conciliación de dos fuentes de datos independientes: un catálogo contra un fichero de ventas externo, un inventario contra un sistema contable.
Curso de SQL
Módulo 1: Introducción a SQL
- ¿Qué es SQL?
- Configurando tu entorno SQL
- Sintaxis básica de SQL
- Entendiendo bases de datos y tablas
- El modelo relacional: claves primarias y foráneas
- La base de datos del curso: TiendaVerde
Módulo 2: Consultas básicas de SQL
- Instrucción SELECT
- Alias, expresiones y columnas calculadas
- Filtrando datos con WHERE
- DISTINCT y eliminación de duplicados
- Ordenando datos con ORDER BY
- Limitando resultados con LIMIT
Módulo 3: Trabajando con múltiples tablas
- Operaciones JOIN
- INNER JOIN
- LEFT JOIN
- RIGHT JOIN
- FULL OUTER JOIN
- SELF JOIN y CROSS JOIN
- Uniones de conjuntos: UNION, INTERSECT y EXCEPT
Módulo 4: Filtrado avanzado de datos
- Usando LIKE para coincidencia de patrones
- Operadores IN y BETWEEN
- Valores NULL y IS NULL
- Funciones de agregación: COUNT, SUM, AVG, MIN y MAX
- Agregando datos con GROUP BY
- Cláusula HAVING
Módulo 5: Manipulación de datos
- Creando tablas y restricciones con CREATE TABLE
- Instrucción INSERT
- Instrucción UPDATE
- Instrucción DELETE
- Instrucción UPSERT (MERGE)
- Modificando el esquema: ALTER TABLE y migraciones seguras
Módulo 6: Funciones avanzadas de SQL
- Funciones de cadena
- Funciones numéricas
- Funciones de fecha y hora
- Conversión de tipos y manejo de NULL: CAST y COALESCE
- Expresiones condicionales
Módulo 7: Subconsultas y consultas anidadas
- Introducción a subconsultas
- Subconsultas correlacionadas
- EXISTS y NOT EXISTS
- Usando subconsultas en cláusulas SELECT, FROM y WHERE
- Subconsultas o JOIN: cuál elegir
Módulo 8: Índices y optimización de rendimiento
- Entendiendo los índices
- Creación y gestión de índices
- Tipos de índice y cuándo no indexar
- Técnicas de optimización de consultas
- Análisis del rendimiento de consultas
Módulo 9: Transacciones y concurrencia
- Introducción a las transacciones
- Propiedades ACID
- Instrucciones de control de transacciones
- Niveles de aislamiento y anomalías de concurrencia
- Manejo de concurrencia: bloqueos e interbloqueos
Módulo 10: Temas avanzados
- Vistas
- Expresiones de tabla comunes (CTE)
- Funciones de ventana
- Procedimientos almacenados
- Triggers
- JSON y datos semiestructurados
Módulo 11: SQL en la práctica
- Casos de uso en el mundo real
- Mejores prácticas
- Seguridad: inyección SQL, permisos y roles
- SQL para análisis de datos
- SQL en desarrollo web
