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 MMSS

Comportamiento 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;
aniomesdiahoraminutosegundo
2025815143045

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;
q1q2q3q4semana
123432

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_mesdia_horahora_minutohora_segundo
20250815141430143045

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;
aniotrimestrenum_ventasingresosticket_medio
202411240186000.00150.00
202421385221600.00160.00
202431150172500.00150.00
202441520273600.00180.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;
periodototal_pedidosfacturacion
20240631246800.00
20240728743050.00
20240834555200.00
20240929844700.00
20241037660160.00
20241141069700.00
20241248988020.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;
horaaccesosporcentaje
10452012.3
11421011.5
14398010.9
9375010.2
1536209.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_nullmes_null
NULLNULL

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;
anioq1q2q3q4
2023186000.00221600.00172500.00273600.00
2024198500.00245300.00189000.00312400.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