Lo primero que hay que decir es que LAST() y FIRST() no existen en MySQL. Son funciones de agregación de Microsoft Access, no del SQL estándar; lo que el estándar define son FIRST_VALUE() y LAST_VALUE(), que son funciones de ventana. Pero se puede conseguir el mismo efecto con una pequeña trampa:
SELECT
agrupacion,
MIN(valor) AS minimo,
MAX(valor) AS maximo,
AVG(valor) AS media,
SUBSTRING_INDEX(GROUP_CONCAT(valor ORDER BY fecha), ',', 1) AS primer_valor,
SUBSTRING_INDEX(GROUP_CONCAT(valor ORDER BY fecha DESC), ',', 1) AS ultimo_valor
FROM datos
GROUP BY agrupacion;
GROUP_CONCAT junta todos los valores del grupo en una cadena, en el orden que le digas, y SUBSTRING_INDEX se queda con lo que hay antes de la primera coma: el primero de la lista. Ordenando al revés, el último.
Es útil cuando queremos sacar los distintos valores de una tabla de históricos, por ejemplo, como esta:
| id | producto_id | precio | fecha |
|---|---|---|---|
| 1 | 23 | 50.00 | 2014-02-15 |
| 2 | 23 | 53.00 | 2014-02-18 |
| 3 | 23 | 60.00 | 2014-02-20 |
| 4 | 23 | 48.00 | 2014-02-23 |
| 5 | 23 | 43.00 | 2014-02-25 |
| 6 | 23 | 51.00 | 2014-02-28 |
En esta tabla, valor sería el campo precio y agrupacion sería producto_id. Para el producto 23 saldría mínimo 43, máximo 60, primer precio 50 y último 51, todo en una sola consulta.
Lo que hay que saber antes de usarlo
- La cadena tiene un límite.
GROUP_CONCATcorta el resultado engroup_concat_max_lenbytes, y lo hace sin avisar. Con el primer valor no pasa nada, porque va al principio, pero si el grupo es enorme conviene subirlo:SET SESSION group_concat_max_len = 1000000; - Devuelve texto, no número. Si vas a operar con él, conviértelo:
CAST(... AS DECIMAL(10,2)). - Si el valor lleva comas, el corte falla. Usa otro separador:
GROUP_CONCAT(valor ORDER BY fecha SEPARATOR '|')y corta por'|'. - Si dos filas tienen la misma fecha, añade el
idalORDER BYpara que el resultado no dependa del azar.
Hoy hay formas mejores
Esto lo escribí en 2014. Con MySQL 8 o MariaDB 10.2 en adelante ya hay funciones de ventana (FIRST_VALUE, LAST_VALUE, ROW_NUMBER) que lo resuelven sin trucos. Las cuento, junto con los índices para que vaya rápido, en Primer y último registro en MySQL: alternativas a FIRST() y LAST().