de varias tablas. Aunque mediante las subconsultas se ha conseguido realizar
consultas de este tipo, aquí se verá que en ocasiones, es posible escribir consul-
tas equivalentes que no hacen uso de subconsultas y que se ejecutan de modo
más eficiente. El operador que se introduce es la concatenación (JOIN).
4.8.1. La concatenación: JOIN
La concatenación es una de las operaciones más útiles del lenguaje SQL.
Esta operación permite combinar información de varias tablas sin necesidad
de utilizar subconsultas para ello.
La concatenación natural (NATURAL JOIN) de dos tablas R y S obtiene como
resultado una tabla cuyas filas son todas las filas de R concatenadas con todas
las filas de S que en las columnas que se llaman igual tienen los mismos valores.
Las columnas por las que se hace la concatenación aparecen una sola vez en el
resultado.
La siguiente sentencia hace una concatenación natural de las tablas FACTURAS
y CLIENTES. Ambas tablas tienen una columna con el mismo nombre, codcli,
siendo FACTURAS.codcli una clave ajena a CLIENTES.codcli (clave primaria).
SELECT *
FROM
facturas NATURAL JOIN clientes;
Según la definición de la operación NATURAL JOIN, el resultado tendrá las si-
guientes columnas: codfac, fecha, codven, iva, dto, codcli, nombre,
direccion, codpostal, codpue. En el resultado de la concatenación cada
fila representa una factura que cuenta con sus datos (la cabecera) y los datos
del cliente al que pertenece. Si alguna factura tiene codcli nulo, no aparece
en el resultado de la concatenación puesto que no hay ningún cliente con el
que pueda concatenarse.
Cambiando el contenido de la cláusula SELECT, cambia el resultado de la
consulta. Por ejemplo:
SELECT DISTINCT codcli, nombre, direccion, codpostal, codpue
FROM
facturas NATURAL JOIN clientes;
Esta sentencia muestra los datos de los clientes que tienen facturas. Puesto
que se ha hecho la concatenación, si hay clientes que no tienen facturas, no se
obtienen en el resultado ya que no tienen ninguna factura con la que concate-
narse.
A continuación, se desea modificar la sentencia anterior para que se obtenga
también el nombre de la población del cliente. Se puede pensar que el nombre
de la población se puede mostrar tras hacer una concatenación natural con la
tabla PUEBLOS. El objetivo es concatenar cada cliente con su población a través
de la clave ajena codpue. Sin embargo, la concatenación natural no es útil en
este caso porque las tablas PUEBLOS y CLIENTES tienen también otra columna
que se llama igual: la columna nombre. CLIENTES.nombre contiene el nombre
de cada cliente y PUEBLOS.nombre contiene el nombre de cada pueblo. Ambos
nombres no significan lo mismo, por lo que la concatenación natural a través
de ellas no permite obtener el resultado que se desea.
¿Qué se obtendrá como resultado al ejecutar la siguiente sentencia?
SELECT *
FROM
facturas NATURAL JOIN clientes NATURAL JOIN pueblos;
Se obtendrán las facturas de los clientes cuyo nombre completo coincide con
el nombre de su pueblo, cosa poco probable que suceda.
Cuando se quiere concatenar varias tablas que tienen varios nombres de
columnas en común y no todos han de utilizarse para realizar la concatenación,
se puede disponer de la operación INNER JOIN, que permite especificar las
columnas sobre las que hacer la operación mediante la cláusula USING.
SELECT DISTINCT codcli, clientes.nombre, codpue,
pueblos.nombre
FROM
facturas INNER JOIN clientes USING (codcli)
INNER JOIN pueblos
USING (codpue);
Nótese que, en la consulta anterior, algunas columnas van precedidas por el
nombre de la tabla a la que pertenecen. Esto es necesario cuando hay columnas
que se llaman igual en el resultado: se especifica el nombre de la tabla para
evitar ambigüedades. Esto sucede cuando las tablas que se concatenan tienen
nombres de columnas en común y la concatenación no se hace a través de
ellas, como ha sucedido en el ejemplo con las columnas CLIENTES.nombre y
PUEBLOS.nombre. En el resultado hay dos columnas nombre y, sin embargo, una
sola columna codcli y una sola columna codpue (estas dos últimas aparecen
sólo una vez porque las concatenaciones se han hecho a través de ellas).
En realidad, en SQL el nombre de cada columna está formado por el
nombre de su tabla, un punto y el nombre de la columna (FACTURAS.iva,
CLIENTES.nombre). Por comodidad, cuando no hay ambigüedad al referirse a
una columna, se permite omitir el nombre de la tabla a la que pertenece, que
es lo que se había estado haciendo hasta ahora en este capítulo.
Cuando las columnas por las que se hace la concatenación no se llaman igual
en las dos tablas, se utiliza ON para especificar la condición de concatenación
de ambas columnas, tal y como se ve en el siguiente ejemplo. En él se introduce
también el uso de alias para las tablas, lo que permite no tener que escribir el
nombre completo para referirse a sus columnas:
SELECT v.codven, v.nombre AS vendedor,
j.codven AS codjefe, j.nombre AS jefe
FROM
vendedores AS v INNER JOIN vendedores AS j
ON (v.codjefe=j.codven);
Esta sentencia obtiene el código y el nombre de cada vendedor, junto al código
y el nombre del vendedor que es su jefe.
Es aconsejable utilizar siempre alias para las tablas cuando se hagan con-
sultas multitabla, y utilizarlos para especificar todas las columnas, aunque no
haya ambigüedad. Es una cuestión de estilo.
Ya que este tipo de concatenación (INNER JOIN) es el más habitual, se
permite omitir la palabra INNER al especificarlo, tal y como se muestra en el
siguiente ejemplo:
SELECT DISTINCT c.codcli, c.nombre, c.codpue, p.nombre
FROM
facturas AS f JOIN clientes AS c USING (codcli)
JOIN pueblos
AS p USING (codpue)
WHERE
COALESCE(f.iva,0) = 18
AND
COALESCE(f.dto,0) = 0;
Aunque la operación de NATURAL JOIN es la que originalmente se definió en el
modelo relacional, su uso en SQL no es aconsejable puesto que la creación de
nuevas columnas en tablas de la base de datos puede dar lugar a errores en las
sentencias que las consultan, si estas nuevas columnas tienen el mismo nombre
que otras columnas de otras tablas con las que se han de concatenar.
Es recomendable, al construir las concatenaciones, especificar las tablas
en el mismo orden en el que aparecen en el diagrama referencial (figura 4.2).
De este modo será más fácil depurar las sentencias, así como identificar qué
hace cada una: en el resultado de una consulta escrita de este modo, cada
fila representará lo mismo que representa cada fila de la primera tabla que
aparezca en la cláusula FROM y en este resultado habrá, como mucho, tantas
filas como filas hay en dicha tabla.
Figura 4.2: Diagrama referencial de la base de datos.
Hay un aspecto que todavía no se ha tenido en cuenta: los nulos en las
columnas a través de las cuales se realizan las concatenaciones. Por ejemplo,
si se quiere obtener un listado con las facturas del mes de diciembre del año
pasado, donde aparezcan los nombres del cliente y del vendedor, se puede
escribir la siguiente consulta:
De todas las facturas que hay en dicho mes, aparecen en el resultado sólo algu-
nas. Esto es debido a que las columnas FACTURAS.codcli y FACTURAS.codven
aceptan nulos. Las facturas con algún nulo en alguna de estas columnas son
las que no aparecen en el resultado.
Para evitar estos problemas, se puede hacer uso de la operación OUTER
JOIN con tres variantes: LEFT, RIGHT, FULL. Con LEFT/RIGHT OUTER JOIN,
en el resultado se muestran todas las filas de la tabla de la izquierda/derecha;
aquellas que no tienen nulos en la columna de concatenación, se concatenan
con las filas de la otra tabla mediante INNER JOIN. Las filas de la tabla de la
izquierda/derecha que tienen nulos en la columna de concatenación aparecen
en el resultado concatenadas con una fila de nulos. Con FULL OUTER JOIN se
hacen ambas operaciones: LEFT OUTER JOIN y RIGHT OUTER JOIN.
Teniendo en cuenta que, tanto FACTURAS.codcli como FACTURAS.codven
aceptan nulos, el modo correcto de realizar la consulta en este último ejemplo
será:
Esta sentencia obtiene un listado con todos los clientes de la tabla CLIENTES
y el número de facturas que cada uno tiene. Si algún cliente no tiene ninguna
factura (no es referenciado por ninguna fila de la tabla de FACTURAS), también
aparecerá en el resultado y la cuenta del número de facturas será cero. Nó-
tese que la cuenta del número de facturas se hace sobre la clave primaria de
FACTURAS (COUNT(f.codfac)) ya que los clientes sin facturas tienen un nulo
en esta columna tras la concatenación y las funciones de columna ignoran los
nulos, por lo que la cuenta será cero.
4.8.2. Sintaxis original de la concatenación
En versiones anteriores del estándar de SQL la concatenación no se rea-
lizaba mediante JOIN, ya que esta operación no estaba implementada direc-
tamente. En el lenguaje teórico en el que se basa SQL, el álgebra relacional,
la operación de concatenación sí existe, pero ya que no es una operación pri-
mitiva, no fue implementada en SQL en un principio. No es una operación
primitiva porque se puede llevar a cabo mediante la combinación de otras dos
operaciones: el producto cartesiano y la restricción. La restricción se lleva a
cabo mediante la cláusula WHERE, que ya es conocida. El producto cartesiano
se lleva a cabo separando las tablas involucradas por una coma en la cláusula
FROM, tal y como se muestra a continuación:
No hay que olvidar que la concatenación que se acaba de mostrar utiliza
una sintaxis que ha quedado obsoleta en el estándar de SQL. La sintaxis del
estándar actual es más aconsejable porque permite identificar más claramente
qué son restricciones (aparecerán en el WHERE) y qué son condiciones de con-
catenación (aparecerán en el FROM con la palabra clave JOIN). Sin embargo, es
importante conocer esta sintaxis porque todavía es muy habitual su uso.
4.8.3. Ejemplos
Ejemplo 4.11 Obtener los datos completos del cliente al que pertenece la fac-
tura 5886.
Una versión que utiliza subconsultas es la siguiente:
4.8.4. Algunas cuestiones importantes
A continuación se plantean algunas cuestiones que es importante tener en
cuenta cuando se realizan concatenaciones:
Al hacer un NATURAL JOIN es importante fijarse muy bien en los nom-
bres de las columnas de las tablas que participan en la operación. Como
se sabe, mediante este operador se concatenan las filas de ambas tablas
que en los atributos que tienen el mismo nombre tienen también los mis-
mos valores. Por ejemplo, un NATURAL JOIN entre las tablas PUEBLOS
y CLIENTES se realizará a través de las columnas codpue y nombre. El
resultado, si contiene alguna fila, serán los datos de clientes que tienen
como nombre el mismo nombre de su población. Si nuestro objetivo era
realizar la concatenación a través de codpue podemos decir que el uso
del NATURAL JOIN nos ha jugado una mala pasada. Concatenar filas por
columnas no deseadas implica tener en cuenta más restricciones, con lo
que los resultados obtenidos pueden no ser correctos. Es más aconsejable
utilizar INNER JOIN, ya que pueden evitarse estos problemas al especifi-
carse de manera explícita las columnas de concatenación.
En la vida de una base de datos puede ocurrir que a una tabla se le deban
añadir nuevas columnas para que pueda almacenar más información. Si
esta tabla se ha utilizado para realizar algún NATURAL JOIN en alguna
de las consultas de los programas de aplicación, hay que ser cuidadosos
al escoger el nombre ya que si una nueva columna se llama igual que
otra columna de la otra tabla participante en dicha operación, la con-
catenación que se hará ya no será la misma. Es posible evitar este tipo
de problemas utilizando siempre INNER JOIN ya que éste requiere que se
especifiquen las columnas por las que realizar la concatenación, y aun-
que se añadan nuevas columnas a las tablas, no cambiará la operación
realizada por más que haya nuevas coincidencias de nombres en ambas
tablas.
Ordenar las tablas en el FROM tal y como aparecen en los diagramas
referenciales ayuda a tener un mayor control de la consulta en todo mo-
mento: es posible saber si se ha olvidado incluir alguna tabla intermedia
y es posible saber qué representa cada fila del resultado de la concatena-
ción de todas las tablas implicadas. Además, será más fácil decidir qué
incluir en la función COUNT() cuando sea necesaria, y también será más
fácil determinar si en la proyección final (SELECT) es necesario el uso de
DISTINCT.
4.9. Operadores de conjuntos






No hay comentarios:
Publicar un comentario