PERIOD_DIFF

La función PERIOD_DIFF calcula la diferencia en meses entre dos valores de período. Al igual que PERIOD_ADD, trabaja con valores numéricos en formato YYMM o YYYYMM, no con valores de tipo DATE. Devuelve un número entero que representa cuántos meses separan ambos períodos, siendo positivo cuando el primer período es posterior al segundo y negativo en caso contrario.

Esta función es fundamental en escenarios donde necesitas medir duraciones en meses completos, como calcular la antigüedad de un empleado, la duración de una suscripción o el tiempo transcurrido entre dos eventos medidos en períodos contables.

Sintaxis

PERIOD_DIFF(P1, P2)

Ambos argumentos son valores de período en formato YYMM o YYYYMM. La función calcula P1 - P2 y devuelve la diferencia en meses como un número entero con signo. No importa si usas formato de dos o cuatro dígitos para el año, pero por claridad es recomendable usar siempre YYYYMM.

Comportamiento básico

La forma más directa de usar PERIOD_DIFF es calcular los meses entre dos períodos conocidos.

SELECT
    PERIOD_DIFF(202602, 202508) AS meses_diferencia;
meses_diferencia
6

Desde agosto de 2025 hasta febrero de 2026 hay exactamente 6 meses. Si inviertes el orden de los argumentos, el resultado es negativo.

SELECT
    PERIOD_DIFF(202508, 202602) AS diferencia_inversa;
diferencia_inversa
-6

La función también maneja correctamente diferencias que cruzan varios años.

SELECT
    PERIOD_DIFF(202602, 202002) AS seis_anios,
    PERIOD_DIFF(202612, 202601) AS mismo_anio,
    PERIOD_DIFF(202602, 202602) AS mismo_periodo;
seis_aniosmismo_aniomismo_periodo
72110

Seis años equivalen a 72 meses, de enero a diciembre del mismo año hay 11 meses, y dos períodos iguales tienen diferencia cero.

Caso práctico: antigüedad de empleados

Calcular la antigüedad de cada empleado en meses y años es uno de los usos más comunes de PERIOD_DIFF. El período actual se obtiene formateando la fecha actual con DATE_FORMAT.

SELECT
    e.nombre,
    e.departamento,
    e.periodo_ingreso,
    PERIOD_DIFF(
        DATE_FORMAT(CURDATE(), '%Y%m'),
        e.periodo_ingreso
    ) AS meses_antiguedad,
    FLOOR(
        PERIOD_DIFF(DATE_FORMAT(CURDATE(), '%Y%m'), e.periodo_ingreso) / 12
    ) AS anios_completos,
    MOD(
        PERIOD_DIFF(DATE_FORMAT(CURDATE(), '%Y%m'), e.periodo_ingreso), 12
    ) AS meses_restantes
FROM empleados e
ORDER BY meses_antiguedad DESC
LIMIT 5;
nombredepartamentoperiodo_ingresomeses_antiguedadanios_completosmeses_restantes
Ana MartínezIngeniería2015031311011
Carlos LópezVentas20170910185
Laura GómezMarketing2019018571
Pedro SánchezFinanzas2020066858
María RuizRRHH2022014941

Al combinar PERIOD_DIFF con FLOOR y MOD, obtienes la antigüedad expresada en años y meses, que es más legible que solo el total de meses. Ana Martínez lleva 10 años y 11 meses en la empresa, siendo la empleada con mayor antigüedad.

Caso práctico: duración de suscripciones

Un servicio de streaming necesita analizar cuánto tiempo permanecen suscritos sus clientes para medir la retención.

SELECT
    u.nombre,
    u.plan,
    u.periodo_alta,
    COALESCE(u.periodo_baja, DATE_FORMAT(CURDATE(), '%Y%m')) AS periodo_referencia,
    PERIOD_DIFF(
        COALESCE(u.periodo_baja, DATE_FORMAT(CURDATE(), '%Y%m')),
        u.periodo_alta
    ) AS meses_suscrito,
    CASE
        WHEN u.periodo_baja IS NULL THEN 'Activo'
        ELSE 'Cancelado'
    END AS estado
FROM usuarios_suscripcion u
ORDER BY meses_suscrito DESC
LIMIT 6;
nombreplanperiodo_altaperiodo_referenciameses_suscritoestado
María GarcíaPremium20220120260249Activo
Carlos LópezBásico20230620260232Activo
Laura SánchezPremium20240120251223Cancelado
Pedro FernándezFamiliar20240620260220Activo
Ana MartínezBásico2025012025098Cancelado
Juan RodríguezPremium2025102026024Activo

