Explicación visual de los SQL Join - Unir tablas con SQL
Escrito por Alex Barros Me ha parecido muy interesante el artículo escrito por Jeff Atwood titulado A Visual Explanation of SQL Joins.
Tanto es así que he decidido traducir este artículo al español. Disfrutad:
Autor Original: Jeff Atwood
Fecha: 11/10/07
Traductor: Alex Barros
Pensé que el post de Ligaya Turmelle sobre SQL joins era una genial introducción para los desarrolladores primerizos. Dado que los SQL joins están basados fundamentalmente en conjuntos relacionados, el uso de diagramas de Venn para explicarlo parece lo más acertado. De todas formas, igual que los comentaristas de su post, opino que sus diagramas de Venn no encajaban con la sintaxis de SQL join en mis pruebas.
Aún así me encanta el concepto, así que veamos si podemos hacerlo funcionar. Supongamos que tenemos las dos siguientes tablas. La tabla A está a la izquierda, y la tabla B está a la derecha. Las rellenaremos con cuatro registros cada una.

Ahora unamos estas dos tablas por el campo nombre de algunas formas distintas, y veamos si podemos obtener unas representaciones conceptuales con los ingeniosos diagramas de Venn.
Inner join sólo produce los registros que coinciden en las dos tablas A y B.
-
SELECT * FROM TablaA
-
INNER JOIN TablaB
-
ON TablaA.nombre = TablaB.nombre

Full outer join produce el conjunto de todos los registros en las tablas A y B, con registros coincidentes en ambos lados cuando sea posible. Si no hay coincidencia, el lado que falta contendrá null.
-
SELECT * FROM TablaA
-
FULL OUTER JOIN TablaB
-
ON TablaA.nombre = TablaB.nombre

Left outer join produce el conjunto completo de registros de la tabla A, con los registros coincidentes (si están disponibles) en la tabla B. Si no hay coincidencia, el lado derecho contendrá null.
-
SELECT * FROM TablaA
-
LEFT OUTER JOIN TablaB
-
ON TablaA.nombre = TablaB.nombre

Para producir el conjunto de registros en la tabla A, pero no en la tabla B, usamos el mismo Left Outer Join, y luego excluimos los registros que no queremos del lado derecho mediante una cláusula Where.
-
SELECT * FROM TablaA
-
LEFT OUTER JOIN TablaB
-
ON TablaA.nombre = TablaB.nombre
-
WHERE TablaB.id IS NULL

Para producir el conjunto de registros únicos de la tabla A y la tabla B, usamos el mismo Full Outer Join, y luego excluimos los registros que no queremos de los dos lados mediante una cláusula Where.
-
SELECT * FROM TablaA
-
FULL OUTER JOIN TablaB
-
ON TablaA.nombre = TablaB.nombre
-
WHERE TablaA.id IS NULL
-
OR TablaB.id IS NULL

También hay un cross join, el cuál no puede ser expresado con un diagrama de Venn:
-
SELECT * FROM TablaA
-
CROSS JOIN TablaB
Esto une “todo con todo”, dando como resultado 4 x 4 = 16 filas, muchas más de las que teníamos en los conjuntos originales. Si haces unos simples cálculos, puedes ver por qué es un Join muy peligroso de ejecutar en tablas grandes.





