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?
-
AT TIME ZONE 'CET'
transforma otimestamp
valoreventtime
paratimestamptz
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).
-
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 paratimestamptz
, 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
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