Revista Tecnología

#Curso SQL: Cruzando tablas, JOIN

Publicado el 09 mayo 2018 por Instintobinario

Continúo con el cuarto capítulo del curso de SQL. Hasta ahora hemos ido haciendo sentencias más y más complejas, pero afectando a una sola tabla de datos. En éste capítulo vamos a ver cómo podemos cruzar dos o más tablas. Existen dos forma de hacer ésto: mediante la cláusula WHERE, o mediante una nueva cláusula: JOIN. Para ello vamos a tener las siguientes tablas con sus campos:

Tablas SQL

En éste diagrama puedes ver una tabla ‘Cliente’. El cliente tiene un identificador de dirección (‘Id_Direccion’) que coincidirá con un registro de la tabla ‘Direccion’. A su vez, la Dirección tiene asignada una Localidad mediante el campo ‘Id_Localidad’.

Lo que vamos a ver es cómo hacer cosas del estilo: “Seleccionar todos los clientes de Madrid” en éste tipo de escenarios. A parte de otras posibilidades que tenemos mezclando tablas y consultas.

Uso de WHERE para cruzar tablas.

Como te habrás dado cuenta, no se puede seleccionar los clientes de una localidad con tanta facilidad como hasta ahora. La típica consulta que se hacía hasta ahora era:

SELECT * FROM Clientes WHERE Localidad = "Madrid";

Pero ahora ya no hay una columna ‘Localidad’ en la tabla Cliente. El cliente tiene un identificador de Dirección y ésta tiene un identificador de Localidad. Así que hay que llegar a la tabla Localidad para poder preguntar por el nombre de la misma.

Antes de ello vamos a ver varias posibilidades que tenemos con dos tablas. Para comenzar puedes obtener todos los datos de las tablas ‘Cliente’ y ‘Direccion’, sin ningún tipo de filtro:

SELECT * FROM Cliente, Direccion;

En la base de datos de pruebas existen 4 clientes y 5 direcciones, así que esta sentencia los cruzará todos. Obtendrás 20 líneas de resultado: cada una de las 5 direcciones asignada a cada cliente.

Lo anterior no es muy útil. Más bien interesaría tener 4 líneas, con los datos de la dirección que de verdad se corresponde al cliente. Para esto hay que usar el WHERE de la siguiente forma:

SELECT * FROM Cliente c, Direccion d
WHERE c.Id_Direccion = d. Id_Direccion;

Aquí pone como condición que los ‘Id_Direccion’ tienen que coincidir. Por este motivo, devolverá los datos de cada cliente y de la dirección correspondiente al mismo. Observa que he puesto un alias a cada tabla. Esto es importante, pues el campo ‘Id_Direccion’ se llama igual en ambas, y no se sabría cual campo es. Si ejecutas la consulta sin alias, habrá un error por dicha ambigüedad.

Sin salir del ámbito de éstas dos tablas, puedes añadir otros filtros. Por ejemplo, los clientes de una localidad en concreto:

SELECT * FROM Cliente c, Direccion d
WHERE c.Id_Direccion = d. Id_Direccion AND d.Id_Localidad = 4;

Observa que es igual que la anterior, pero filtrando que solo muestra los datos del Cliente y la Dirección si están en la Localidad 4. También puedes seleccionar los clientes de 2 localidades:

SELECT * FROM Cliente c, Direccion d
WHERE c.Id_Direccion = d. Id_Direccion AND (d.Id_Localidad = 4 OR d.Id_Localidad = 1);

Ojo con los paréntesis, son muy importantes en esta sentencia, de lo contrario el resultado es: los clientes cuya dirección sea 4 y las direcciones con localidad 1. De forma que nos devolvería el resultado de la anterior más todos los clientes asignados las direcciones de localidad 1.

Si quieres, puedes limitar las columnas que se muestran:

SELECT c.Nombre,c.Apellidos,c.Telefono,d.Calle,d.Numero,d.Piso
FROM Cliente c, Direccion d
WHERE c.Id_Direccion = d. Id_Direccion AND d.Id_Localidad = 4;

Uso y tipos de JOIN

La cláusula JOIN sirve para combinar columnas de varias tablas. Aunque se puede hacer con la cláusula WHERE, como hemos visto, esta es más correcta. En términos de eficiencia no hay diferencia, sin embargo se considera más correcto desde el punto de vista de la sintaxis.

La cláusula JOIN, combina columnas de dos o más tablas de datos. Esta cláusula puede aparecer en el apartado del FROM o en el WHERE. Las columnas a comparar en el JOIN, al igual que pasa con el WHERE, no necesitan estar entre las del SELECT.

