Conoce SQL – SELECT – Parte 3 – Agrupaciones

En esta tercera parte de select veremos qué son y cómo se usan las agrupaciones de datos.

Vamos a empezar por lo más sencillo. Imagina que queremos contar el numero de filas de una tabla, por ejemplo para saber cuantos usuarios tenemos o el numero de paquetes en nuestra base de datos. Si no se conce como sql nos lo hace, lo normal es tirar por algo de esto desde php:

$sql=”SELECT * FROM paquetes”;
$res=mysql_query($sql);
$n=0;
while ($fila=mysql_fetch_array($res))
{
$n++;
}
echo “Tenemos $n paquetes en la base de datos”;

Quizá a estas alturas ya te hayas percatado (aunque sea porque siempre digo lo mismo :P) de lo ineficiente que es este algoritmo. Seleccionamos TODO cuando no nececitamos NADA, no necesitamos ningún dato de la tabla pero sin embargo vamos a ir extrayendo toda la tabla. Para esto existe count, que se usaría de esta manera:

$sql=”SELECT count(*) FROM paquetes”;
$res=mysql_query($sql);
$fila=mysql_fetch_array($res);
echo “Tenemos “.$fila[0].” paquetes en la base de datos”;

A la función count se le pasa el argumento de la columna que queremos contar pero ya te digo que da igual que pongas algo o simplemente el asterisco, mysql hará lo mismo así que ponemos asterisco para evitar tener fallos. En este caso la diferencia es la eficiencia ya que el algoritmo en php no es nada complejo.

Para la siguiente demostración vamos a insertar una columna más a la tabla paquetes. Esta columna contendrá el precio que se ha cobrado por transportar el paquete

ALTER TABLE paquetes ADD precio DOUBLE

Y asignamos precio a los paquetes

UPDATE paquetes SET precio = ‘25.30’ WHERE codigo_paquete = ‘MA125′;
UPDATE paquetes SET precio = ’12’ WHERE codigo_paquete = ‘PA123’;
UPDATE paquetes SET precio = ‘9.36’ WHERE codigo_paquete = ‘PA124’;
UPDATE paquetes SET precio = ‘8.12’ WHERE codigo_paquete = ‘PA125’;
UPDATE paquetes SET precio = ‘5.30’ WHERE codigo_paquete = ‘PA129’;
UPDATE paquetes SET precio = ‘9.67’ WHERE codigo_paquete = ‘VA126’;
UPDATE paquetes SET precio = ‘22.4’ WHERE codigo_paquete = ‘VA127′;
UPDATE paquetes SET precio = ’35’ WHERE codigo_paquete = ‘VA128’;

Ojo con los dobles, aunque sean numeros es recomendable ponerlos entre comillas simples porque si llevan decimales te dará error si no las pones.

Ahora queremos saber lo que ha costado enviar todos los paquetes. En php podríamos hacerlo de esta manera:

$sql=”SELECT precio FROM paquetes”;
$res=mysql_query($sql);
$n=0;
while ($fila=mysql_fetch_array($res))
{
$n+=$fila[0];
}
echo “El precio total es $n”;

Bueno esta solución no está mal del todo pero es mucho mas elegante esta otra

$sql=”SELECT sum(precio) FROM paquetes”;
$res=mysql_query($sql);
$fila=mysql_fetch_array($res);
echo “El precio total es “.$fila[0];

Bien, estas funciones están bien pero se debería de poder hacer algo más complejo para tener una sección solo para ello. Pues si, y ya veras que cachi. Ahora vamos realmente a agrupar datos.
Imagina que queremos saber el numero de paquetes que se han enviado a cada ciudad, la consulta podría ser la siguiente.

SELECT ciudad,count(*) FROM paquetes
JOIN ciudades USING (id_ciudad)
GROUP BY ciudad

Esta consulta ya nos soluciona bastante a que sí. Esta consulta lo que hace es mediante GROUP BY dividir en grupos todos los datos en función a una columna. Es decir, si le decimos que agrupe por ciudad cojerá y hará tantos grupos como ciudades haya y meterá en esos grupos a los paquetes de de esa ciudad.

Lo que hay que tener en cuenta es que al agrupar datos, esos datos solo se pueden manejar como grupos y no individualmente. En resumidas cuentas, solo podemos estraer el dato de la columna que usemos como “agrupador” que en este caso es ciudad y funciones de grupo como son count o sum.

Más o menos la divisió que hace de los grupos sería esta, en el caso de crearlos a partir de la ciudad

Se puede apreciar mi deficiente hablidad con el gimp

