En el artículo anterior vimos el uso más básico de la clausula SELECT de SQL, sin embargo apenas arañamos la superficie en cuanto a la cantidad de tipos de selecciones que podemos hacer.
En este segundo artículo vamos a explorar las posibilidades de los comandos de conjunto que nos permiten realizar operaciones sobre las filas que devolvemos (como devolver una suma, o el número de filas) así como el uso de la orden GROUP BY que está íntimamente relacionado con lo anterior. Por otro lado exploraremos las intersecciones que se pueden realizar entre consultas (JOIN).
Las funciones de conjunto (aggregate functions en inglés) son una serie de funciones aplicables a cada columna devuelta por un SELECT que nos permiten realizar determinadas acciones sobre ellas. Ejemplo de dichas funciones son obtener la media de una columna, obtener el número de columnas devueltas u obtener el valor máximo de entre los resultados de una columna.
La sintaxis genérica podríamos definirla así
Como vemos el uso de las funciones de conjunto va asociado al uso de la palabra GROUP BY. Más adelante veremos algunos usos de esta palabra reservada, de momento vamos a ver algunos ejemplos:
Nos devolverá el número de filas que hay en la tabla Personas, es completamente equivalente a, por ejemplo
pero puede devolver un número distinto a
esto se debe a que si especificamos una columna concreta nos contará solo aquellas filas que no tengan un valor null en dicha columna por lo que si existe una persona sin apellidos en nuestra tabla, dicha persona no contará en la query (el caso de Persona_Id que es clave y por tanto no nula el resultado es equivalente).
El caso concreto de "Count(*)" que nos devuelve la cantidad de registros de una tabla suele utilizarse para mostrar el número total de personas o para, por ejemplo, organizar una vista paginada de dicha tabla. También podemos, por supuesto contar solo aquellos registros que cumplen una condición
obtendría el número de personas de nuestra base de datos cuya edad es menor que 30 años.
o, por ejemplo, la media de edad de aquellas personas que ganan más de 30.000 € al año
cuyo resultado puede ser algo como
| Edad |
| 31 |
nos permitirá saber quien es la persona de menor edad con un salario superior a los 30.000 € al año.
Esos son los principales y más usados, existen algunos otros como First y Last que obtienen el primer y el último elemento respectivamente, y existen también funciones de grupo propias de cada proveedor de base de datos que, por tanto, no son estándares de SQL.
El comando Group by está estrechamente relacionado con las funciones de agrupación y nos permite, como su nombre, en inglés, indica, agrupar los resultados. Hasta ahora los ejemplos que hemos visto se han limitado a realizar operaciones con una columna o con el total de filas. Así obteníamos la edad media o el número de filas de una determinada tabla. Sin embargo en ocasiones podemos querer aplicar la función de agrupación por "grupos". Por ejemplo, supongamos que tenemos una tabla con el estado de los productos disponibles que pueden estar en disponibles, pedidos, agotados, o decatalogados. Supongamos que lo que deseamos es saber cuantos productos de cada clase tenemos, si utilizamos la siguiente consulta
obtendremos un error indicándonos que "Estado" debe estar dentro de una cláusula Group By. Esto es así porque necesitamos indicarle al motor de SQL que queremos que agrupe los resultados teniendo en cuenta el valor de esta columna, es decir, que primero determine que filas tienen el mismo valor para la columna estado y luego, sobre dichas filas, por separado, aplique la función de agrupación. De esta forma tendríamos:
que nos proporcionaría una salida de este estilo
| Total | Estado |
| 1395 | Disponibe |
| 135 | Pedido |
| 13 | Agotado |
| 7 | Descatalogado |
Podemos aplicar el comando Group By a más de una columna para agrupar por varias columnas que sean iguales, pero evidentemente si lo hacemos, nos agrupará los resultados por aquellos resultados que tengan el contenido de ambas columnas exactamente igual, por lo que si coinciden en tan solo una columna pero la otra difiere, se mostrarán como resultados distintos. Por ejemplo:
nos devolverá la edad media de todos los estudiantes que vivan en la misma ciudad y atiendan a la misma universidad teniendo en cuenta tan solo aquellos estudiantes menores de 30 años.
| Ciudad | Universidad | Edad |
| Madrid | Universidad Politécnica | 23 |
| Madrid | Universidad Autónoma | 24 |
| Toledo | Universidad Politécnica | 26 |
| Barcelona | Universidad de Barcelona | 21 |
| Barcelona | Universidad de Tarragona | 24 |
Para acabar de complementar las funciones de agrupación tenemos el operador Having. La función de Having es, a las funciones de agrupación, lo que el Where es al comando select. De esta forma, el having nos permitirá realizar filtrados sobre los resultados obtenidos en función del valor de la función de agrupación, por ejemplo, volviendo a la consulta anterior podemos refinarla y obtener tan solo aquellos resultados cuya edad media sea superior a 23 años.
El término Join (juntar en inglés) se utiliza en SQL para definir una operación que unifica datos de dos (o más) tablas en base a una o varias comparaciones entre las columnas de las tablas. Podemos definir dos tipos de joins, los Inner Join y los Outer Join.
Un inner join mezcla los datos de dos tablas realizando el producto cartesiano entre ellas en función de lo indicado en la clausula WHERE de estar esta presente. Si no indicamos ningún comando de comparación en la clausula WHERE el resultado será el producto cartesiano entre las dos tablas. Vamos a ver un ejemplo (muy sencillo porque el producto cartesiano produce resultados muy abultados):
|
|
| Nombre | Marca |
| Benito | Ford |
| Benito | Opel |
| Benito | Nissan |
| Benito | Nissan |
| Pepe | Ford |
| Pepe | Opel |
| Pepe | Nissan |
| Pepe | Nissan |
| Lucía | Ford |
| Lucía | Opel |
| Lucía | Nissan |
| Lucía | Nissan |
| Andrés | Ford |
| Andrés | Opel |
| Andrés | Nissan |
| Andrés | Nissan |
Como vemos, al hacer el Join obtenemos el producto cartesiano de las dos tablas (es decir, cada fila de la tabla A se combina con todas las de la tabla B por lo que al final acabamos teniendo n*m filas, donde n es el número de filas de la tabla A y m el número de filas de la tabla b). Podemos ver como funciona el Join observando que la marca Nissan aparece repetida, puesto que hay dos entradas de Nissan en la tabla Coches estas dos entradas se combinan con las de la tabla personas y más tarde se filtran.
Aunque en unos pocos casos contados este ejemplo pudiera llegar a ser útil, en general el uso más habitual de un Join es el de mezclar dos tablas para obtener resultados que pertenezcan a ambas tablas. Esto, en realidad, ya lo vimos en el artículo anterior, aunque no le dimos nombre, voy a recuperar ese ejemplo:
Que, como vimos, obtiene todas las facturas que pertenecen al cliente cuyo nombre es 'Pedro' (en realidad, siendo puristas, obtiene todas las facturas que pertenecen a todos los clientes que se llaman Pedro). Este es el caso más común de consulta Join y probablemente también el más útil, podemos utilizar este tipo de consultas para obtener exactamente la información que necesitamos de entre dos tablas.
La forma anterior de expresar un join es la forma implicita, es decir que el motor de la base de datos internamente hará un Join para conseguirnos los resultados sin necesidad de que nosotros se lo digamos. No obstante podemos conseguir el mismo resultado utilizando la declaración explicita del Join de la siguiente forma:
Otro ejemplo, en una situación maestro detalle podemos hacer un join para obtener todas las direcciones de cada cliente:
de esta forma estaremos obteniendo el nombre, apellido y cada dirección para cada cliente que conozcamos, produciría una salida similar a la siguiente (que como vemos repite nombres y apellidos por cada dirección).
| Nombre | Apellido | Direccion |
| Jorge | Martín | C/ Acacias 27 |
| Jorge | Martín | C/ Castellana 102 |
| Juan | Pérez | C/ Goya 134 |
| Jose | Ruiz | Pº de las delicias 28 |
Un outer join es muy similar a un inner join exceptuando el trato que se da a aquellos registros que no existen en una de las tablas. En un Inner Join tradicional cuando un elemento no existe en alguna de las tablas ese elemento no se muestra en el listado final mientras que en el caso de un outer join si. Existen tres tipos de outer join: left outer join, right outer join y full outer join. Los nombres, "left" (izquierda), "right" (derecha) y "full" (completo) se refieren al comportamiento que queremos que tenga el join para aquellos registros que no existen. Así, el left outer join mostrará siempre todos los registros de la tabla izquierda (en la consulta) independientemente de que estén presentes en la tabla derecha, el right outer join hará lo correspondiente con la tabla situada a la derecha mientras que el full outer join mostrará todos los registros de las dos tablas. Vamos a ver algunos ejemplos comparando el inner join con el outer join. Supongamos las tablas anteriormente vistas y una nueva que vendrá bien para los ejemplos
|
|
|
Supongamos que realizamos un outer join entre la tabla personas y la tabla coches:
Producirá el siguiente resultado que es idéntico al de un inner join puesto que todos los registros de la tabla izquierda poseen un valor presente en la tabla derecha:
| Nombre | Marca |
| Benito | Opel |
| Pepe | Opel |
| Lucia | Nissan |
| Andrés | Ford |
Sin embargo si realizamos un right outer join veremos como si hay diferencias
el resultado en este caso será:
| Nombre | Marca |
| Benito | Opel |
| Pepe | Opel |
| Lucia | Nissan |
| Andrés | Ford |
| null | Citroen |
que para el citroen tiene un null en la parte del nombre ya que no existe ninguna persona que tenga un citroen. Otro ejemplo, si hacemos un inner join y un outer join entre la tabla Personas y la tabla Parking
|
SELECT Personas.Nombre, Parking.Plaza FROM
Personas INNER JOIN Parking ON Personas.ParkingID = Parking.ParkingID |
SELECT Personas.Nombre, Parking.Plaza FROM
Personas RIGHT OUTER JOIN Parking ON Personas.ParkingID = Parking.ParkingID |
||||||||||||||
|
|
Como vemos en el caso del outer join la tabla derecha (porque es un right outer join) se muestra completa, con todos los registros independientemente de si estos están o no la tabla izquierda. Si realizamos un left outer join obtendremos en cambio:
| Nombre | Marca |
| Benito | A4 |
| Pepe | null |
| Lucia | null |
| Andrés | null |
| null | Citroen |