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

  1. La regla del RIGHT JOIN y el diagrama de conjuntos
  2. Todos los empleados con los pedidos que gestionaron
  3. La equivalencia formal: A RIGHT JOIN BB LEFT JOIN A
  4. Por qué casi todo el mundo prefiere LEFT
  5. Cuándo un RIGHT JOIN sí resulta natural
  6. El anti-join en versión RIGHT
  7. El peligro real: mezclar LEFT y RIGHT en una cadena
  8. Soporte por motor
  9. Errores Comunes y Consejos
  10. Ejercicios
  11. Conclusión

  1. La regla del RIGHT JOIN y el diagrama de conjuntos

flowchart 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 palabra OUTER es opcional.
  • Los NULL los fabrica el motor, ahora en las columnas de la tabla izquierda.
  • El paso 1d del orden lógico reintroduce las filas derechas huérfanas dentro del FROM, antes del WHERE.
  • Y por tanto una condición en el WHERE sobre una columna de la tabla izquierda degrada el RIGHT JOIN a INNER JOIN. Es exactamente la trampa de 03-03, reflejada.

  1. 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:

filas del resultado = filas que casan + filas DERECHAS huérfanas
15 = 10 + 5

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.

  1. La equivalencia formal: A RIGHT JOIN BB LEFT JOIN A

Escribamos 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 JOIN en LEFT JOIN: intercambia el orden de las dos tablas y cambia la palabra. La condición ON se 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.

  1. Por qué casi todo el mundo prefiere LEFT

Si 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 JOIN siempre que podamos elegir, y colocaremos como primera tabla del FROM aquella cuyas filas deben aparecer todas. El RIGHT JOIN aparece en este curso para que sepas leerlo y reescribirlo, no como estilo recomendado.

  1. Cuándo un RIGHT JOIN sí resulta natural

Dicho 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:

...
RIGHT JOIN productos AS p ON lp.producto_id = p.id

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.

  1. El anti-join en versión RIGHT

El 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.

  1. El peligro real: mezclar LEFT y RIGHT en una cadena

Aquí 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.id

Que devuelve 23 filas, como viste en 03-03.

Regla de oro: en una cadena de tres o más tablas, no mezcles LEFT y RIGHT. Elige el lado que quieres conservar, ponlo como primera tabla del FROM y usa solo LEFT JOIN a partir de ahí. Una consulta con LEFT y RIGHT alternados 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

  1. 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 B se 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 LEFT y RIGHT en la misma cadena. El RIGHT actúa sobre todo lo acumulado hasta ese punto, no sobre la tabla contigua, y puede anular en silencio un LEFT anterior.
  • Poner una condición sobre la tabla izquierda en el WHERE de un RIGHT JOIN. Lo degrada a INNER 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 con IS NULL es la clave primaria de la tabla izquierda (la opcional), no la de la que se conserva.
  • Usar RIGHT JOIN sin comentar por qué. Quien revise el código asumirá que es un descuido. Un comentario de una línea lo resuelve.
  • Escribir RIGHT JOIN en 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 RIGHT por LEFT, deja el ON intacto. Funciona siempre y es la forma más rápida de entender un RIGHT JOIN ajeno.
  • Consejo: al revisar código, reescribe mentalmente todo RIGHT como LEFT antes 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.

  1. Escríbelo con RIGHT JOIN para la parte de los pedidos.
  2. Reescríbelo entero con LEFT JOIN.
  3. Explica por qué el JOIN con la tabla de jefes debe ser LEFT y no INNER.

(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;
  1. ¿Cuál crees que era la intención de quien la escribió?
  2. ¿Qué hace realmente? ¿Aparecen los productos nunca vendidos?
  3. Corrígela para que devuelva todos los productos, mostrando solo las líneas de más de 2 unidades y NULL para 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:

47 líneas de pedido que casan
+ 3 productos sin ninguna línea (13, 19 y 20)
= 50

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 con NULL las 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 BB LEFT JOIN A. Intercambia las tablas, cambia la palabra, deja el ON intacto. Ninguna consulta necesita un RIGHT 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 admite RIGHT hasta la 3.39).
  • Sabes cuándo un RIGHT es 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 RIGHT funciona igual, comprobando con IS NULL la clave primaria de la tabla izquierda.
  • Y sobre todo: no mezcles LEFT y RIGHT en una cadena. El RIGHT actúa sobre todo lo acumulado y puede anular en silencio un LEFT anterior, como en el ejemplo donde Núria, Hugo e Inés desaparecían pese al LEFT JOIN escrito 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

Módulo 2: Consultas básicas de SQL

Módulo 3: Trabajando con múltiples tablas

Módulo 4: Filtrado avanzado de datos

Módulo 5: Manipulación de datos

Módulo 6: Funciones avanzadas de SQL

Módulo 7: Subconsultas y consultas anidadas

Módulo 8: Índices y optimización de rendimiento

Módulo 9: Transacciones y concurrencia

Módulo 10: Temas avanzados

Módulo 11: SQL en la práctica

Módulo 12: Proyecto final

© Copyright 2026. Todos los derechos reservados