Crea tres grupos, uno para cada ciudad. Pero a la información de dentro no se puede acceder individualmente, es decir, no podríamos extraer la descripción ni el código de paquete pero sí contar el numero de elementos del grupo (count) o sumar todos los elemenos de una columna numérica (sum). Para esto hay muchas funciones, otra interesante es AVG que calcula el valor medio pero vamos, que hay muchas.

Ahora se nos a antojado saber con qué ciudad hemos ganado más dinero.

SELECT CONCAT(‘La ciudad con la que mas dinero se ha ganado ha sido ‘,ciudad)
FROM paquetes
JOIN ciudades USING (id_ciudad)
GROUP BY ciudad
ORDER BY SUM(precio) DESC
LIMIT 1

Ala cuantas cosas extrañas tiene esta consulta no? Simplemente quería enseñarte que con SQL puedes hacer de todo. Vamos a explicar la consulta.
CONCAT une tantas cadenas de texto como parámetros se le pasen. En este caso se pasa un string y el valor de la columna ciudad. Despés agrupamos y ordenamos por la suma del precio de cada grupo en descendente (los más altos se muestran primero) y por último limitamos la consulta a un único resultado.

Ahora vamos a usar buena parte de nuestros conocimientos para hacer una consulta que nos diga cuando hemos facturado a partir del 2009.

SELECT SUM(precio) FROM (
SELECT SUM(precio) AS precio FROM paquetes
WHERE fecha_entrega >= ‘1/1/2009’
GROUP BY fecha_entrega
) AS tablita

Vamos a comprender la consulta. Usamos la funcion SUM para sumar todos los valores de la columna precio de una tabla, en este caso la tabla no existe realmente asi que “creamos” una mediante un subselect, a la cual le damos el nombre “tablita”. Dentro del subselect se seleccionan de la tabla paquetes que se han entregado el dia 1/1/2009 o más tarde, los agrupamos y sumamos (se podría haber hecho sin agrupar pero así vamos practicando las agrupaciones). Cuando sepas hacer consultas de este tipo se podría decir que conoces bastante bien mysql a nivel conceptual.

La última parte de este capítulo es como comparar dos columnas “agrupadas”. Ejecuta esta consulta:

SELECT SUM(precio) FROM paquetes
WHERE SUM(precio) > 10
GROUP BY precio

Supongo que te da error. Vamos a intentar esto otro

SELECT SUM(precio) FROM paquetes
WHERE precio > 10
GROUP BY precio

Parece que aqui si nos deja, pero no nos vale ya que esta comparando precio individualmente y no como grupo. Vemos que WHERE no funciona, entonces me veo obligado a ensñarte otra palabrita mágina, HAVING.

SELECT SUM(precio) FROM paquetes
GROUP BY precio
HAVING SUM(precio) > 10

!Que bien! esto ya funciona. Cuando queremos realizar alguna comparación de datos agrupados  lo que hay que usar es HAVING (que va desupués de GROUP BY evidentemente) en vez de WHERE.

Ahora ya solo queda practicar y practicar para coger habilidad. Si nunca habías leído un manual de SQL te habrás dado cuenta de que una base de datos no sólo guarda datos y con el tiempo te irás dando más cuenta. Los select o mejor dicho el DQL es todo un mundo y la parte mas potente de SQL y por mucho rollo que te haya contado me he dejado muchas cosas en el tintero pero mi objetivo no es hacerte un experto sino ayudarte a dar el primer paso.

Hasta el siguiente capítulo!

You may also like...

4 Responses

  1. nestor dice:

    muy bueno xD estuve practicando

  2. Maxpowel dice:

    las agrupaciones algo muy útil que no todo el mundo conoce. Si tienes algún problema aquí te lo aclararé

  3. Luis Angel dice:

    Hola Maxpowel, amigo están muy buenos los ejemplos que muestras aquí, pero quiero realizar lo siguiente y no hay manera que lo pueda resolver, te explico:

    tengo una tabla con los siguientes campos:
    id
    organismo_id
    producto_id
    planprod
    realprod
    annoanteriorprod
    cumprod
    crecprod
    planentrega
    realentrega
    annoantentrega
    cumpentrega

    Amigo estoy tratando de sumar el campo planprod de los productos agrupados por organismo_id, o sea que me muestre el total de planprod de los productos agrupados por organismo_id…colega espero me entiendas y me puedas echar una mano…..

  4. Eragonz dice:

    quiza sea muy tarde pero creo q podria ser con
    select sum(planprod) from tabla group by organismo_id

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *