Отговор за timestamp
Трябва да разберете естеството на типовете данни timestamp
(timestamp without time zone
) и timestamptz
(timestamp with time zone
). Ако не го направите, прочетете първо това:
- Изцяло игнориране на часовите зони в Rails и PostgreSQL
AT TIME ZONE
construct трансформира timestamp
до timestamptz
, което почти сигурно е грешният ход за вашия случай:
where eventtime at time zone 'CET' between '2015-06-16 06:00:00'
and '2015-06-17 06:00:00'
Първо , това убива производителността. Прилага се AT TIME ZONE
към колоната eventtime
прави изразът не подлежащ на саргиране . Postgres не може да използва обикновени индекси за eventtime
. Но дори и без индекс, sargable изразите са по-евтини. Коригирайте стойностите на филтъра, вместо да манипулирате стойностите на всеки ред.
Можете можете компенсира със съвпадащ индекс на израз, но вероятно това е просто недоразумение и така или иначе.
Какво се случва в този израз?
-
AT TIME ZONE 'CET'
трансформираtimestamp
стойностeventtime
доtimestamptz
като добавите часовото отместване на текущата ви часова зона. Когато използвате име на часова зона (не е числово изместване или съкращение), това също взема предвид правилата за DST (лятно часово време), така че получавате различно изместване за "зимни" часови маркировки. По принцип получавате отговора на въпроса:Какво е съответното времево клеймо UTC за дадената времева марка в дадена часова зона?
При показване резултатът за потребителя се форматира като местно времеви печат със съответното времево отместване за текущата часова зона на сесията. (Може или не може да е същото като използваното в израза).
-
Низовите литерали от дясната страна нямат тип данни за тях, така че типът се извлича от присвояването в израза. Тъй като това е
timestamptz
сега и двете са прехвърлени къмtimestamptz
, като се приеме текущата часова зона на сесията.Какво е съответното UTC клеймо за дадено време за настройката на часовата зона на текущата сесия.
Отместването може да варира в зависимост от DST правилата.
Накратко , ако винаги работят със същата часова зона:CET
или 'Europe/Berlin'
- същото нещо за днешните времеви марки, но не и за исторически или (евентуално) бъдещи такива, можете просто да намалите границата.
Вторият проблем с израза: почти винаги е грешно с BETWEEN
timestamp
стойности. Вижте:
- Оптимизиране на изявление за дата BETWEEN
- Намерете припокриващи се периоди от време в 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()
е реализацията на Postgres на SQL стандарта CURRENT_TIMESTAMP
. И двете връщат timestamptz
(не timestamp
!). Можете да използвате едно от двете.now()::date
е еквивалентен на CURRENT_DATE
. И двете зависят от текущата настройка на часовата зона.
Трябва да имате индекс от формата:
CREATE INDEX foo ON t_el_eventlog(sourceid, eventtime)
Или, за да разрешите сканиране само за индекс:
CREATE INDEX foo2 ON t_el_eventlog(sourceid, eventtime, serialnumber)
Ако работите в различни часови зони, нещата стават по-сложни и трябва да използвате timestamptz
за всичко.
Алтернатива за timestamptz
Преди актуализацията на въпроса изглеждаше, че часовите зони имат значение. Когато работите с различни часови зони, „днес“ е функционална зависимост на текущата часова зона. Хората са склонни да забравят това.
За да работите само с текущата настройка на часовата зона на сесията, използвайте същата заявка, както по-горе. Ако се изпълни в различна часова зона, резултатите са грешни в действителност. (Важи и за горното.)
За да гарантирате правилен резултат за дадена часова зона („Европа/Берлин“ във вашия случай), независимо от текущата настройка на часовата зона на сесията, използвайте този израз вместо това:
((now() AT TIME ZONE 'Europe/Berlin')::date - interval '18 hours')
AT TIME ZONE 'Europe/Berlin' -- 2nd time to convert back
Имайте предвид, че AT TIME ZONE
construct връща timestamp
за timestamptz
въвеждане и обратно.
Както бе споменато в началото, всички кървави подробности тук:
- Изцяло игнориране на часовите зони в Rails и PostgreSQL