Existen varios tipos de JOIN. Vamos a describir cómo funciona cada uno:

  • (INNER) JOIN: Devuelve los registros que coinciden en ambas tablas
  • LEFT (OUTER) JOIN: Devuelve los registros de la tabla izquierda y los registros coincidentes de la tabla derecha.
  • RIGHT (OUTER) JOIN: Devuelve los registros de la tabla derecha y los registros coincidentes de la tabla izquierda.
  • FULL (OUTER) JOIN: Devuelve todos los registros de ambas tablas cuando hay una coincidencia, ya sea en la tabla derecha o la izquierda.

tipos de join

INNER JOIN

Como he dicho, el resultado de éste JOIN es los resultados que tienen valores coincidentes en ambas columnas. Por ejemplo, para seleccionar los clientes y su dirección puedes hacer lo siguiente:

SELECT *
FROM Cliente c INNER JOIN Direccion d
ON c.Id_Direccion = d.Id_Direccion;

Para hacer un JOIN de tres tablas, tienes que realizar primero el de dos y después la tercera. Vamos a mostrar un ejemplo con el nombre del cliente y su localidad:

SELECT c.Nombre, l.Localidad
FROM ((Cliente c
    INNER JOIN Direccion d ON c.Id_Direccion = d.Id_Direccion)
    INNER JOIN Localidad l ON d.Id_Localidad = l.Id_Localidad);

Puede parecer muy complicado, pero no lo es tanto. Observa que los paréntesis hacen que se tenga en cuenta en primer el JOIN de la tabla Cliente con la tabla Dirección. Una vez hecho éste JOIN, se va al segundo nivel del paréntesis y se compara el resultado con la tabla Localidad. De ésta forma, hacemos JOIN entre Cliente y Localidad sin estar ambas conectadas.

Éste concepto es bastante importante, porque empezamos a ver la mecánica de las subconsultas. Explicaremos que son las subconsultas y para qué sirven más adelante.

LEFT JOIN

Es un tipo de JOIN similar al anterior. La diferencia reside en que, éste, devuelve todas las tuplas de la primera tabla, más las coincidentes en la segunda.

Lo mejor es verlo con un ejemplo seleccionando las direcciones con la localidad que les corresponde:

SELECT *
FROM Localidad l LEFT JOIN Direccion d
ON d.Id_Localidad = l.Id_Localidad;

En la consulta anterior, si sustituyes LEFT JOIN por INNER JOIN puedes ver que hay un resultado menos. Esto es porque la localidad 2, no tiene asignada ninguna dirección. Lo que puedes observar es que todos los campos de dirección correspondientes a dicha localidad son NULL.

La utilidad de ésta consulta consiste en listar todos los registros de una tabla con los que corresponden de una segunda tabla sin obviar ninguno de la primera.

RIGHT JOIN

Es el inverso que el anterior. Toma todos los registros de la segunda tabla mencionada y los coincidentes de la primera. Puedes hacer la prueba con la misma consulta que el apartado anterior

SELECT *
FROM Localidad l RIGHT JOIN Direccion d
ON d.Id_Localidad = l.Id_Localidad;

FULL OUTER JOIN

Esta consulta devuelve todos los campos de la primera tabla más todos los campos de la segunda. No importa si tienen registros coincidentes en la otra, simplemente devuelve todos los campos y los asigna si coinciden. Puedes ver un ejemplo en la siguiente consulta:

SELECT *
FROM Localidad l FULL OUTER JOIN Direccion d
ON d.Id_Localidad = l.Id_Localidad;

SELF JOIN

El SELF JOIN es un tipo especial de JOIN en el cual una tabla se combina consigo misma. Podemos ver un ejemplo con la tabla Localidad:

SELECT a.Localidad AS Localidad_A, b.Localidad AS Localidad_B
FROM Localidad a, Localidad b
WHERE a.Id_Localidad != b.Id_Localidad;

Como vemos no existe sintaxis propia. Lo que hacemos es utilizar la cláusula WHERE para hacer un cruce entre ambas tablas que nos combine un resultado con todos los demás de la propia tabla.

Unión de consultas

Imagina ahora que tienes una tabla más llamada Trabajadores. En éste caso queremos saber el nombre y el teléfono de todas las  personas relacionadas con la empresa. Debes seleccionar todos los nombres de clientes y los de trabajadores. El problema es que al hacerlo con un único SELECT, no obtienes los nombres de una tabla y otra en diferente fila.

Por ello tenemos la cláusula UNION, que permite tener dos consultas independientes y juntar los resultados:

SELECT c.Nombre FROM Cliente c
UNION
SELECT t.Nombre FROM Trabajador t;

De ésta forma obtienes todos los nombres de clientes y trabajadores. Puedes probar la diferencia haciendo un JOIN si no entiendes la utilidad de esto.

