FIRST_VALUE
FIRST_VALUE devuelve el valor de una expresión evaluada en la primera fila de la ventana, según el orden especificado. Es la función ideal para comparaciones de línea base: puedes mostrar junto a cada fila el primer valor de su grupo y calcular cuánto se desvía del punto de referencia.
En análisis de negocio, el "primer valor" tiene muchos significados prácticos: el primer empleado contratado, la primera venta del mes, el precio más bajo de una categoría (si ordenas ascendentemente), o el salario más alto del departamento (si ordenas descendentemente). FIRST_VALUE te permite traer esa referencia a cada fila sin subconsultas.
Sintaxis
FIRST_VALUE(expresion) OVER (
[PARTITION BY columna1, columna2, ...]
ORDER BY columna1 [ASC|DESC], columna2 [ASC|DESC], ...
[especificación_de_marco]
)La función acepta un solo argumento: la expresión cuyo primer valor dentro de la ventana quieres obtener. A diferencia de LAG y LEAD, no tiene parámetros de offset ni valor por defecto. El ORDER BY determina qué fila se considera "primera". La especificación de marco rara vez se necesita con FIRST_VALUE porque el marco por defecto (UNBOUNDED PRECEDING a CURRENT ROW) siempre incluye la primera fila de la partición.
Comportamiento básico
En su forma más simple, FIRST_VALUE devuelve el mismo valor para todas las filas de la partición, correspondiente a la primera fila según el orden:
SELECT
nombre,
departamento,
salario,
FIRST_VALUE(nombre) OVER (
PARTITION BY departamento
ORDER BY salario DESC
) AS mejor_pagado
FROM empleados;| nombre | departamento | salario | mejor_pagado |
|---|---|---|---|
| Roberto Fernández | Marketing | 72000.00 | Roberto Fernández |
| Laura Díaz | Marketing | 58000.00 | Roberto Fernández |
| Sofía Moreno | Marketing | 45000.00 | Roberto Fernández |
| Carlos López | Ventas | 68000.00 | Carlos López |
| Ana García | Ventas | 62000.00 | Carlos López |
| Marta Ruiz | Ventas | 55000.00 | Carlos López |
| Pedro Sánchez | Ventas | 48000.00 | Carlos López |
Cada fila muestra quién tiene el salario más alto en su departamento. Como ordenamos descendentemente por salario, la primera fila de cada partición es la del empleado con mayor salario. El valor se repite en todas las filas del mismo departamento.
Comparación con el primer valor
El verdadero poder de FIRST_VALUE aparece cuando lo usas para calcular diferencias respecto al primer valor:
SELECT
nombre,
departamento,
salario,
FIRST_VALUE(salario) OVER (
PARTITION BY departamento
ORDER BY salario DESC
) AS salario_maximo,
salario - FIRST_VALUE(salario) OVER (
PARTITION BY departamento
ORDER BY salario DESC
) AS diferencia_vs_maximo
FROM empleados;| nombre | departamento | salario | salario_maximo | diferencia_vs_maximo |
|---|---|---|---|---|
| Roberto Fernández | Marketing | 72000.00 | 72000.00 | 0.00 |
| Laura Díaz | Marketing | 58000.00 | 72000.00 | -14000.00 |
| Sofía Moreno | Marketing | 45000.00 | 72000.00 | -27000.00 |
| Carlos López | Ventas | 68000.00 | 68000.00 | 0.00 |
| Ana García | Ventas | 62000.00 | 68000.00 | -6000.00 |
| Marta Ruiz | Ventas | 55000.00 | 68000.00 | -13000.00 |
La primera fila de cada departamento tiene diferencia 0 (ella misma es el máximo). Las demás muestran cuánto les falta para alcanzar al mejor pagado de su equipo.
Caso práctico: comparación con la primera venta del mes
En análisis de ventas, comparar cada venta del día con la primera venta del mes revela cómo evoluciona el negocio desde el arranque del periodo:
SELECT
fecha_venta,
total,
FIRST_VALUE(total) OVER (
PARTITION BY YEAR(fecha_venta), MONTH(fecha_venta)
ORDER BY fecha_venta
) AS primera_venta_mes,
ROUND(
(total - FIRST_VALUE(total) OVER (
PARTITION BY YEAR(fecha_venta), MONTH(fecha_venta)
ORDER BY fecha_venta
)) / FIRST_VALUE(total) OVER (
PARTITION BY YEAR(fecha_venta), MONTH(fecha_venta)
ORDER BY fecha_venta
) * 100,
2
) AS variacion_vs_primera
FROM ventas
WHERE id_cliente = 100 AND YEAR(fecha_venta) = 2024
ORDER BY fecha_venta
LIMIT 6;| fecha_venta | total | primera_venta_mes | variacion_vs_primera |
|---|---|---|---|
| 2024-01-05 | 250.00 | 250.00 | 0.00 |
| 2024-01-12 | 380.00 | 250.00 | 52.00 |
| 2024-01-28 | 175.00 | 250.00 | -30.00 |
| 2024-02-03 | 420.00 | 420.00 | 0.00 |
| 2024-02-15 | 310.00 | 420.00 | -26.19 |
| 2024-02-22 | 540.00 | 420.00 | 28.57 |
El PARTITION BY YEAR(fecha_venta), MONTH(fecha_venta) reinicia el primer valor en cada mes. En enero, la primera venta fue de 250 y las siguientes se comparan contra esa referencia. En febrero, el punto de referencia cambia a 420.
Caso práctico: primer empleado contratado por departamento
FIRST_VALUE puede extraer cualquier columna de la primera fila, no solo valores numéricos. Aquí obtenemos el nombre del empleado más antiguo de cada departamento:
SELECT
nombre,
departamento,
fecha_contratacion,
FIRST_VALUE(nombre) OVER (
PARTITION BY departamento
ORDER BY fecha_contratacion
) AS empleado_mas_antiguo,
FIRST_VALUE(fecha_contratacion) OVER (
PARTITION BY departamento
ORDER BY fecha_contratacion
) AS fecha_primer_contrato,
DATEDIFF(
fecha_contratacion,
FIRST_VALUE(fecha_contratacion) OVER (
PARTITION BY departamento
ORDER BY fecha_contratacion
)
) AS dias_despues_del_primero
FROM empleados
ORDER BY departamento, fecha_contratacion;| nombre | departamento | fecha_contratacion | empleado_mas_antiguo | fecha_primer_contrato | dias_despues_del_primero |
|---|---|---|---|---|---|
| Roberto Fernández | Marketing | 2018-03-15 | Roberto Fernández | 2018-03-15 | 0 |
| Laura Díaz | Marketing | 2020-07-01 | Roberto Fernández | 2018-03-15 | 839 |
| Sofía Moreno | Marketing | 2022-01-10 | Roberto Fernández | 2018-03-15 | 1397 |
| Carlos López | Ventas | 2017-06-20 | Carlos López | 2017-06-20 | 0 |
| Ana García | Ventas | 2019-09-05 | Carlos López | 2017-06-20 | 808 |
| Marta Ruiz | Ventas | 2021-04-12 | Carlos López | 2017-06-20 | 1392 |
Roberto lleva 839 días más que Laura en Marketing. Carlos fue el primero en Ventas y Ana llegó 808 días después. Este tipo de análisis es útil para calcular antigüedad relativa dentro de cada equipo.
Caso práctico: precio de referencia por categoría
En e-commerce, comparar cada producto con el más barato (o más caro) de su categoría es un análisis frecuente para estrategias de precios:
SELECT
nombre,
categoria,
precio,
FIRST_VALUE(precio) OVER (
PARTITION BY categoria
ORDER BY precio
) AS precio_minimo_cat,
FIRST_VALUE(nombre) OVER (
PARTITION BY categoria
ORDER BY precio
) AS producto_mas_barato,
ROUND(precio / FIRST_VALUE(precio) OVER (
PARTITION BY categoria
ORDER BY precio
), 2) AS factor_vs_minimo
FROM productos
WHERE activo = 1
ORDER BY categoria, precio;| nombre | categoria | precio | precio_minimo_cat | producto_mas_barato | factor_vs_minimo |
|---|---|---|---|---|---|
| Ratón inalámbrico | Electrónica | 29.90 | 29.90 | Ratón inalámbrico | 1.00 |
| Teclado mecánico RGB | Electrónica | 129.00 | 29.90 | Ratón inalámbrico | 4.31 |
| Monitor 27" 4K | Electrónica | 349.00 | 29.90 | Ratón inalámbrico | 11.67 |
| Camiseta algodón orgánico | Ropa | 25.00 | 25.00 | Camiseta algodón orgánico | 1.00 |
| Pantalón chino slim | Ropa | 49.90 | 25.00 | Camiseta algodón orgánico | 2.00 |
| Abrigo lana italiano | Ropa | 245.00 | 25.00 | Camiseta algodón orgánico | 9.80 |
El factor 11.67 del monitor indica que cuesta casi 12 veces más que el producto más barato de su categoría. Este análisis ayuda a entender la dispersión de precios dentro de cada categoría.
Manejo de NULL
Si la primera fila de la partición tiene NULL en la columna referenciada, FIRST_VALUE devuelve NULL para todas las filas de esa partición:
SELECT
nombre,
comision,
FIRST_VALUE(comision) OVER (ORDER BY comision) AS primera_comision
FROM empleados;| nombre | comision | primera_comision |
|---|---|---|
| Pedro Sánchez | NULL | NULL |
| Elena Torres | NULL | NULL |
| David Moreno | 3000.00 | NULL |
| Roberto Fernández | 5000.00 | NULL |
Como los NULL se ordenan primero en orden ascendente, el primer valor es NULL, y eso se propaga a todas las filas. Si quieres que el primer valor sea el primer valor no nulo, filtra los NULL o usa un orden que los coloque al final:
FIRST_VALUE(comision) OVER (
ORDER BY ISNULL(comision), comision
)Con ISNULL(comision), los NULL se ordenan después de todos los valores no nulos, haciendo que el primer valor sea 3000.
Combinación con otras funciones
FIRST_VALUE se combina con funciones de agregación de ventana para crear análisis multidimensionales donde cada fila muestra tanto su posición individual como la referencia del grupo:
SELECT
nombre,
departamento,
salario,
FIRST_VALUE(salario) OVER w AS maximo_depto,
AVG(salario) OVER w AS promedio_depto,
ROUND(salario / FIRST_VALUE(salario) OVER w * 100, 1) AS pct_del_maximo,
ROUND(salario / AVG(salario) OVER w * 100, 1) AS pct_del_promedio
FROM empleados
WINDOW w AS (PARTITION BY departamento ORDER BY salario DESC)
ORDER BY departamento, salario DESC;| nombre | departamento | salario | maximo_depto | promedio_depto | pct_del_maximo | pct_del_promedio |
|---|---|---|---|---|---|---|
| Roberto Fernández | Marketing | 72000.00 | 72000.00 | 72000.00 | 100.0 | 100.0 |
| Laura Díaz | Marketing | 58000.00 | 72000.00 | 65000.00 | 80.6 | 89.2 |
| Sofía Moreno | Marketing | 45000.00 | 72000.00 | 58333.33 | 62.5 | 77.1 |
El uso de la cláusula WINDOW evita repetir la definición de ventana en cada función. Cada fila muestra qué porcentaje alcanza respecto al máximo y respecto al promedio de su departamento.
Practica con FIRST_VALUE
Usa el editor para obtener el primer valor de cada particion:
En el siguiente artículo veremos LAST_VALUE para obtener el último valor de la ventana.
Escrito por Eduardo Lázaro
