PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Subtrair horas da função now()

Resposta para timestamp


Você precisa entender a natureza dos tipos de dados timestamp (timestamp without time zone ) e timestamptz (timestamp with time zone ). Se não, leia primeiro:
  • Ignorando completamente os fusos horários no Rails e no PostgreSQL

O AT TIME ZONE construção transforma um timestamp para timestamptz , que é quase certamente o movimento errado para o seu caso:
where eventtime at time zone 'CET' between '2015-06-16 06:00:00'
                                       and '2015-06-17 06:00:00'

Primeiro , ele mata o desempenho. Aplicando AT TIME ZONE para a coluna eventtime torna a expressão não sargável . Postgres não pode usar índices simples em eventtime . Mas mesmo sem índice, as expressões sargáveis ​​são mais baratas. Ajuste os valores do filtro em vez de manipular o valor de cada linha.
Você poderia compensar com um índice de expressão correspondente, mas provavelmente é apenas um mal-entendido e errado de qualquer maneira.

O que acontece nessa expressão?

  1. AT TIME ZONE 'CET' transforma o timestamp valor eventtime para timestamptz anexando o fuso horário do seu fuso horário atual. Ao usar um fuso horário nome (não um deslocamento numérico ou uma abreviação), isso também leva em consideração as regras de horário de verão (horário de verão), para que você obtenha um deslocamento diferente para carimbos de data e hora de "inverno". Basicamente, você obtém a resposta para a pergunta:

    Qual ​​é o carimbo de data/hora UTC correspondente para o carimbo de data/hora fornecido no fuso horário especificado?

    Ao exibir o resultado para o usuário é formatado como carimbo de data/hora local com o deslocamento de tempo de acordo com o fuso horário atual da sessão. (Pode ou não ser igual ao usado na expressão).

  2. Os literais de cadeia de caracteres do lado direito não têm tipo de dados para eles, portanto, o tipo é derivado da atribuição na expressão. Já que é timestamptz agora, ambos são convertidos para timestamptz , assumindo o fuso horário atual da sessão.

    Qual ​​é o carimbo de data/hora UTC correspondente para o carimbo de data/hora fornecido para a configuração de fuso horário da sessão atual.

    O deslocamento pode variar com as regras de horário de verão.

Resumindo a história , se você sempre operar com o mesmo fuso horário:CET ou 'Europe/Berlin' - a mesma coisa para os carimbos de data e hora atuais, mas não para os históricos ou (possivelmente) futuros, você pode simplesmente cortar o lixo.

O segundo problema com a expressão:BETWEEN está quase sempre errado com timestamp valores. Ver:
  • Otimizar ENTRE a declaração de data
  • Encontre intervalos de datas sobrepostos no PostgreSQL
SELECT date_trunc('hour', eventtime) AS hour
     , count(DISTINCT serialnumber)  AS ct  -- sure you need distinct?
FROM   t_el_eventlog
WHERE  eventtime >= now()::date - interval '18 hours'
AND    eventtime <  now()::date + interval '6 hours'
AND    sourceid  =  44  -- don't quote the numeric literal
GROUP  BY 1
ORDER  BY 1;

now() é a implementação Postgres do padrão SQL CURRENT_TIMESTAMP . Ambos retornam timestamptz (não timestamp !). Você pode usar qualquer um.
now()::date é equivalente a CURRENT_DATE . Ambos dependem da configuração de fuso horário atual.

Você deve ter um índice do formulário:
CREATE INDEX foo ON t_el_eventlog(sourceid, eventtime)

Ou, para permitir verificações somente de índice:
CREATE INDEX foo2 ON t_el_eventlog(sourceid, eventtime, serialnumber)

Se você opera em fusos horários diferentes, as coisas ficam mais complicadas e você deve usar timestamptz para tudo.

Alternativa para timestamptz


Antes da atualização da pergunta, parecia que os fusos horários eram importantes. Ao lidar com fusos horários diferentes, "hoje" é uma dependência funcional do fuso horário atual. As pessoas tendem a esquecer isso.

Para trabalhar apenas com a configuração de fuso horário atual da sessão, use a mesma consulta acima. Se executado em um fuso horário diferente, os resultados estão errados na realidade. (Aplica-se ao acima também.)

Para garantir um resultado correto para um determinado fuso horário ('Europa/Berlim' no seu caso) independentemente da configuração de fuso horário atual da sessão, use esta expressão:
    ((now() AT TIME ZONE 'Europe/Berlin')::date - interval '18 hours')
            AT TIME ZONE 'Europe/Berlin'  -- 2nd time to convert back

Esteja ciente de que o AT TIME ZONE construção retorna timestamp para timestamptz entrada e vice-versa.

Como mencionado no início, todos os detalhes sangrentos aqui:
  • Ignorando completamente os fusos horários no Rails e no PostgreSQL