Odpověď na timestamp
Musíte pochopit povahu datových typů timestamp
(timestamp without time zone
) a timestamptz
(timestamp with time zone
). Pokud ne, přečtěte si nejprve toto:
- Úplné ignorování časových pásem v Rails a PostgreSQL
AT TIME ZONE
konstrukt transformuje timestamp
na timestamptz
, což je téměř jistě špatný krok pro váš případ:
where eventtime at time zone 'CET' between '2015-06-16 06:00:00'
and '2015-06-17 06:00:00'
První , zabíjí výkon. Použití AT TIME ZONE
do sloupce eventtime
způsobí, že výraz není sargable . Postgres nemůže používat prosté indexy v eventtime
. Ale i bez indexu jsou sargable výrazy levnější. Upravte hodnoty filtru namísto manipulace s každou hodnotou řádku.
To můžete kompenzovat pomocí indexu shodného výrazu, ale pravděpodobně je to jen nedorozumění a špatné.
Co se stane v tomto výrazu?
-
AT TIME ZONE 'CET'
transformujetimestamp
hodnotaeventtime
natimestamptz
připojením časového posunu vašeho aktuálního časového pásma. Při použití časového pásma name (nikoli číselný posun nebo zkratka), zohledňuje to také pravidla DST (letního času), takže pro „zimní“ časová razítka získáte jiný posun. V podstatě dostanete odpověď na otázku:Jaké je odpovídající časové razítko UTC pro dané časové razítko v daném časovém pásmu?
Při zobrazení výsledek pro uživatele je naformátován jako místní časové razítko s příslušným časovým posunem pro aktuální časové pásmo relace. (Může nebo nemusí být stejný jako výraz použitý ve výrazu).
-
Řetězcové literály na pravé straně nemají žádný datový typ, takže typ je odvozen z přiřazení ve výrazu. Protože to je
timestamptz
nyní jsou oba přetypovány natimestamptz
, za předpokladu aktuálního časového pásma relace.Jaké je odpovídající časové razítko UTC pro dané časové razítko pro nastavení časového pásma aktuální relace.
Posun se může lišit podle pravidel DST.
Krátký dlouhý příběh , pokud vždy pracovat se stejným časovým pásmem:CET
nebo 'Europe/Berlin'
- Totéž pro současná časová razítka, ale ne pro historická nebo (možná) budoucí, můžete si to prostě uříznout.
Druhý problém s výrazem: je téměř vždy špatně s BETWEEN
timestamp
hodnoty. Viz:
- Optimalizace výpisu data BETWEEN
- Najděte překrývající se období v 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()
je Postgres implementace standardu SQL CURRENT_TIMESTAMP
. Oba vrátí timestamptz
(nikoli timestamp
!). Můžete použít buď.now()::date
je ekvivalentní CURRENT_DATE
. Obojí závisí na aktuálním nastavení časového pásma.
Měli byste mít index formuláře:
CREATE INDEX foo ON t_el_eventlog(sourceid, eventtime)
Nebo chcete-li povolit skenování pouze na základě indexu:
CREATE INDEX foo2 ON t_el_eventlog(sourceid, eventtime, serialnumber)
Pokud působíte v různých časových pásmech, věci se zkomplikují a měli byste použít timestamptz
za všechno.
Alternativa pro timestamptz
Před aktualizací otázky se zdálo, že na časových pásmech záleží. Když řešíte různá časová pásma, "dnes" je funkční závislost aktuálního časového pásma. Lidé na to mají tendenci zapomínat.
Chcete-li pracovat pouze s aktuálním nastavením časového pásma relace, použijte stejný dotaz jako výše. Při spuštění v jiném časovém pásmu jsou výsledky ve skutečnosti nesprávné. (Platí také pro výše uvedené.)
Chcete-li zaručit správný výsledek pro dané časové pásmo (ve vašem případě „Evropa/Berlín“) bez ohledu na aktuální nastavení časového pásma relace, použijte místo toho tento výraz:
((now() AT TIME ZONE 'Europe/Berlin')::date - interval '18 hours')
AT TIME ZONE 'Europe/Berlin' -- 2nd time to convert back
Uvědomte si, že AT TIME ZONE
konstrukt vrací timestamp
pro timestamptz
vstup a naopak.
Jak bylo zmíněno na začátku, všechny krvavé detaily zde:
- Úplné ignorování časových pásem v Rails a PostgreSQL