Svar för timestamp
Du måste förstå arten av datatyperna timestamp
(timestamp without time zone
) och timestamptz
(timestamp with time zone
). Om du inte gör det, läs detta först:
- Ignorerar tidszoner helt och hållet i Rails och PostgreSQL
AT TIME ZONE
konstruktionen transformerar en timestamp
till timestamptz
, vilket nästan säkert är fel drag för ditt fall:
where eventtime at time zone 'CET' between '2015-06-16 06:00:00'
and '2015-06-17 06:00:00'
Först , det dödar prestandan. Använder AT TIME ZONE
till kolumnen eventtime
gör uttrycket inte sargbart . Postgres kan inte använda vanliga index på eventtime
. Men även utan index är sargbara uttryck billigare. Justera filtervärden istället för att manipulera varje radvärde.
Du kunde kompensera med ett matchande uttrycksindex, men det är förmodligen bara ett missförstånd och fel ändå.
Vad händer i det uttrycket?
-
AT TIME ZONE 'CET'
omvandlartimestamp
värdeeventtime
tilltimestamptz
genom att lägga till tidsförskjutningen för din nuvarande tidszon. När du använder en tidszon namn (inte en numerisk offset eller en förkortning), detta tar även hänsyn till DST-regler (sommartid), så du får en annan offset för "vinter" tidsstämplar. I grund och botten får du svaret på frågan:Vad är motsvarande UTC-tidsstämpel för den givna tidsstämpeln i den givna tidszonen?
När du visar resultatet till användaren formateras som lokal tidsstämpel med motsvarande tidsförskjutning för sessionens aktuella tidszon. (Kan eller kanske inte är samma som den som används i uttrycket).
-
Strängliteralerna på höger sida har ingen datatyp, så typen härleds från tilldelningen i uttrycket. Eftersom det är
timestamptz
nu castas båda tilltimestamptz
, antar sessionens aktuella tidszon.Vad är motsvarande UTC-tidsstämpel för den givna tidsstämpeln för tidszonsinställningen för den aktuella sessionen.
Offsetet kan variera med sommarreglerna.
Lång historia kort , om du alltid arbeta med samma tidszon:CET
eller 'Europe/Berlin'
- Samma sak för dagens tidsstämplar, men inte för historiska eller (eventuellt) framtida, du kan bara klippa av tyget.
Det andra problemet med uttrycket: är nästan alltid fel med BETWEEN
timestamp
värden. Se:
- Optimera MELLAN datumuppgift
- Hitta överlappande datumintervall i 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()
är Postgres-implementeringen av SQL-standarden CURRENT_TIMESTAMP
. Båda returnerar timestamptz
(inte timestamp
!). Du kan använda antingen.now()::date
motsvarar CURRENT_DATE
. Båda beror på den aktuella tidszonsinställningen.
Du bör ha ett index av formuläret:
CREATE INDEX foo ON t_el_eventlog(sourceid, eventtime)
Eller, för att tillåta endast index-skanningar:
CREATE INDEX foo2 ON t_el_eventlog(sourceid, eventtime, serialnumber)
Om du arbetar i olika tidszoner blir saker och ting mer komplicerade och du bör använda timestamptz
för allt.
Alternativ för timestamptz
Innan frågeuppdateringen verkade det som om tidszoner spelar roll. När du har att göra med olika tidszoner, "idag" är ett funktionellt beroende av den aktuella tidszonen. Folk tenderar att glömma det.
För att bara arbeta med den aktuella tidszonsinställningen för sessionen, använd samma fråga som ovan. Om de körs i en annan tidszon är resultaten felaktiga i verkligheten. (Gäller även ovanstående.)
För att garantera ett korrekt resultat för en given tidszon ('Europa/Berlin' i ditt fall) oavsett den aktuella tidszonsinställningen för sessionen, använd istället detta uttryck:
((now() AT TIME ZONE 'Europe/Berlin')::date - interval '18 hours')
AT TIME ZONE 'Europe/Berlin' -- 2nd time to convert back
Tänk på att AT TIME ZONE
konstruktion returnerar timestamp
för timestamptz
input och vice versa.
Som nämndes i början, alla blodiga detaljer här:
- Ignorerar tidszoner helt och hållet i Rails och PostgreSQL