Pero la cláusula UNION hace un DISTINCT por defecto, por lo que si quieres listar todos los resultados incluso siendo iguales podemos usar UNION ALL:

SELECT c.Nombre FROM Cliente c
UNION ALL
SELECT t.Nombre FROM Trabajador t

Probando los ejemplos de código de éste artículo

Para probar los ejemplos de código y experimentar con las consultas puedes ir al siguiente enlace:

http://sqlfiddle.com/#!4/348e05/

En caso de estar en blanco el cuadro de texto sobre el botón “Build Schema”, pegar el siguiente código y pulsar sobre dicho botón.

CREATE TABLE Cliente (
Id_Cliente number(6),
Dni varchar(9),
Nombre varchar(255),
Apellidos varchar(255),
Edad number(3),
Telefono varchar (255),
Id_Direccion number(6)
);
INSERT INTO Cliente(Id_Cliente, Dni, Nombre, Apellidos, Edad, Telefono, Id_Direccion)
VALUES (1,'11111111A', 'Juan', 'Pérez Mora', 19, '111111111', 1);
INSERT INTO Cliente(Id_Cliente, Dni, Nombre, Apellidos, Edad, Telefono, Id_Direccion)
VALUES (2,'22222222B', 'Isabel', 'Jimeno Hoyo', 23, '22222222', 2);
INSERT INTO Cliente(Id_Cliente, Dni, Nombre, Apellidos, Edad, Telefono, Id_Direccion)
VALUES (3,'33333333C', 'Patricia', 'Rodriguez Denia', null, '333333333', 2);
INSERT INTO Cliente(Id_Cliente, Dni, Nombre, Apellidos, Edad, Telefono, Id_Direccion)
VALUES (4,'44444444D', 'Mario', 'Fernandez Bosque', 31, '444444444', 4);

CREATE TABLE Trabajador (
Id_Trabajador number(6),
Nombre varchar(255),
Apellidos varchar(255),
Telefono varchar (255)
);
INSERT INTO Trabajador(Id_Trabajador, Nombre, Apellidos, Telefono)
VALUES (1, 'Pedro', 'Gomez Gomez', '876543289');
INSERT INTO Trabajador(Id_Trabajador, Nombre, Apellidos, Telefono)
VALUES (2,'Juan', 'Valero Roi', '965223587');
INSERT INTO Trabajador(Id_Trabajador, Nombre, Apellidos, Telefono)
VALUES (3,'Julia', 'Piquet dell Vals', '933547899');

CREATE TABLE Direccion (
Id_Direccion number(6),
Calle varchar(100),
Numero number(4),
Piso varchar(5),
CodPostal varchar (5),
Id_Localidad number(6)
);
INSERT INTO Direccion (Id_Direccion, Calle, Numero, Piso, CodPostal, Id_Localidad)
VALUES(1, 'Paseo de la Castellana', 12, '3A', '12345', 1);
INSERT INTO Direccion (Id_Direccion, Calle, Numero, Piso, CodPostal, Id_Localidad)
VALUES(2, 'Paseo Colon', 27, '1F', '98765', 3);
INSERT INTO Direccion (Id_Direccion, Calle, Numero, Piso, CodPostal, Id_Localidad)
VALUES(3, 'Calle Beris', 4, '3D', '14236', 3);
INSERT INTO Direccion (Id_Direccion, Calle, Numero, Piso, CodPostal, Id_Localidad)
VALUES(4, 'San Fernando', 34, '3 Izq', '34123', 4);
INSERT INTO Direccion (Id_Direccion, Calle, Numero, Piso, CodPostal)
VALUES(5, 'Calla Jovellanos',1, '2E', '37890' );

CREATE TABLE Localidad (
Id_Localidad number(6),
Localidad varchar(100)
);
INSERT INTO Localidad (Id_Localidad, Localidad)
VALUES(1, 'Madrid');
INSERT INTO Localidad (Id_Localidad, Localidad)
VALUES(2, 'Barcelona');
INSERT INTO Localidad (Id_Localidad, Localidad)
VALUES(3, 'Sevilla');
INSERT INTO Localidad (Id_Localidad, Localidad)
VALUES(4, 'Santander');

En el recuadro de la derecha se escriben las consultas y se ejecutan pulsando sobre “Run SQL”. Y en la parte inferior se mostrarán los resultados de la ejecución de la consulta.

Artículos anteriores del curso de SQL:

Capítulo 1: Introducción al SQL y las bases de datos relacionales

Capítulo 2: La cláusula WHERE, filtrando datos

Capítulo 3: SELECT avanzado e INSERT INTO

El artículo #Curso SQL: Cruzando tablas, JOIN apareció por primera vez en Instinto Binario.


Volver a la Portada de Logo Paperblog

Revista