sqlpostgresqldatetimetimestamp

CURRENT_TIMESTAMP e LOCALTIMESTAMP no SQL

Como CURRENT_TIMESTAMP difere de LOCALTIMESTAMP, por que ambos congelam na transacao e quando usar clock_timestamp().

2 min de leituraReferencesql · postgresql · datetime · timestamp · audit

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_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,    -- timestamptz
  LOCALTIMESTAMP    AS without_zone; -- timestamp

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;     -- 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 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);
-- tres valores de tempo DIFERENTES

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.

Pratique com exercícios reais

Resolva exercícios no treinador de SQL com correção instantânea e dicas.

Abrir o treinador