Respeta el copyleft
Octubre
22:13 bendem Ha dicho:
Whow!! Muchas gracias… joder que sencillo que es verlo con estos gráficos de conjuntos.
Octubre
22:19 Alex Barros Ha dicho:
Eso mismo pensé yo al leer el artículo original. No pude resistir la traducción, esto nadie debería perdérselo.
[...] of SQL Joins. Tanto es as que he decidido traducir este artculo al espaol.Articulo completo: http://boozox.net/mysql/explicacion-visual-de-los-sql-join-unir-tablas-con-sql/ [...]
Octubre
14:00 gafeman Ha dicho:
ojala hubiese tenido como tu y no uno que conozco jeje
Octubre
17:01 Alex Barros Ha dicho:
Jeje, siempre viene bien una explicación detallada como esta.
A más de un profesor de mi universidad le pedía yo que hiciera cosas así…
Como dice un dicho español: Las cosas claras, y el chocolate espeso!
[...] Por aquí me encuentro con este post: Explicación visual de los SQL Join. [...]
Octubre
18:15 PRINCIPAINTE Ha dicho:
CON ESTE ARTITUCULO SALI DE DUDAS SOBRE EL MANEJO DE JOIN, ESTA CLARISIMO OJALA HUBIERA MAS ARTICULOS ASI DE BIEN EXPLICADOS, IGUAL SI ME PUDIERAN AYUDAR CON SUM YA QUE LO HE INTENTADO PERO NO PUEDO REALIZAR UNA CONSULTA UTILIZANDO SUM Y JOIN
ESTE ES MI CODIGO, PERO CUANDO TRATO DE AGREGAR MAS CAMPOS PARA MOSTRAR ME MANDA ERROR
/*TRANSFORM Sum(T0.QUANTITY) AS ‘CANTIDAD’, SUM(T0.LINETOTAL)AS ‘TOTAL’*/select T0.BASECARD AS ‘CODIGO’, SUM ( T0.QUANTITY ) AS ‘CANTIDAD’, sum ( T0.LINETOTAL) AS ‘TOTAL’
from OINV T1 FULL OUTER JOIN INV1 T0 ON T0.basecard =T1.cardcode GROUP BY T0.basecard ORDER BY T0.basecard
Octubre
14:04 Alex Barros Ha dicho:
Principiante, te agradecería que no escribas en mayúsculas, poque cuesta leer, y porque se considera gritar.
No domino demasiado el lenguaje SQL (este artículo es una traducción). Lo único que he notado extraño en tu código son las asignaciones. Cuando escribes Sum(T0.QUANTITY) AS ‘CANTIDAD’ asignas a CANTIDAD el resultado anterior, pero que yo recuerde no debe llevar comillas, cuando haces asignaciones.
El código te quedaría así:
SELECT T0.BASECARD AS CODIGO, SUM ( T0.QUANTITY ) AS CANTIDAD, sum ( T0.LINETOTAL) AS TOTALfrom OINV T1 FULL OUTER JOIN INV1 T0 ON T0.basecard =T1.cardcode GROUP BY T0.basecard ORDER BY T0.basecard
Contéstame si te ha funcionado o no.
Diciembre
19:06 maykol Ha dicho:
estoy usando la siguiente consulta de union y me da error de sintaxis en la clausula from
SELECT *FROM consultorio
INNER JOIN caminata 6 minutos ON consultorio.ecg_pdf = caminata 6 minutos.ecg_pdf
Diciembre
1:20 Alex Barros Ha dicho:
Separa el asterisco del from, eso lo primero.
Y en segundo lugar, no recomiendo que tengas una tabla cuyo nombre contiene espacios.
Aparte de esos dos apuntes, todo el resto es correcto, si existen las tablas “consultorio” y “caminata 6 minutos” y los campos “ecg_pdf” en las dos tablas.
Enero
6:29 VicHaunter Ha dicho:
Un premio a la sencillez. Me quito el sombrero, has resuelto todas mis dudas sobre su funcionamiento y mira que le he dado vueltas. Gracias y un saludo
Enero
21:16 Alex Barros Ha dicho:
Todo un placer. Me alegra que pueda servir. Es un recurso que no pude dejar de traducir, por su enorme claridad ante esta cuestión algo compleja.
Febrero
16:39 Fabian Andres Ha dicho:
ESta muy interesante, te amo.
Marzo
13:12 Nico Ha dicho:
Excelente artículo, muchas gracias por tu tiempo. Te hago una consulta: es posible hacer un Inner Join con 3 tablas? porque he probado diferentes sintaxis pero no encuentro una que resulte.
Una vez más, muchas gracias.
Marzo
13:43 Jorge Troncoso Ha dicho:
Alex Barros, con una simple prueba podras darte cuenta que no es necesario separar el asterisco de la palabra FROM, eso no tiene nada que ver. De echo una consulta puede inclusive hacerse asi:
select*from tabla
…y funcionará.
Saludos.
Marzo
21:28 Alex Barros Ha dicho:
Gracias por el apunte, Jorge. No conocía si funciona escribiendo todo junto, era más un tema de pulcritud en el código.
@nico: Realmente no lo se, y algunas personas me lo han preguntado. Investigaré al respecto.
Abril
0:05 MARINO MENDEZ Ha dicho:
eres el mejor del mundo, estoy a punto de ganarme 3,000 dolares gracias a ti, enviame un correo a ver si se te pega algo
Abril
17:34 paty Ha dicho:
hola que tal esta genial todo esto aunque tengo una preguntilla
cuando tengo dos tablas tabala a y tabla b solo quiero que salga una sola vez el campo con el que estan ligadas espero me entiendas y me ayudes gracias
Abril
15:49 Alex Barros Ha dicho:
@paty: debería saber un poco más el contexto en el que te encuentras. Estás en un script PHP? si deseas que te ayude, tendrás que proporcionar un poco más de información.
Abril
21:45 Sandro Ha dicho:
SELECT (campoligado) FROM TABLA A, TABLA B
WHERE A.campoligado = B.campoligado AND
A.campoligado = ?
Abril
21:46 Sandro Ha dicho:
Muy Agradecido por la Traduccion
Abril
13:33 Alex Barros Ha dicho:
No entiendo muy bien tu primer comentario, Sandro. Necesitas que te ayude con alguna sentencia SQL?
En ese caso te pido que me detalles cuál es el problema y me comentes un poco el contexto en el que te encuentras.
Abril
17:04 paty Ha dicho:
Muchas gracias Alex por todo y dejame decirte que estoy haciendo una aplicacion en netbenas osea con java y como manejador de base de datos en mysql
Abril
19:51 De un principio Piante Ha dicho:
Buenisima explicación, conceptual al maximo.
digno.
Alex
Abril
20:27 De un principio Piante Ha dicho:
Alex,
Mi viejo please,
Esta query de consistencia, funciona, pero, debido al universo (cantidad de registros) de algunas tablas, necesito sumar en esta misma query los que quedan con OK o con error, como lo hago?.Gracias.
Atte, Alex Quiroz.
select tabla1.campo1, tabla2.campo1,
(Case when tabla1.campo1 = tabla2.campo1 Then
‘OK’ Else ‘ERROR’
End)AS resp_create
FROM tabla1 INNER JOIN tabla2 tabla1.llave_tabla1=tabla2.llave_tabla2 Where tabla1.llave_tabla1 IS NOT Null
and IS NOT Null tabla2.llave_tabla2
Order by resp_create
Abril
17:43 luis alberto Ha dicho:
hola ojala me puedan ayudar, soy nuevo en esto del mysql, estoy realizando una base de datos con php quiero mostrar informacion de dos tablas que cree cliente y ciudad ellas tienen en comun el campo cod_ciudad, la idea es mostrar la informacion de la tabla ciudad tambien en el mismo reporte.
gracias de antemano
Abril
23:41 Alex Barros Ha dicho:
Una vez más me pedís ayuda muy general.
Luis: Busca tutoriales sobre lectura y muestra de tablas SQL, practica, después reúne información sobre la unión Inner Join en SQL (como este artículo), practica, haz prueba y error.
Es la única forma de enfrentarse a estas cosas.
Si tienes alguna duda concreta, entonces ya puedes contactar conmigo por mail, o mediante el blog. Pero dudas generales, son demasiado complejas de explicar.
Un saludo.
Abril
5:09 luis alberto Ha dicho:
Bueno gracias a tu consejo investige un poco mas acerca de consultas y logre mi objetivo que era mostrar la informacion de varias tablas en una sola consulta total no se si sea la mas idonea y asi quedo
SELECT * FROM CLIENTE,CIUDAD,ACTIVIDAD_ECONOMICA,DEPARTAMENTO WHERE (CLIENTE.cod_ciudad=CIUDAD.cod_ciudad)and(CLIENTE.cod_actividad=ACTIVIDAD_ECONOMICA.cod_actividad)and (CIUDAD.cod_depto=DEPARTAMENTO.cod_depto)and (CLIENTE.nom_cliente LIKE ‘%” . $_POST['txtbusqueda'] . “%’
como ves se torno algo extensa y compleja “por lo menos para mi” ahora mi inquietud es si hay forma de simplificar esta consulta, y ademas quiero hacer una busqueda multiple desde varios textbox, porque como ves en la parte final de la consulta involucro la busqueda desde un text identificado como txtbusqueda al campo nomcliente, ahora quiero buscar en 6 campos a la ves con valores introducidos en 6 textbox es posible esto?
Abril
12:39 Alex Barros Ha dicho:
La sentencia SQL no es demasiado extensa, al menos en comparación con muchas que he visto y con las que he trabajado. Por la extensión no te preocupes demasiado.
Si quieres buscar en 6 campos al mismo tiempo, puedes convertir el último parentesis en una serie de ORs que busquen en esos campos. Por ejemplo:
… and (CIUDAD.cod_depto=DEPARTAMENTO.cod_depto)and (CLIENTE.nom_cliente LIKE ‘%” . $_POST[’txtbusqueda’] . “%’ OR TABLA.campo LIKE ‘%” . $_POST[’txtbusqueda2’] . “%’ OR TABLA2.campo2 LIKE ‘%” . $_POST[’txtbusqueda3’] . “%’ …… etc)
Abril
17:42 luis alberto Ha dicho:
Gracias Alex por tu pronta respuesta, me ha sido de gran ayuda para entender mas acerca de las consultas en mysql, segui tu consejo al poner los or si dejo cualquiera de los 6 campos de busqueda vacio no filtra informacion me sigue mostrando todos los registros sin embargo si en vez de or pongo and filtra la busqueda por el criterio que ponga en el los texbox, aun la logica no la comprendo no tengo muy claro la diferencia entre un and y un or por lo que realize la consulta a prueba y error si me pudieras explicar la diferencia te agradeceria puede ser algo tonto pero es mejor pasar por tonto 5 minutos que toda la vida.
Mayo
3:24 ARCANGEL Ha dicho:
OLA SR. ALEX KISIERA K ME AYUDARA PARA PODER ENCONTRAR UNA SENTENCIA PARA PODER LISTAR LAS TABLAS DEL SISTEMA….SERIA DE MUI AGRADO K M BRINDARA SU AYUDA…D ANTE MANO MIL GRACIAS…
Mayo
12:13 José Cabo Ha dicho:
Hola Arcangel,
Escribir en mayúsculas en internet y en la web es sinónimo de gritar por eso hay gente, yo me incluyo, que puede sentirse incomodada. Por favor, escribe en minúsculas.
Sobre tu duda, cuando Alex tenga un rato estoy seguro de que te echará una mano.
Mayo
15:11 Alex Barros Ha dicho:
Buenas Arcangel. Como ha dicho José Cabo, te agradeceríamos que escribieras de forma adecuada.
Sobre tu duda, tendrás que explicar mejor el contexto, y qué es lo que deseas. Cuando hablas de “el sistema” a qué te refieres?
La forma de listar tablas, suele ser con PHP, y las funciones que este lenguaje ofrece para tratar con tablas de tipo MySQL (que encontrarás en esta dirección: http://es.php.net/manual/es/ref.mysql.php)
Un saludo.
Mayo
18:42 Daniel Ha dicho:
Muy explicativo, gracias por compartirlo
Saludos!!
Mayo
17:05 Alberto Ha dicho:
Muy buena explicación, saludos
[...] Explicación visual de los SQL Join [...]
Mayo
6:36 Gaston Ha dicho:
Hola: Quisiera saber si hay alguna instrucción JOIN que pueda servirme para lo siguiente:
tengo 2 tablas diferentes, una con 10 campos y otra con 15 y entre ellas hay 4 campos equivalentes (contienen datos del mismo tipo), pero con distintos nombres. Necesito obtener como resultado de la cosulta, una tabla que muestre sólo los campos equivalentes 4 (no 8), que despliegue los registros de ambas tablas.
Espero haberme explicado y que me puedas ayudar.
Gracias de antemano,
Gaston
Junio
12:16 Saiyine Ha dicho:
¿Se pueden unir columnas?
Es decir, tengo tabla1 con la columna idtabla1 de enteros, y la tabla2 con la columna idtabla2:
tabla1[idtabla1,saludos] = [1,"hola"],[2,"adios"]
tabla2[idtabla2,vehiculos] = [17,"coche"],[84,"moto"]
¿Se puede hacer una query que devuelva [1,2,17,84] ???
Gracias.
Junio
17:29 EMMANUEL Ha dicho:
EXCELENTE!!!
Ejemplo bastante claro y facil de entender, mil gracias
Junio
18:33 pikamorfo Ha dicho:
mu grande la explicacion !!!!!! :DDDD espero que me sirva en el examen!
Junio
23:06 Alex Barros Ha dicho:
Gracias. Y mucha suerte con el examen, pikamorfo
Junio
17:39 Doris pinto Ha dicho:
Hola necesito ayuda…
Necesito hacer una busqueda de tablas diferentes y tengo este codigo pero no funciona.
asignar.ci,asignar.nrotiket,detalleasig.serial,usuarios.nombre,tipoequipos.descripcionseriales.codestado FROM asignar LEFT JOIN usuarios on asignar.ci=usuarios.ci LEFT JOIN seriales on seriales.serial=detallesasig.serial LEFT JOIN equipos on equipos.codequipo=seriales.codequipo LEFT JOIN tipoequipos on tipoequipos.codtipo = equipos.codtipo = seriales.codestado WHERE asignar.ci=’” & RESPUESTA & “‘”
Nota:la ci la busco en la tabla asignar pero el nombre q corresponde a esa ci esta en la tabla usuario;busco el serial de la tabla detallesasig pero sus caracteristicas ya estan guardadas en la tabla seriales que esta intersectada con la tabla equipos a traves del codequipo
Junio
20:59 Alex Barros Ha dicho:
No estoy seguro porque no he indagado en JOINS anidados, un día de estos lo probaré, pero no tengo tiempo por ahora. Pero sospecho que puedas necesitar paréntesis, o otra sintaxis, para anidar JOINS.
Pero insisto, no estoy seguro.
Junio
20:53 Damasco Dantesco Ha dicho:
Buenas tardes,
Tengo que hacer un trabajo en la uni y ya tengo la estructura de datos
como podran observar soy novato en mysql. y necesito generar un qry q explico a continuacion:
::::::: Tablas::::::::
|grados| |secciones|
*idgrado *idseccion
*grado *idgrado
*seccion
*cupos
*disponible
este sentecia –> SELECT grado,seccion,cupos-matricula FROM grados LEFT OUTER JOIN secciones ON grados.idgrado=secciones.idgrado;
Genera el siguiente resultado:
+——————-+———+—————–+
| grado | seccion | cupos-matricula |
+——————-+———+—————–+
| Prescolar | A | 2 |
| Prescolar | B | 15 |
| 1er Grado | A | 15 |
| 1er Grado | B | 15 |
Pero ahora quiero sumar el campo cupos-matricula
para que me genere algo asi:
+——————-++—————–+
| grado |cupos-matricula |
+——————-++—————–+
| Prescolar | 17|
| 1er Grado | 30|
Pueden ayudarme???
Julio
14:40 el_shanky Ha dicho:
La verdad que de 10 lo tuyo!! muchas gracias x fin lo entendí gracias a vos!!!
Julio
17:48 Liliet Ha dicho:
Muchas gracias por la traducción y es verdad que es muy útil esta explicación grafica de los join en sql, llevaba media hora buscando como obtener las filas de una tabla que no las tuviera la segunda y no encontraba la solución. De verás está muy bueno esto.
Para Damasco creo que le serviría “SELECT grado, sum(cupos-matricula) FROM grados LEFT OUTER JOIN secciones ON grados.idgrado=secciones.idgrado GROUP BY grados”.
Julio
18:22 Alex Barros Ha dicho:
Muchas gracias Liliet por tu aportación!
[...] Re: Inner Join jajaja tipo billiquen
pasate por ste link [...]
Agosto
5:17 El Zorro Ha dicho:
Muy buena la explicación, por demás didáctica.
Gracias por haberte tomado el tiempo de hacer esta explicación tan completa del JOIN y todas sus variantes.
Agosto
16:33 Yiller Ha dicho:
Que explicacion mas interesante,
necesito que me indiques algo:
Estoy programando en java un analizador sintactico de sentencias SQL. como mejora del software de la compañia.
y para ello necesito conocer las estructuras de las sentencias SELECT.
estoy trabajando con JAVA CC. pero necesito realizar un analizador de que verifique antes de pasarlo por el JAVACC sintactico.
ahora.
necesito saber si tienes la estructura de los joins , la mas completa posible por ejemplo.
la del select es asi.
SELECT [ALL|DISTINCT]
{ * | expr_1 [AS c_alias_1] [, ...
[, expr_k [AS c_alias_k]]]}
FROM table_name_1 [t_alias_1]
[, ... [, table_name_n [t_alias_n]]]
[WHERE condition]
[GROUP BY name_of_attr_i
[,... [, name_of_attr_j]]
[HAVING condition]]
[{UNION [ALL] | INTERSECT | EXCEPT} SELECT …]
[ORDER BY name_of_attr_i [ASC|DESC]
[, ... [, name_of_attr_j [ASC|DESC]]]];
pero la de los joins no la encuentro, y necesito conocer la complejidad de los joins para realizar este analisis, pues en este momento lo estoy realizando con ejemplos de sentencias con joins muy complejos.
de antemano muchas gracias.
Agosto
17:05 Alex Barros Ha dicho:
En la página de documentación oficial de MySQL tienes una referencia a la sintaxis completa del Join:
http://dev.mysql.com/doc/refman/5.0/en/join.html (Inglés)
http://dev.mysql.com/doc/refman/5.0/es/join.html (Español)
Suerte con tu trabajo
Agosto
20:13 Jaguar Ha dicho:
Ayuda por favor ya entendi el filtro de los join, el problema que tengo. tengo dos regillas es decir AxMSHFlexGrid1 y AxMSHFlexGrid2, en el uno es tabla1 y el 2 tabla2 si me explico.
quiero filtrar en la regilla 2 unicamente los datos en comun de la regilla 1, es decir regilla 1 es padre y regilla 2 es hija.
en visual net y utilizo MYSQL, GRACIAS….