Tutorial de SQL. SELECT Parte II

Introducción

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

Funcions de conjunto y Group By

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í

SELECT func1(col1), func2(col2), col3 FROM Tabla WHERE Condicion GROUP BY col3

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:

  • Count: Probablemente la función de agrupación más usada, devuelve el número de ocurrencias de la columna en cuestión.

SELECT Count(*) FROM Personas;

Nos devolverá el número de filas que hay en la tabla Personas, es completamente equivalente a, por ejemplo

SELECT Count(Persona_Id) FROM Personas;

pero puede devolver un número distinto a

SELECT Count(Apellidos) FROM Personas;

esto se debe a que si especificamos una columna concreta nos contará [b]solo aquellas filas que no tengan un valor null en dicha columna[/b] 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

SELECT Count(*) FROM Personas WHERE Edad < 30;

obtendría el número de personas de nuestra base de datos cuya edad es menor que 30 años.

  • Avg: Del inglés "average", calcula la media de la columna dada, por ejemplo, si tenemos una tabla llamada personas y queremos obtener la media de edad ...

SELECT Avg(Edad) FROM Personas;

o, por ejemplo, la media de edad de aquellas personas que ganan más de 30.000 € al año

SELECT Avg(Edad) FROM Personas WHERE Salario > 30000;

cuyo resultado puede ser algo como

Edad
31
  • Max y Min: Devuelven el valor máximo y minimo respectivamente de una columna, por ejemplo

SELECT Min(Edad) FROM Personas WHERE Salario > 30000;

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.

Group By

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

SELECT Count(*), Estado FROM Productos;

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:

SELECT Count(*) AS Total, Estado FROM Productos GROUP BY Estado

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:

SELECT Ciudad, Universidad, Avg(Edad) FROM Alumnos WHERE Edad < 30 GROUP BY Ciudad, Universidad;

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

Having

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.

SELECT Ciudad, Universidad, Avg(Edad) FROM Alumnos WHERE Edad < 30
GROUP BY Ciudad, Universidad HAVING Avg(Edad) > 23

Joins

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.

Inner 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):

SELECT Personas.Nombre, Coches.Marca FROM Personas, Coches

Personas
PersonaId Nombre CocheId
1 Benito 2
2 Pepe 2
3 Lucía 3
4 Andrés 1
Coches
CocheId Marca Modelo
1 Ford Focus
2 Opel Laguna
3 Nissan Almera
4 Nissan 350z
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:

SELECT Facturas.* FROM Facturas, Clientes
WHERE Clientes.Nombre = 'Pedro' AND
           Facturas.IdCliente = Clientes.IdCliente

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:

SELECT Facturas.* FROM Facturas INNER JOIN Clientes
ON Facturas.IdCliente = Clientes.IdClient

Otro ejemplo, en una situación maestro detalle podemos hacer un join para obtener todas las direcciones de cada cliente:

SELECT C.Nombre, C.Apellido, D.Direccion FROM Clientes C, Direcciones D
WHERE D.ClienteId = C.ClienteId

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

Outer Join

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

Personas
PersonaId Nombre CocheId ParkingId
1 Benito 2 1
2 Pepe 2 null
3 Lucía 3 7
4 Andrés 1 null
Coches
CocheId Marca Modelo
1 Ford Focus
2 Opel Laguna
3 Nissan Almera
4 Nissan 350z
5 Citroen C4
Parking
ParkingId Plaza Tamaño
1 A4 14
2 B3 12
3 A1 14
4 A8 10

Supongamos que realizamos un outer join entre la tabla personas y la tabla coches:

SELECT Personas.Nombre, Coches.Marca FROM Personas LEFT OUTER JOIN Coches
ON Personas.CocheId = Coches.CocheId

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

SELECT Personas.Nombre, Coches.Marca FROM Personas RIGHT OUTER JOIN Coches
ON Personas.CocheId = Coches.CocheId

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
INNER JOIN
Nombre Plaza
Benito A4
OUTER JOIN
Nombre Plaza
Benito A4
null B3
null A1
null A8

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:

SELECT Personas.Nombre, Parking.Plaza FROM
Personas LEFT OUTER JOIN Parking
ON Personas.ParkingId = Parking.ParkingId

Nombre Marca
Benito A4
Pepe null
Lucia null
Andrés null
null Citroen
8.09091
Average: 8.1 (11 votes)
Your rating: None