Diferencia entre fechas en SQL: DATEDIFF y sus equivalentes
Contar cuántos días hay entre dos fechas se puede hacer en cualquier base de datos SQL, pero cada SGBD lo hace a su manera. MySQL y SQL Server tienen la función DATEDIFF, pero con argumentos distintos; PostgreSQL ni siquiera la tiene: allí las fechas simplemente se restan. El panorama general:
Vamos a ver cada SGBD y después resolveremos problemas reales: desde «cuántos días tardó el cliente en decidirse a hacer su primer pedido» hasta la edad a partir de la fecha de nacimiento.
DATEDIFF en MySQL
La versión de MySQL recibe dos fechas y devuelve la diferencia en días. La fecha más reciente va como primer argumento:
MySQL 8.4SELECT DATEDIFF('2026-06-12', '2026-06-01') AS days;
Si intercambias los argumentos, el resultado sale negativo, y esto es una fuente habitual de bugs en los informes:
MySQL 8.4SELECT DATEDIFF('2026-06-01', '2026-06-12') AS days; -- -11
El DATEDIFF de MySQL no sabe contar horas, meses ni años. Para eso existe TIMESTAMPDIFF, cuyo orden de argumentos es el contrario: primero el inicio y luego el fin:
MySQL 8.4SELECT TIMESTAMPDIFF(HOUR, '2026-06-11 10:00:00', '2026-06-12 18:30:00') AS hours; -- 32: TIMESTAMPDIFF solo devuelve horas completas (32.5 se trunca hacia abajo) SELECT TIMESTAMPDIFF(MONTH, '2026-01-15', '2026-06-12') AS months; -- 4: meses completos entre las fechas
¿Qué devuelve en MySQL la consulta SELECT DATEDIFF('2026-06-01', '2026-06-12')?
DATEDIFF en SQL Server
En SQL Server la función tiene tres argumentos: la unidad de medida, el inicio y el fin. Fíjate en que las fechas van en orden inverso respecto a MySQL:
MySQL 8.4SELECT DATEDIFF(day, '2026-06-01', '2026-06-12') AS days; -- 11 SELECT DATEDIFF(hour, '2026-06-11 10:00:00', '2026-06-12 18:30:00') AS hours; -- 32
SQL Server tiene una particularidad traicionera: no cuenta periodos completos, sino el número de límites de la unidad que se cruzan. La diferencia «en años» entre el 31 de diciembre y el 1 de enero es de un año:
MySQL 8.4SELECT DATEDIFF(year, '2025-12-31', '2026-01-01') AS years; -- 1, aunque solo ha pasado un día
Por eso, en SQL Server no se puede usar DATEDIFF(year, ...) directamente para calcular la edad o la antigüedad: el resultado sale casi un año más alto para todos los que todavía no han cumplido años este año.
PostgreSQL: resta en lugar de DATEDIFF
Una consulta con DATEDIFF termina en PostgreSQL con un error:
MySQL 8.4SELECT DATEDIFF('2026-06-12', '2026-06-01');
En lugar de una función, aquí se usa aritmética: la diferencia entre dos valores de tipo DATE es un número entero de días:
PostgreSQL 17.9SELECT DATE '2026-06-12' - DATE '2026-06-01' AS days;
Con el tipo TIMESTAMP, la resta ya no devuelve un número, sino un intervalo:
PostgreSQL 17.9SELECT TIMESTAMP '2026-06-12 18:30:00' - TIMESTAMP '2026-06-11 10:00:00' AS diff; -- 1 day 08:30:00
Para convertir un intervalo en un número hay dos caminos:
-
EXTRACT(EPOCH FROM ...) pasa el intervalo a segundos, y a partir de ahí es aritmética normal:
PostgreSQL 17.9SELECT EXTRACT(EPOCH FROM (TIMESTAMP '2026-06-12 18:30:00' - TIMESTAMP '2026-06-11 10:00:00')) / 3600 AS hours; -- 32.5: a diferencia de TIMESTAMPDIFF, se conserva la parte decimal -
AGE(...) devuelve un intervalo «humano» en años, meses y días:
PostgreSQL 17.9SELECT AGE(TIMESTAMP '2026-06-12', TIMESTAMP '1995-08-20') AS age; -- 30 years 9 mons 23 days
¿Por qué falla en PostgreSQL la consulta SELECT DATEDIFF('2026-06-12', '2026-06-01')?
Ejemplo en vivo: del registro al primer pedido
Calculemos con la base de datos del servicio de reparto cuántos días pasan desde que un usuario se registra hasta que hace su primer pedido, una métrica de producto clásica:
La versión para MySQL:
MySQL 8.4SELECT u.user_id, DATE(u.registration_date) AS registered, DATE(MIN(o.order_date)) AS first_order, DATEDIFF(MIN(o.order_date), u.registration_date) AS days_to_order FROM users u JOIN orders o ON o.user_id = u.user_id GROUP BY u.user_id, u.registration_date ORDER BY u.user_id LIMIT 5;
La misma consulta para PostgreSQL:
PostgreSQL 17.9SELECT u.user_id, DATE(u.registration_date) AS registered, DATE(MIN(o.order_date)) AS first_order, MIN(o.order_date)::date - u.registration_date::date AS days_to_order FROM users u JOIN orders o ON o.user_id = u.user_id GROUP BY u.user_id, u.registration_date ORDER BY u.user_id LIMIT 5;
Las consultas solo se diferencian en una línea, la forma de restar las fechas, y el resultado es el mismo.
La edad a partir de la fecha de nacimiento
Otro problema típico en el que es fácil equivocarse. Las versiones correctas:
MySQL 8.4-- MySQL: TIMESTAMPDIFF cuenta años completos SELECT TIMESTAMPDIFF(YEAR, '1995-08-20', CURDATE()) AS age;
PostgreSQL 17.9-- PostgreSQL: AGE devuelve un intervalo y de él extraemos los años SELECT date_part('year', AGE(DATE '1995-08-20')) AS age;
Las dos dan 30 para una persona nacida el 20 de agosto de 1995 (a fecha de 12 de junio de 2026): todavía no ha llegado su cumpleaños, así que tiene 30 años cumplidos. En cambio, el ingenuo (año actual - año de nacimiento) daría 31, uno más de la cuenta mientras no llegue el cumpleaños. Los dos bloques son ejecutables: pon tu fecha de nacimiento y compruébalo.
Chuleta: problema → función
Y la diferencia principal que conviene tener en la cabeza: TIMESTAMPDIFF de MySQL y AGE de PostgreSQL cuentan periodos completos, mientras que DATEDIFF de SQL Server cuenta los límites del periodo que se cruzan.
Siguientes pasos
- cómo funcionan los tipos DATE, TIME y TIMESTAMP, en la lección Fecha y hora en SQL;
- las funciones para trabajar con fechas, con ejercicios, en la lección Trabajar con fecha y hora;
- fichas rápidas en la Referencia de funciones SQL: DATEDIFF, TIMESTAMPDIFF;
- practica con ejercicios en la Práctica SQL.