Casi toda consulta analitica empieza con "los ultimos N dias" o "el mes actual". Para que esos filtros sean correctos y rapidos hay que saber en que se diferencia NOW() de CURRENT_DATE, y como sumar intervalos a las fechas.
NOW() y CURRENT_DATE: que devuelven
PostgreSQL ofrece dos formas basicas de pedir "ahora":
NOW() (alias CURRENT_TIMESTAMP) devuelve un timestamptz — un momento con fecha y hora.
CURRENT_DATE devuelve un date — solo el dia de hoy, sin hora.
SELECT
NOW() AS ts,
CURRENT_DATE AS today,
CURRENT_TIME AS clock;
La diferencia importa al comparar. created_at >= CURRENT_DATE conserva todo desde la medianoche de hoy, mientras que created_at >= NOW() solo deja el futuro, que casi nunca es lo que buscas.
La ventana de "los ultimos 7 dias"
El patron canonico resta un intervalo al momento actual:
SELECT id, email, created_at
FROM users
WHERE created_at >= NOW() - INTERVAL '7 days'
ORDER BY created_at DESC;
INTERVAL '7 days' es un literal de duracion. Puedes sumarlo o restarlo a cualquier timestamp o date. Admite hours, days, months, years y combinaciones como INTERVAL '1 day 6 hours'.
El mismo truco escala a los agregados — ingresos del ultimo dia, desglosados por estado:
SELECT status, COUNT(*) AS cnt, SUM(amount) AS revenue
FROM orders
WHERE created_at >= NOW() - INTERVAL '24 hours'
GROUP BY status;
Detalle util: el lado derecho es constante por consulta, asi que el planificador lo evalua una vez y aun puede usar un indice sobre created_at.
"El mes actual" y truncar al limite
Para ventanas de calendario, date_trunc recorta un timestamp al inicio de un periodo:
SELECT id, amount
FROM orders
WHERE created_at >= date_trunc('month', CURRENT_DATE)
AND created_at < date_trunc('month', CURRENT_DATE) + INTERVAL '1 month';
Es mas robusto que EXTRACT(MONTH FROM created_at) = 6: el limite superior se ajusta solo a la duracion del mes y la consulta sigue siendo sargable (usa indice). La misma forma sirve para 'day', 'week' y 'year'.
NOW() frente a clock_timestamp()
El matiz clave: NOW() se fija al inicio de la transaccion y no cambia hasta que esta termina. Todas las llamadas dentro de una misma transaccion devuelven el mismo valor.
BEGIN;
SELECT NOW();
SELECT NOW();
SELECT clock_timestamp();
COMMIT;
NOW() / CURRENT_TIMESTAMP / transaction_timestamp() — hora de inicio de la transaccion.
statement_timestamp() — inicio de la sentencia actual.
clock_timestamp() — reloj real, cambia en cada llamada.
Para la logica de negocio y los filtros usa NOW(): el valor es estable y reproducible. clock_timestamp() solo sirve para medir tiempos dentro de una sola transaccion.
Gotcha: no envuelvas la columna en una funcion solo para compararla. WHERE date_trunc('day', created_at) = CURRENT_DATE desactiva el indice sobre created_at. Compara la columna desnuda contra los limites: created_at >= CURRENT_DATE AND created_at < CURRENT_DATE + INTERVAL '1 day'.
Diferencias de MySQL y ClickHouse
MySQL escribe los intervalos de otra forma — sin comillas y con unidades en singular:
SELECT id, email
FROM users
WHERE created_at >= NOW() - INTERVAL 7 DAY;
SELECT CURDATE() AS today, NOW() AS ts;
- En MySQL,
CURDATE() es el analogo de CURRENT_DATE, y la unidad se escribe INTERVAL 7 DAY (DAY, HOUR, MONTH, sin s).
- El
NOW() de MySQL tambien es estable dentro de una sentencia; el reloj en vivo lo da SYSDATE().
- ClickHouse usa
now() con ayudantes como subtractDays(now(), 7) o now() - INTERVAL 7 DAY; el inicio de mes es toStartOfMonth(now()).
Recuerda tres cosas: NOW() es el timestamp del tiempo de transaccion, CURRENT_DATE es solo fecha, y la aritmetica de intervalos sobre una columna desnuda da ventanas temporales correctas y rapidas.
Casi toda consulta analitica empieza con "los ultimos N dias" o "el mes actual". Para que esos filtros sean correctos y rapidos hay que saber en que se diferencia
NOW()deCURRENT_DATE, y como sumar intervalos a las fechas.NOW() y CURRENT_DATE: que devuelven
PostgreSQL ofrece dos formas basicas de pedir "ahora":
NOW()(aliasCURRENT_TIMESTAMP) devuelve untimestamptz— un momento con fecha y hora.CURRENT_DATEdevuelve undate— solo el dia de hoy, sin hora.SELECT NOW() AS ts, -- 2026-06-17 14:30:00+00 CURRENT_DATE AS today, -- 2026-06-17 CURRENT_TIME AS clock; -- 14:30:00+00La diferencia importa al comparar.
created_at >= CURRENT_DATEconserva todo desde la medianoche de hoy, mientras quecreated_at >= NOW()solo deja el futuro, que casi nunca es lo que buscas.La ventana de "los ultimos 7 dias"
El patron canonico resta un intervalo al momento actual:
SELECT id, email, created_at FROM users WHERE created_at >= NOW() - INTERVAL '7 days' ORDER BY created_at DESC;INTERVAL '7 days'es un literal de duracion. Puedes sumarlo o restarlo a cualquiertimestampodate. Admitehours,days,months,yearsy combinaciones comoINTERVAL '1 day 6 hours'.El mismo truco escala a los agregados — ingresos del ultimo dia, desglosados por estado:
SELECT status, COUNT(*) AS cnt, SUM(amount) AS revenue FROM orders WHERE created_at >= NOW() - INTERVAL '24 hours' GROUP BY status;Detalle util: el lado derecho es constante por consulta, asi que el planificador lo evalua una vez y aun puede usar un indice sobre
created_at."El mes actual" y truncar al limite
Para ventanas de calendario,
date_truncrecorta un timestamp al inicio de un periodo:SELECT id, amount FROM orders WHERE created_at >= date_trunc('month', CURRENT_DATE) AND created_at < date_trunc('month', CURRENT_DATE) + INTERVAL '1 month';Es mas robusto que
EXTRACT(MONTH FROM created_at) = 6: el limite superior se ajusta solo a la duracion del mes y la consulta sigue siendo sargable (usa indice). La misma forma sirve para'day','week'y'year'.NOW() frente a clock_timestamp()
El matiz clave:
NOW()se fija al inicio de la transaccion y no cambia hasta que esta termina. Todas las llamadas dentro de una misma transaccion devuelven el mismo valor.BEGIN; SELECT NOW(); -- 14:30:00 -- ... trabajo largo ... SELECT NOW(); -- sigue siendo 14:30:00 SELECT clock_timestamp(); -- hora real del reloj, ya avanzo COMMIT;NOW()/CURRENT_TIMESTAMP/transaction_timestamp()— hora de inicio de la transaccion.statement_timestamp()— inicio de la sentencia actual.clock_timestamp()— reloj real, cambia en cada llamada.Para la logica de negocio y los filtros usa
NOW(): el valor es estable y reproducible.clock_timestamp()solo sirve para medir tiempos dentro de una sola transaccion.Diferencias de MySQL y ClickHouse
MySQL escribe los intervalos de otra forma — sin comillas y con unidades en singular:
-- MySQL SELECT id, email FROM users WHERE created_at >= NOW() - INTERVAL 7 DAY; SELECT CURDATE() AS today, NOW() AS ts;CURDATE()es el analogo deCURRENT_DATE, y la unidad se escribeINTERVAL 7 DAY(DAY,HOUR,MONTH, sins).NOW()de MySQL tambien es estable dentro de una sentencia; el reloj en vivo lo daSYSDATE().now()con ayudantes comosubtractDays(now(), 7)onow() - INTERVAL 7 DAY; el inicio de mes estoStartOfMonth(now()).Recuerda tres cosas:
NOW()es el timestamp del tiempo de transaccion,CURRENT_DATEes solo fecha, y la aritmetica de intervalos sobre una columna desnuda da ventanas temporales correctas y rapidas.