Odpowiedź dla timestamp
Musisz zrozumieć naturę typów danych timestamp
(timestamp without time zone
) i timestamptz
(timestamp with time zone
). Jeśli nie, przeczytaj najpierw to:
- Całkowite ignorowanie stref czasowych w Rails i PostgreSQL
AT TIME ZONE
konstrukcja przekształca timestamp
do timestamp
, co prawie na pewno jest niewłaściwym posunięciem w Twoim przypadku:
where eventtime at time zone 'CET' between '2015-06-16 06:00:00'
and '2015-06-17 06:00:00'
Pierwszy , zabija wydajność. Stosuję AT TIME ZONE
do kolumny eventtime
sprawia, że wyrażenie nie jest sargable . Postgres nie może używać zwykłych indeksów w eventtime
. Ale nawet bez indeksu wyrażenia sargable są tańsze. Dostosuj wartości filtrów zamiast manipulować wartościami każdego wiersza.
Możesz mógł zrekompensować pasującym indeksem wyrażenia, ale prawdopodobnie jest to tylko nieporozumienie i i tak źle.
Co się dzieje w tym wyrażeniu?
-
AT TIME ZONE 'CET'
przekształcatimestamp
wartośćeventtime
dotimestamp
dołączając przesunięcie czasowe aktualnej strefy czasowej. W przypadku korzystania ze strefy czasowej nazwa (nie przesunięcie numeryczne lub skrót), uwzględnia to również reguły DST (czas letni), więc otrzymujesz inne przesunięcie dla „zimowych” znaczników czasu. Zasadniczo otrzymujesz odpowiedź na pytanie:Jaka jest odpowiednia sygnatura czasowa UTC dla podanej sygnatury czasowej w danej strefie czasowej?
Podczas wyświetlania wynik dla użytkownika jest sformatowany jako lokalny znacznik czasu z odpowiednim przesunięciem czasu dla bieżącej strefy czasowej sesji. (Może być lub nie być tym samym, co użyte w wyrażeniu).
-
Literały ciągów po prawej stronie nie mają typu danych, więc typ pochodzi z przypisania w wyrażeniu. Ponieważ to jest
timestamptz
teraz oba są rzutowane natimestamptz
, zakładając aktualną strefę czasową sesji.Jaka jest odpowiednia sygnatura czasowa UTC dla podanej sygnatury czasowej dla ustawienia strefy czasowej bieżącej sesji.
Przesunięcie może się różnić w zależności od reguł czasu letniego.
Krótkie opowiadanie , jeśli zawsze działają w tej samej strefie czasowej:CET
lub 'Europe/Berlin'
- to samo dla dzisiejszych znaczników czasu, ale nie dla historycznych lub (ewentualnie) przyszłych, możesz po prostu pominąć.
Drugi problem z wyrażeniem: jest prawie zawsze błędny z BETWEEN
timestamp
wartości. Zobacz:
- Optymalizuj BETWEEN zestawienie daty
- Znajdź nakładające się zakresy dat w 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()
jest implementacją Postgres standardu SQL CURRENT_TIMESTAMP
. Oba zwracają timestamptz
(nie timestamptz
!). Możesz użyć obu.now()::date
jest odpowiednikiem CURRENT_DATE
. Oba zależą od aktualnego ustawienia strefy czasowej.
Powinieneś mieć indeks postaci:
CREATE INDEX foo ON t_el_eventlog(sourceid, eventtime)
Lub, aby zezwolić na skanowanie tylko indeksu:
CREATE INDEX foo2 ON t_el_eventlog(sourceid, eventtime, serialnumber)
Jeśli działasz w różnych strefach czasowych, sprawy stają się bardziej skomplikowane i powinieneś użyć timestamptz
za wszystko.
Alternatywa dla timestamp
Przed aktualizacją pytania wydawało się, że strefy czasowe mają znaczenie. W przypadku różnych stref czasowych „dzisiaj” jest funkcjonalną zależnością od aktualnej strefy czasowej. Ludzie zwykle o tym zapominają.
Aby po prostu pracować z bieżącym ustawieniem strefy czasowej sesji, użyj tego samego zapytania, co powyżej. W przypadku wykonania w innej strefie czasowej wyniki są w rzeczywistości błędne. (Dotyczy również powyższego.)
Aby zagwarantować poprawny wynik dla danej strefy czasowej (w Twoim przypadku „Europa/Berlin”) niezależnie od bieżącego ustawienia strefy czasowej sesji, użyj tego wyrażenia:
((now() AT TIME ZONE 'Europe/Berlin')::date - interval '18 hours')
AT TIME ZONE 'Europe/Berlin' -- 2nd time to convert back
Pamiętaj, że AT TIME ZONE
konstrukcja zwraca timestamp
dla timestamp
wejście i odwrotnie.
Jak wspomniano na wstępie, wszystkie krwawe szczegóły tutaj:
- Całkowite ignorowanie stref czasowych w Rails i PostgreSQL