Cuando necesitas registrar "cuando paso esto", casi todo el mundo recurre a CURRENT_TIMESTAMP. Pero PostgreSQL trae toda una familia de funciones de tiempo, y las diferencias deciden que tipo obtienes y como de "reciente" es ese instante. Vamos a lo concreto.
Los dos protagonistas: con zona y sin zona
CURRENT_TIMESTAMP devuelve timestamp with time zone (alias timestamptz), mientras que LOCALTIMESTAMP devuelve timestamp without time zone. No es algo cosmetico: el primero guarda un instante absoluto, el segundo guarda la hora "de reloj de pared" sin zona asociada.
SELECT
CURRENT_TIMESTAMP AS with_zone,
LOCALTIMESTAMP AS without_zone;
La conclusion practica: en una aplicacion global casi siempre quieres timestamptz. Una columna users.created_at deberia ser timestamptz, para que un usuario en Lisboa y otro en Moscu registren el mismo instante absoluto y no dos lecturas "locales" distintas.
CREATE TABLE users (
id bigserial PRIMARY KEY,
email text NOT NULL UNIQUE,
name text,
country text,
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
);
Hora de la transaccion, no de la sentencia
El rasgo clave: CURRENT_TIMESTAMP y LOCALTIMESTAMP devuelven el momento en que empezo la transaccion, y ese valor queda congelado hasta el commit. Dentro de una misma transaccion cada llamada devuelve un valor identico, aunque pasen segundos entre ellas.
BEGIN;
SELECT CURRENT_TIMESTAMP;
SELECT CURRENT_TIMESTAMP;
COMMIT;
Esto es muy util: si una sola transaccion inserta un pedido y una fila de auditoria, ambas reciben exactamente el mismo created_at, sin desfase de fracciones de segundo.
BEGIN;
INSERT INTO orders (user_id, amount, status, created_at)
VALUES (42, 99.90, 'paid', CURRENT_TIMESTAMP);
INSERT INTO order_audit (order_id, event, at)
VALUES (currval('orders_id_seq'), 'created', CURRENT_TIMESTAMP);
COMMIT;
Cuando necesitas el "ahora real": clock_timestamp()
A veces la congelacion estorba, por ejemplo al perfilar o al marcar filas una a una dentro de una sola consulta. PostgreSQL ofrece una gradacion:
transaction_timestamp() es sinonimo de CURRENT_TIMESTAMP, el momento de inicio de la transaccion.
statement_timestamp() es el momento en que empezo la sentencia actual.
clock_timestamp() es el "ahora" real del reloj, cambia en cada llamada, incluso dentro de un mismo SELECT.
SELECT
clock_timestamp() AS row_time
FROM generate_series(1, 3);
Regla practica: para auditoria y datos de negocio usa CURRENT_TIMESTAMP (gana la consistencia). Para medir cuanto tardan los pasos dentro de una funcion, usa clock_timestamp().
Trampa: now() en PostgreSQL es exactamente transaction_timestamp(), asi que tambien esta congelado. Si un bucle de PL/pgSQL espera que now() "avance", no lo hara. Usa clock_timestamp() en su lugar.
Valores DEFAULT para columnas de auditoria
El escenario mas comun es autocompletar columnas de creacion y actualizacion. created_at se resuelve con un DEFAULT, mientras que updated_at necesita un trigger, porque un DEFAULT solo se dispara en el INSERT.
CREATE TABLE employees (
id bigserial PRIMARY KEY,
name text NOT NULL,
manager_id bigint REFERENCES employees(id),
dept text,
salary numeric(12,2),
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE OR REPLACE FUNCTION touch_updated_at()
RETURNS trigger AS $$
BEGIN
NEW.updated_at := CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_touch
BEFORE UPDATE ON employees
FOR EACH ROW EXECUTE FUNCTION touch_updated_at();
Diferencias en MySQL y ClickHouse
- MySQL:
CURRENT_TIMESTAMP devuelve un DATETIME (sin zona) y, a diferencia de PostgreSQL, refleja la hora de la sentencia y no de la transaccion. A cambio ofrece la comoda sintaxis created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP mas updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP para un updated_at automatico sin trigger.
- ClickHouse: usa
now() (tipo DateTime) o now64() para precision de milisegundos; LOCALTIMESTAMP se admite como alias. No existe la congelacion por transaccion de PostgreSQL, porque el modelo de ejecucion es totalmente distinto.
En resumen: CURRENT_TIMESTAMP para tiempo absoluto con zona, LOCALTIMESTAMP cuando no necesitas zona, ambos estables dentro de la transaccion, y clock_timestamp() como herramienta cuando necesitas un reloj real que avanza.
Cuando necesitas registrar "cuando paso esto", casi todo el mundo recurre a
CURRENT_TIMESTAMP. Pero PostgreSQL trae toda una familia de funciones de tiempo, y las diferencias deciden que tipo obtienes y como de "reciente" es ese instante. Vamos a lo concreto.Los dos protagonistas: con zona y sin zona
CURRENT_TIMESTAMPdevuelvetimestamp with time zone(aliastimestamptz), mientras queLOCALTIMESTAMPdevuelvetimestamp without time zone. No es algo cosmetico: el primero guarda un instante absoluto, el segundo guarda la hora "de reloj de pared" sin zona asociada.SELECT CURRENT_TIMESTAMP AS with_zone, -- timestamptz LOCALTIMESTAMP AS without_zone; -- timestampLa conclusion practica: en una aplicacion global casi siempre quieres
timestamptz. Una columnausers.created_atdeberia sertimestamptz, para que un usuario en Lisboa y otro en Moscu registren el mismo instante absoluto y no dos lecturas "locales" distintas.CREATE TABLE users ( id bigserial PRIMARY KEY, email text NOT NULL UNIQUE, name text, country text, created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP );Hora de la transaccion, no de la sentencia
El rasgo clave:
CURRENT_TIMESTAMPyLOCALTIMESTAMPdevuelven el momento en que empezo la transaccion, y ese valor queda congelado hasta el commit. Dentro de una misma transaccion cada llamada devuelve un valor identico, aunque pasen segundos entre ellas.BEGIN; SELECT CURRENT_TIMESTAMP; -- p. ej. 12:00:00.000 -- ... consulta pesada de 3 segundos ... SELECT CURRENT_TIMESTAMP; -- SIGUE siendo 12:00:00.000 COMMIT;Esto es muy util: si una sola transaccion inserta un pedido y una fila de auditoria, ambas reciben exactamente el mismo
created_at, sin desfase de fracciones de segundo.BEGIN; INSERT INTO orders (user_id, amount, status, created_at) VALUES (42, 99.90, 'paid', CURRENT_TIMESTAMP); INSERT INTO order_audit (order_id, event, at) VALUES (currval('orders_id_seq'), 'created', CURRENT_TIMESTAMP); COMMIT;Cuando necesitas el "ahora real": clock_timestamp()
A veces la congelacion estorba, por ejemplo al perfilar o al marcar filas una a una dentro de una sola consulta. PostgreSQL ofrece una gradacion:
transaction_timestamp()es sinonimo deCURRENT_TIMESTAMP, el momento de inicio de la transaccion.statement_timestamp()es el momento en que empezo la sentencia actual.clock_timestamp()es el "ahora" real del reloj, cambia en cada llamada, incluso dentro de un mismoSELECT.SELECT clock_timestamp() AS row_time FROM generate_series(1, 3); -- tres valores de tiempo DISTINTOSRegla practica: para auditoria y datos de negocio usa
CURRENT_TIMESTAMP(gana la consistencia). Para medir cuanto tardan los pasos dentro de una funcion, usaclock_timestamp().Valores DEFAULT para columnas de auditoria
El escenario mas comun es autocompletar columnas de creacion y actualizacion.
created_atse resuelve con unDEFAULT, mientras queupdated_atnecesita un trigger, porque unDEFAULTsolo se dispara en elINSERT.CREATE TABLE employees ( id bigserial PRIMARY KEY, name text NOT NULL, manager_id bigint REFERENCES employees(id), dept text, salary numeric(12,2), created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE OR REPLACE FUNCTION touch_updated_at() RETURNS trigger AS $$ BEGIN NEW.updated_at := CURRENT_TIMESTAMP; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg_touch BEFORE UPDATE ON employees FOR EACH ROW EXECUTE FUNCTION touch_updated_at();Diferencias en MySQL y ClickHouse
CURRENT_TIMESTAMPdevuelve unDATETIME(sin zona) y, a diferencia de PostgreSQL, refleja la hora de la sentencia y no de la transaccion. A cambio ofrece la comoda sintaxiscreated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMPmasupdated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMPpara unupdated_atautomatico sin trigger.now()(tipoDateTime) onow64()para precision de milisegundos;LOCALTIMESTAMPse admite como alias. No existe la congelacion por transaccion de PostgreSQL, porque el modelo de ejecucion es totalmente distinto.En resumen:
CURRENT_TIMESTAMPpara tiempo absoluto con zona,LOCALTIMESTAMPcuando no necesitas zona, ambos estables dentro de la transaccion, yclock_timestamp()como herramienta cuando necesitas un reloj real que avanza.