EXTRACT
La función EXTRACT extrae una parte específica de una fecha o datetime. Mientras que MySQL ofrece funciones individuales como YEAR(), MONTH() o DAY(), EXTRACT unifica todas esas operaciones en una sola función con una sintaxis estándar SQL. Si vienes de otros motores de bases de datos como PostgreSQL u Oracle, reconocerás esta función inmediatamente porque forma parte del estándar ANSI SQL.
La ventaja principal de EXTRACT es su versatilidad. Con una misma función puedes obtener desde el año completo hasta fracciones de segundo, pasando por trimestres, semanas y combinaciones como año-mes o día-hora. Esto la convierte en la navaja suiza para descomponer fechas en MySQL.
Sintaxis
EXTRACT(unidad FROM fecha)El argumento unidad determina qué parte de la fecha quieres obtener. El argumento fecha puede ser un valor de tipo DATE, DATETIME o TIMESTAMP. El resultado siempre es un número entero.
Las unidades disponibles son:
-- Unidades simples
EXTRACT(YEAR FROM fecha) -- Año (ej: 2025)
EXTRACT(MONTH FROM fecha) -- Mes (1-12)
EXTRACT(DAY FROM fecha) -- Día del mes (1-31)
EXTRACT(HOUR FROM fecha) -- Hora (0-23)
EXTRACT(MINUTE FROM fecha) -- Minutos (0-59)
EXTRACT(SECOND FROM fecha) -- Segundos (0-59)
EXTRACT(QUARTER FROM fecha) -- Trimestre (1-4)
EXTRACT(WEEK FROM fecha) -- Semana del año (0-53)
-- Unidades compuestas
EXTRACT(YEAR_MONTH FROM fecha) -- Año y mes como AAAAMM
EXTRACT(DAY_HOUR FROM fecha) -- Día y hora como DDHH
EXTRACT(DAY_MINUTE FROM fecha) -- Día, hora y min como DDHHMM
EXTRACT(DAY_SECOND FROM fecha) -- Día a segundos como DDHHMMSS
EXTRACT(HOUR_MINUTE FROM fecha) -- Hora y min como HHMM
EXTRACT(HOUR_SECOND FROM fecha) -- Hora a segundos como HHMMSS
EXTRACT(MINUTE_SECOND FROM fecha) -- Min y seg como MMSSComportamiento básico
Unidades simples
Las unidades simples devuelven exactamente lo que esperas: un número que representa esa porción de la fecha.
SELECT
EXTRACT(YEAR FROM '2025-08-15 14:30:45') AS anio,
EXTRACT(MONTH FROM '2025-08-15 14:30:45') AS mes,
EXTRACT(DAY FROM '2025-08-15 14:30:45') AS dia,
EXTRACT(HOUR FROM '2025-08-15 14:30:45') AS hora,
EXTRACT(MINUTE FROM '2025-08-15 14:30:45') AS minuto,
EXTRACT(SECOND FROM '2025-08-15 14:30:45') AS segundo;| anio | mes | dia | hora | minuto | segundo |
|---|---|---|---|---|---|
| 2025 | 8 | 15 | 14 | 30 | 45 |
Cada valor se devuelve como un entero sin ceros a la izquierda. El mes 8 es agosto, no "08". Esto es idéntico a lo que obtendrías con YEAR('2025-08-15'), MONTH('2025-08-15'), etc.
Trimestre y semana
El trimestre (QUARTER) divide el año en 4 bloques de 3 meses, mientras que la semana (WEEK) indica el número de semana dentro del año:
SELECT
EXTRACT(QUARTER FROM '2025-01-20') AS q1,
EXTRACT(QUARTER FROM '2025-05-10') AS q2,
EXTRACT(QUARTER FROM '2025-09-03') AS q3,
EXTRACT(QUARTER FROM '2025-11-28') AS q4,
EXTRACT(WEEK FROM '2025-08-15') AS semana;| q1 | q2 | q3 | q4 | semana |
|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 32 |
Los trimestres son: enero-marzo (1), abril-junio (2), julio-septiembre (3), octubre-diciembre (4).
Unidades compuestas
Las unidades compuestas concatenan varias partes de la fecha en un solo número. Esto es especialmente útil para ordenar o agrupar por combinaciones de fecha:
SELECT
EXTRACT(YEAR_MONTH FROM '2025-08-15 14:30:45') AS anio_mes,
EXTRACT(DAY_HOUR FROM '2025-08-15 14:30:45') AS dia_hora,
EXTRACT(HOUR_MINUTE FROM '2025-08-15 14:30:45') AS hora_minuto,
EXTRACT(HOUR_SECOND FROM '2025-08-15 14:30:45') AS hora_segundo;| anio_mes | dia_hora | hora_minuto | hora_segundo |
|---|---|---|---|
| 202508 | 1514 | 1430 | 143045 |
YEAR_MONTH devuelve 202508, que es el año y mes concatenados. DAY_HOUR devuelve 1514 (día 15, hora 14). Estos valores compuestos son ideales para crear claves de agrupación compactas.
Caso práctico: informe de ventas por trimestre
Imagina que tienes una tabla de ventas y necesitas calcular los ingresos trimestrales. EXTRACT(QUARTER FROM ...) es perfecto para esto:
SELECT
EXTRACT(YEAR FROM fecha_venta) AS anio,
EXTRACT(QUARTER FROM fecha_venta) AS trimestre,
COUNT(*) AS num_ventas,
SUM(total) AS ingresos,
ROUND(AVG(total), 2) AS ticket_medio
FROM ventas
WHERE fecha_venta >= '2024-01-01'
GROUP BY
EXTRACT(YEAR FROM fecha_venta),
EXTRACT(QUARTER FROM fecha_venta)
ORDER BY anio, trimestre;| anio | trimestre | num_ventas | ingresos | ticket_medio |
|---|---|---|---|---|
| 2024 | 1 | 1240 | 186000.00 | 150.00 |
| 2024 | 2 | 1385 | 221600.00 | 160.00 |
| 2024 | 3 | 1150 | 172500.00 | 150.00 |
| 2024 | 4 | 1520 | 273600.00 | 180.00 |
La belleza de usar EXTRACT aquí es que el mismo patrón funciona para cualquier nivel de agrupación. Si mañana el jefe pide el desglose por semana, solo cambias QUARTER por WEEK. Con funciones individuales necesitarías reescribir más código.
Caso práctico: YEAR_MONTH para series temporales
Cuando necesitas agrupar datos por año y mes simultáneamente, EXTRACT(YEAR_MONTH FROM ...) genera un valor compacto ideal para GROUP BY:
SELECT
EXTRACT(YEAR_MONTH FROM fecha_pedido) AS periodo,
COUNT(*) AS total_pedidos,
SUM(importe) AS facturacion
FROM pedidos
WHERE fecha_pedido >= '2024-06-01'
AND fecha_pedido < '2025-01-01'
GROUP BY EXTRACT(YEAR_MONTH FROM fecha_pedido)
ORDER BY periodo;| periodo | total_pedidos | facturacion |
|---|---|---|
| 202406 | 312 | 46800.00 |
| 202407 | 287 | 43050.00 |
| 202408 | 345 | 55200.00 |
| 202409 | 298 | 44700.00 |
| 202410 | 376 | 60160.00 |
| 202411 | 410 | 69700.00 |
| 202412 | 489 | 88020.00 |
El valor 202406 es compacto y se ordena naturalmente de forma cronológica. Esta técnica es más limpia que concatenar YEAR() y MONTH() con cadenas, porque el resultado es un número que se ordena correctamente sin necesidad de ajustes.
Caso práctico: análisis de actividad por hora del día
Puedes usar EXTRACT(HOUR FROM ...) combinado con GROUP BY para analizar patrones horarios. Supongamos que quieres saber cuándo se producen más registros de acceso en tu aplicación:
SELECT
EXTRACT(HOUR FROM fecha_acceso) AS hora,
COUNT(*) AS accesos,
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER(), 1) AS porcentaje
FROM log_accesos
WHERE fecha_acceso >= '2025-01-01'
GROUP BY EXTRACT(HOUR FROM fecha_acceso)
ORDER BY accesos DESC
LIMIT 5;| hora | accesos | porcentaje |
|---|---|---|
| 10 | 4520 | 12.3 |
| 11 | 4210 | 11.5 |
| 14 | 3980 | 10.9 |
| 9 | 3750 | 10.2 |
| 15 | 3620 | 9.9 |
Este tipo de análisis revela que la mayor actividad se concentra entre las 9 y las 11 de la mañana, con un segundo pico después del almuerzo a las 14-15h.
EXTRACT vs funciones individuales
MySQL ofrece funciones dedicadas como YEAR(), MONTH(), DAY(), HOUR(), MINUTE() y SECOND(). Entonces, ¿cuándo usar EXTRACT?
-- Estas dos consultas son equivalentes
SELECT YEAR(fecha_venta) FROM ventas;
SELECT EXTRACT(YEAR FROM fecha_venta) FROM ventas;La diferencia principal es de estilo y portabilidad. EXTRACT pertenece al estándar SQL, así que funciona igual en PostgreSQL, Oracle, MariaDB y otros motores. Las funciones individuales como YEAR() son extensiones de MySQL que pueden no existir en otros sistemas. Además, EXTRACT ofrece unidades compuestas como YEAR_MONTH o DAY_HOUR que no tienen equivalente directo como función individual.
En cuanto a rendimiento, no hay diferencia medible. MySQL internamente optimiza ambas formas de la misma manera. Elige la que haga tu código más legible para tu equipo.
EXTRACT con NULL
Como la mayoría de funciones en MySQL, EXTRACT devuelve NULL si la fecha de entrada es NULL:
SELECT
EXTRACT(YEAR FROM NULL) AS anio_null,
EXTRACT(MONTH FROM NULL) AS mes_null;| anio_null | mes_null |
|---|---|
| NULL | NULL |
Esto significa que si tu tabla tiene columnas de fecha que admiten valores nulos, EXTRACT propagará ese NULL sin generar errores. Tenlo en cuenta al usar EXTRACT dentro de cláusulas WHERE o GROUP BY: las filas con fecha nula se agruparán juntas bajo NULL.
Combinación con otras funciones
EXTRACT se combina naturalmente con funciones de agregación y con CASE para crear informes dinámicos:
SELECT
EXTRACT(YEAR FROM fecha_venta) AS anio,
SUM(CASE WHEN EXTRACT(QUARTER FROM fecha_venta) = 1 THEN total ELSE 0 END) AS q1,
SUM(CASE WHEN EXTRACT(QUARTER FROM fecha_venta) = 2 THEN total ELSE 0 END) AS q2,
SUM(CASE WHEN EXTRACT(QUARTER FROM fecha_venta) = 3 THEN total ELSE 0 END) AS q3,
SUM(CASE WHEN EXTRACT(QUARTER FROM fecha_venta) = 4 THEN total ELSE 0 END) AS q4
FROM ventas
GROUP BY EXTRACT(YEAR FROM fecha_venta)
ORDER BY anio;| anio | q1 | q2 | q3 | q4 |
|---|---|---|---|---|
| 2023 | 186000.00 | 221600.00 | 172500.00 | 273600.00 |
| 2024 | 198500.00 | 245300.00 | 189000.00 | 312400.00 |
Esta técnica pivota los datos trimestrales en columnas, creando una tabla cruzada que es mucho más fácil de leer que filas separadas por trimestre. EXTRACT dentro de CASE permite construir este tipo de informes dinámicos con elegancia.
En el siguiente artículo veremos YEAR como alternativa directa para extraer el año.
Escrito por Eduardo Lázaro