La función COALESCE es clave aquí: para usuarios activos que no tienen periodo_baja, se usa el período actual como referencia. Esto permite calcular uniformemente la duración tanto de suscripciones activas como canceladas.

Caso práctico: clasificar clientes por permanencia

A partir de la duración en meses, puedes segmentar a los clientes en categorías útiles para marketing y retención.

SELECT
    CASE
        WHEN PERIOD_DIFF(DATE_FORMAT(CURDATE(), '%Y%m'), periodo_alta) < 3
            THEN 'Nuevo (< 3 meses)'
        WHEN PERIOD_DIFF(DATE_FORMAT(CURDATE(), '%Y%m'), periodo_alta) < 12
            THEN 'Regular (3-11 meses)'
        WHEN PERIOD_DIFF(DATE_FORMAT(CURDATE(), '%Y%m'), periodo_alta) < 24
            THEN 'Fiel (1-2 años)'
        ELSE 'Veterano (2+ años)'
    END AS segmento,
    COUNT(*) AS total_clientes,
    ROUND(AVG(gasto_mensual), 2) AS gasto_promedio
FROM clientes
WHERE activo = 1
GROUP BY segmento
ORDER BY gasto_promedio DESC;
segmentototal_clientesgasto_promedio
Veterano (2+ años)34289.50
Fiel (1-2 años)51862.30
Regular (3-11 meses)89145.75
Nuevo (< 3 meses)120432.10

Este análisis revela que los clientes veteranos gastan significativamente más que los nuevos, información valiosa para justificar inversiones en programas de retención.

Diferencia con TIMESTAMPDIFF

Mientras que PERIOD_DIFF trabaja con períodos numéricos y solo calcula diferencias en meses, TIMESTAMPDIFF trabaja con valores DATE o DATETIME y puede calcular diferencias en diversas unidades.

-- PERIOD_DIFF: trabaja con períodos numéricos
SELECT PERIOD_DIFF(202602, 202508) AS con_period_diff;
 
-- TIMESTAMPDIFF: trabaja con fechas reales
SELECT TIMESTAMPDIFF(MONTH, '2025-08-14', '2026-02-14') AS con_timestampdiff;
con_period_diff
6
con_timestampdiff
6

Ambas devuelven 6 en este caso, pero hay una diferencia sutil. PERIOD_DIFF calcula la diferencia entre meses sin considerar los días, mientras que TIMESTAMPDIFF con MONTH sí considera el día exacto. Por ejemplo, TIMESTAMPDIFF(MONTH, '2025-08-20', '2026-02-14') devolvería 5, no 6, porque no se ha completado el sexto mes. PERIOD_DIFF(202602, 202508) siempre devuelve 6 independientemente de los días.

Manejo de NULL

PERIOD_DIFF devuelve NULL si cualquiera de sus argumentos es NULL.

SELECT
    PERIOD_DIFF(NULL, 202602) AS primer_nulo,
    PERIOD_DIFF(202602, NULL) AS segundo_nulo,
    PERIOD_DIFF(NULL, NULL) AS ambos_nulos;
primer_nulosegundo_nuloambos_nulos
NULLNULLNULL

En consultas con columnas que pueden ser nulas, usa COALESCE para proporcionar un valor de reemplazo coherente.

SELECT
    nombre,
    COALESCE(
        PERIOD_DIFF(DATE_FORMAT(CURDATE(), '%Y%m'), periodo_inicio),
        0
    ) AS meses_activo
FROM proyectos;
nombremeses_activo
Rediseño web8
App móvil14
Proyecto pendiente0

Combinación con otras funciones

PERIOD_DIFF se combina naturalmente con PERIOD_ADD para realizar cálculos completos. También puedes convertir fechas a períodos usando DATE_FORMAT o EXTRACT.

SELECT
    e.nombre,
    e.fecha_contratacion,
    PERIOD_DIFF(
        EXTRACT(YEAR_MONTH FROM CURDATE()),
        EXTRACT(YEAR_MONTH FROM e.fecha_contratacion)
    ) AS meses_desde_contratacion
FROM empleados e
ORDER BY meses_desde_contratacion DESC
LIMIT 3;
nombrefecha_contratacionmeses_desde_contratacion
Ana Martínez2015-03-16131
Carlos López2017-09-01101
Laura Gómez2019-01-1585

La función EXTRACT(YEAR_MONTH FROM ...) devuelve el año y mes en formato YYYYMM, que es exactamente lo que PERIOD_DIFF espera. Esta combinación permite usar PERIOD_DIFF con columnas DATE sin necesidad de DATE_FORMAT.

En el siguiente artículo veremos TIME_TO_SEC para convertir valores de tiempo a segundos.

Escrito por Eduardo Lázaro