Svar for timestamp
Du skal forstå arten af datatyperne timestamp
(timestamp without time zone
) og timestamptz
(timestamp with time zone
). Hvis du ikke gør det, så læs dette først:
- Ignorerer tidszoner helt i Rails og PostgreSQL
AT TIME ZONE
konstruktion transformerer et timestamp
til timestamptz
, hvilket næsten helt sikkert er det forkerte træk for dit tilfælde:
where eventtime at time zone 'CET' between '2015-06-16 06:00:00'
and '2015-06-17 06:00:00'
Først , det dræber ydeevnen. Anvender AT TIME ZONE
til kolonnen eventtime
gør udtrykket ikke sargerbart . Postgres kan ikke bruge almindelige indekser på eventtime
. Men selv uden indeks er sargerbare udtryk billigere. Juster filterværdier i stedet for at manipulere hver rækkeværdi.
Du kunne kompensere med et matchende udtryksindeks, men det er nok bare en misforståelse og forkert alligevel.
Hvad sker der i det udtryk?
-
AT TIME ZONE 'CET'
transformerertimestamp
værdieventtime
tiltimestamptz
ved at tilføje tidsforskydningen for din aktuelle tidszone. Når du bruger en tidszone navn (ikke en numerisk offset eller en forkortelse), dette tager også højde for DST regler (sommertid), så du får en anden offset for "vinter" tidsstempler. Grundlæggende får du svaret på spørgsmålet:Hvad er det tilsvarende UTC-tidsstempel for det givne tidsstempel i den givne tidszone?
Når du viser resultatet til brugeren er formateret som lokalt tidsstempel med den tilsvarende tidsforskydning for sessionens aktuelle tidszone. (Kan eller ikke være den samme som den, der bruges i udtrykket).
-
Strengliteralerne på højre side har ingen datatype til sig, så typen er afledt af tildelingen i udtrykket. Da det er
timestamptz
nu er begge castet tiltimestamptz
, forudsat den aktuelle tidszone for sessionen.Hvad er det tilsvarende UTC-tidsstempel for det givne tidsstempel for tidszoneindstillingen for den aktuelle session.
Forskydningen kan variere med DST-regler.
Lang historie kort , hvis du altid arbejde med samme tidszone:CET
eller 'Europe/Berlin'
- det samme for nutidens tidsstempler, men ikke for historiske eller (muligvis) fremtidige, du kan bare skære bunden af.
Det andet problem med udtrykket: er næsten altid forkert med BETWEEN
timestamp
værdier. Se:
- Optimer MELLEM datoerklæring
- Find overlappende datointervaller 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()
er Postgres-implementeringen af SQL-standarden CURRENT_TIMESTAMP
. Begge returnerer timestamptz
(ikke timestamp
!). Du kan bruge enten.now()::date
svarer til CURRENT_DATE
. Begge afhænger af den aktuelle tidszoneindstilling.
Du bør have et indeks af formularen:
CREATE INDEX foo ON t_el_eventlog(sourceid, eventtime)
Eller for at tillade kun indeksscanninger:
CREATE INDEX foo2 ON t_el_eventlog(sourceid, eventtime, serialnumber)
Hvis du opererer i forskellige tidszoner, bliver tingene mere komplicerede, og du bør bruge timestamptz
til alt.
Alternativ til timestamptz
Før spørgsmålsopdateringen så det ud til, at tidszoner betyder noget. Når du har at gøre med forskellige tidszoner, "i dag" er en funktionel afhængighed af den aktuelle tidszone. Folk har en tendens til at glemme det.
For kun at arbejde med den aktuelle tidszoneindstilling for sessionen, brug samme forespørgsel som ovenfor. Hvis de udføres i en anden tidszone, er resultaterne i virkeligheden forkerte. (Gælder også ovenstående.)
For at garantere et korrekt resultat for en given tidszone ('Europa/Berlin' i dit tilfælde) uanset den aktuelle tidszoneindstilling for sessionen, skal du bruge dette udtryk i stedet:
((now() AT TIME ZONE 'Europe/Berlin')::date - interval '18 hours')
AT TIME ZONE 'Europe/Berlin' -- 2nd time to convert back
Vær opmærksom på, at AT TIME ZONE
konstruktion returnerer timestamp
for timestamptz
input og omvendt.
Som nævnt i begyndelsen, alle de blodige detaljer her:
- Ignorerer tidszoner helt i Rails og PostgreSQL