Quando voce precisa registrar "quando isso aconteceu", quase todo mundo recorre a CURRENT_TIMESTAMP. Mas o PostgreSQL traz toda uma familia de funcoes de tempo, e as diferencas decidem qual tipo voce recebe e quao "fresco" esse instante realmente e. Vamos ao concreto.
CURRENT_TIMESTAMP retorna timestamp with time zone (apelido timestamptz), enquanto LOCALTIMESTAMP retorna timestamp without time zone. Isso nao e cosmetico: o primeiro guarda um instante absoluto, o segundo guarda a hora de "relogio de parede" sem zona associada.
SELECT
CURRENT_TIMESTAMP AS with_zone,
LOCALTIMESTAMP AS without_zone;
A conclusao pratica: em uma aplicacao global voce quase sempre quer timestamptz. Uma coluna users.created_at deveria ser timestamptz, para que um usuario em Lisboa e outro em Moscou registrem o mesmo instante absoluto, e nao duas leituras "locais" diferentes.
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 da transacao, nao da instrucao
A caracteristica chave: CURRENT_TIMESTAMP e LOCALTIMESTAMP retornam o momento em que a transacao comecou, e esse valor fica congelado ate o commit. Dentro de uma mesma transacao toda chamada retorna um valor identico, mesmo que segundos tenham passado entre elas.
BEGIN;
SELECT CURRENT_TIMESTAMP;
SELECT CURRENT_TIMESTAMP;
COMMIT;
Isso e realmente util: se uma unica transacao insere um pedido e uma linha de auditoria, ambas recebem exatamente o mesmo created_at, sem desvio de fracoes 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;
Quando voce precisa do "agora de verdade": clock_timestamp()
As vezes o congelamento atrapalha, por exemplo ao fazer profiling ou ao carimbar linhas uma a uma dentro de uma unica consulta. O PostgreSQL oferece uma gradacao:
transaction_timestamp() e sinonimo de CURRENT_TIMESTAMP, o momento de inicio da transacao.
statement_timestamp() e o momento em que a instrucao atual comecou.
clock_timestamp() e o "agora" real do relogio, muda a cada chamada, mesmo dentro de um unico SELECT.
SELECT
clock_timestamp() AS row_time
FROM generate_series(1, 3);
Regra pratica: para auditoria e dados de negocio use CURRENT_TIMESTAMP (a consistencia vence). Para medir quanto tempo os passos levam dentro de uma funcao, use clock_timestamp().
Pegadinha: now() no PostgreSQL e exatamente transaction_timestamp(), ou seja, tambem esta congelado. Se um loop em PL/pgSQL espera que now() "avance", ele nao vai. Use clock_timestamp() no lugar.
Valores DEFAULT para colunas de auditoria
O cenario mais comum e preencher automaticamente colunas de criacao e atualizacao. created_at se resolve com um DEFAULT, enquanto updated_at precisa de um trigger, porque um DEFAULT so dispara no 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();
Diferencas no MySQL e no ClickHouse
- MySQL:
CURRENT_TIMESTAMP retorna um DATETIME (sem zona) e, ao contrario do PostgreSQL, reflete a hora da instrucao e nao da transacao. Em compensacao oferece a comoda sintaxe created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP mais updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP para um updated_at automatico sem trigger.
- ClickHouse: use
now() (tipo DateTime) ou now64() para precisao de milissegundos; LOCALTIMESTAMP e suportado como alias. Nao existe o congelamento por transacao do PostgreSQL, porque o modelo de execucao e completamente diferente.
Resumindo: CURRENT_TIMESTAMP para tempo absoluto com zona, LOCALTIMESTAMP quando voce nao precisa de zona, ambos estaveis dentro da transacao, e clock_timestamp() como ferramenta quando voce precisa de um relogio real que avanca.
Quando voce precisa registrar "quando isso aconteceu", quase todo mundo recorre a
CURRENT_TIMESTAMP. Mas o PostgreSQL traz toda uma familia de funcoes de tempo, e as diferencas decidem qual tipo voce recebe e quao "fresco" esse instante realmente e. Vamos ao concreto.Os dois protagonistas: com zona e sem zona
CURRENT_TIMESTAMPretornatimestamp with time zone(apelidotimestamptz), enquantoLOCALTIMESTAMPretornatimestamp without time zone. Isso nao e cosmetico: o primeiro guarda um instante absoluto, o segundo guarda a hora de "relogio de parede" sem zona associada.SELECT CURRENT_TIMESTAMP AS with_zone, -- timestamptz LOCALTIMESTAMP AS without_zone; -- timestampA conclusao pratica: em uma aplicacao global voce quase sempre quer
timestamptz. Uma colunausers.created_atdeveria sertimestamptz, para que um usuario em Lisboa e outro em Moscou registrem o mesmo instante absoluto, e nao duas leituras "locais" diferentes.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 da transacao, nao da instrucao
A caracteristica chave:
CURRENT_TIMESTAMPeLOCALTIMESTAMPretornam o momento em que a transacao comecou, e esse valor fica congelado ate o commit. Dentro de uma mesma transacao toda chamada retorna um valor identico, mesmo que segundos tenham passado entre elas.BEGIN; SELECT CURRENT_TIMESTAMP; -- ex. 12:00:00.000 -- ... consulta pesada de 3 segundos ... SELECT CURRENT_TIMESTAMP; -- AINDA 12:00:00.000 COMMIT;Isso e realmente util: se uma unica transacao insere um pedido e uma linha de auditoria, ambas recebem exatamente o mesmo
created_at, sem desvio de fracoes 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;Quando voce precisa do "agora de verdade": clock_timestamp()
As vezes o congelamento atrapalha, por exemplo ao fazer profiling ou ao carimbar linhas uma a uma dentro de uma unica consulta. O PostgreSQL oferece uma gradacao:
transaction_timestamp()e sinonimo deCURRENT_TIMESTAMP, o momento de inicio da transacao.statement_timestamp()e o momento em que a instrucao atual comecou.clock_timestamp()e o "agora" real do relogio, muda a cada chamada, mesmo dentro de um unicoSELECT.SELECT clock_timestamp() AS row_time FROM generate_series(1, 3); -- tres valores de tempo DIFERENTESRegra pratica: para auditoria e dados de negocio use
CURRENT_TIMESTAMP(a consistencia vence). Para medir quanto tempo os passos levam dentro de uma funcao, useclock_timestamp().Valores DEFAULT para colunas de auditoria
O cenario mais comum e preencher automaticamente colunas de criacao e atualizacao.
created_atse resolve com umDEFAULT, enquantoupdated_atprecisa de um trigger, porque umDEFAULTso dispara noINSERT.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();Diferencas no MySQL e no ClickHouse
CURRENT_TIMESTAMPretorna umDATETIME(sem zona) e, ao contrario do PostgreSQL, reflete a hora da instrucao e nao da transacao. Em compensacao oferece a comoda sintaxecreated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMPmaisupdated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMPpara umupdated_atautomatico sem trigger.now()(tipoDateTime) ounow64()para precisao de milissegundos;LOCALTIMESTAMPe suportado como alias. Nao existe o congelamento por transacao do PostgreSQL, porque o modelo de execucao e completamente diferente.Resumindo:
CURRENT_TIMESTAMPpara tempo absoluto com zona,LOCALTIMESTAMPquando voce nao precisa de zona, ambos estaveis dentro da transacao, eclock_timestamp()como ferramenta quando voce precisa de um relogio real que avanca.