Risposta per timestamp
È necessario comprendere la natura dei tipi di dati timestamp
(timestamp without time zone
) e timestamptz
(timestamp with time zone
). Se non lo fai, leggi prima questo:
- Ignora del tutto i fusi orari in Rails e PostgreSQL
Il AT TIME ZONE
costrutto trasforma un timestamp
a timestamptz
, che è quasi sicuramente la mossa sbagliata per il tuo caso:
where eventtime at time zone 'CET' between '2015-06-16 06:00:00'
and '2015-06-17 06:00:00'
Prima , uccide le prestazioni. Applicazione di AT TIME ZONE
alla colonna eventtime
rende l'espressione non sargable . Postgres non può utilizzare indici semplici su eventtime
. Ma anche senza indice, le espressioni sargable sono più economiche. Regola i valori del filtro invece di manipolare ogni valore di riga.
Puoi potere compensare con un indice di espressione corrispondente, ma probabilmente è solo un malinteso e comunque sbagliato.
Cosa succede in quell'espressione?
-
AT TIME ZONE 'CET'
trasforma iltimestamp
valoreeventtime
atimestamptz
aggiungendo la differenza di orario del fuso orario corrente. Quando si utilizza un nome di un fuso orario (non un offset numerico o un'abbreviazione), questo tiene conto anche delle regole dell'ora legale (ora legale), quindi ottieni un offset diverso per i timestamp "invernali". Fondamentalmente ottieni la risposta alla domanda:Qual è il timestamp UTC corrispondente per il timestamp specificato nel fuso orario specificato?
Durante la visualizzazione il risultato per l'utente viene formattato come timestamp locale con l'offset di tempo corrispondente al fuso orario corrente della sessione. (Può essere o meno lo stesso di quello utilizzato nell'espressione).
-
I valori letterali stringa sul lato destro non hanno alcun tipo di dati, quindi il tipo viene derivato dall'assegnazione nell'espressione. Dato che è
timestamptz
ora, entrambi vengono trasmessi atimestamptz
, assumendo il fuso orario corrente della sessione.Qual è il timestamp UTC corrispondente per il timestamp specificato per l'impostazione del fuso orario della sessione corrente.
L'offset può variare con le regole dell'ora legale.
Per farla breve , se sempre operare con lo stesso fuso orario:CET
o 'Europe/Berlin'
- stessa cosa per i timestamp attuali, ma non per quelli storici o (possibilmente) futuri, puoi semplicemente tagliare il cruft.
Il secondo problema con l'espressione: è quasi sempre sbagliato con BETWEEN
timestamp
valori. Vedi:
- Ottimizza BETWEEN data statement
- Trova intervalli di date sovrapposti in 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()
è l'implementazione Postgres dello standard SQL CURRENT_TIMESTAMP
. Entrambi restituiscono timestamptz
(non timestamp
!). Puoi usare entrambi.now()::date
è equivalente a CURRENT_DATE
. Entrambi dipendono dall'impostazione del fuso orario corrente.
Dovresti avere un indice del modulo:
CREATE INDEX foo ON t_el_eventlog(sourceid, eventtime)
Oppure, per consentire le scansioni solo indice:
CREATE INDEX foo2 ON t_el_eventlog(sourceid, eventtime, serialnumber)
Se operi in fusi orari diversi, le cose si complicano e dovresti usare timestamptz
per tutto.
Alternativa per timestamptz
Prima dell'aggiornamento della domanda, sembrava che i fusi orari fossero importanti. Quando hai a che fare con fusi orari diversi, "oggi" è una dipendenza funzionale del fuso orario corrente. Le persone tendono a dimenticarlo.
Per lavorare solo con l'impostazione del fuso orario corrente della sessione, utilizzare la stessa query di cui sopra. Se eseguito in un fuso orario diverso, i risultati sono in realtà errati. (Vale anche per quanto sopra.)
Per garantire un risultato corretto per un determinato fuso orario ("Europa/Berlino" nel tuo caso) indipendentemente dall'impostazione del fuso orario corrente della sessione, utilizza invece questa espressione:
((now() AT TIME ZONE 'Europe/Berlin')::date - interval '18 hours')
AT TIME ZONE 'Europe/Berlin' -- 2nd time to convert back
Tieni presente che il AT TIME ZONE
costrutto restituisce timestamp
per timestamptz
input e viceversa.
Come accennato all'inizio, tutti i dettagli cruenti qui:
- Ignora del tutto i fusi orari in Rails e PostgreSQL