PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Извадете часовете от функцията now().

Отговор за 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 изразите са по-евтини. Коригирайте стойностите на филтъра, вместо да манипулирате стойностите на всеки ред.
Можете можете компенсира със съвпадащ индекс на израз, но вероятно това е просто недоразумение и така или иначе.

Какво се случва в този израз?

  1. AT TIME ZONE 'CET' трансформира timestamp стойност eventtime до timestamptz като добавите часовото отместване на текущата ви часова зона. Когато използвате име на часова зона (не е числово изместване или съкращение), това също взема предвид правилата за DST (лятно часово време), така че получавате различно изместване за "зимни" часови маркировки. По принцип получавате отговора на въпроса:

    Какво е съответното времево клеймо UTC за дадената времева марка в дадена часова зона?

    При показване резултатът за потребителя се форматира като местно времеви печат със съответното времево отместване за текущата часова зона на сесията. (Може или не може да е същото като използваното в израза).

  2. Низовите литерали от дясната страна нямат тип данни за тях, така че типът се извлича от присвояването в израза. Тъй като това е 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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да намерите таблица с конкретна колона в postgresql

  2. Как да използвате Joda-Time с java.sql.Timestamp

  3. ГРЕШКА в PostgreSQL:функция to_tsvector(променлив знак, неизвестен) не съществува

  4. Настройка на външен ключ с различен тип данни

  5. Как да сравним текущия ред със следващия и предишния ред в PostgreSQL?