Каков самый быстрый способ обрезать временные метки до 5 минут в Postgres?

Postgres может округлять (сокращать) временные метки, используя функцию date_trunc, например:

date_trunc('hour', val)
date_trunc('minute', val)

Я ищу способ обрезать временную метку до ближайшей 5-минутной границы, так что, например, 14:26:57 становится 14:25:00. Прямой способ сделать это выглядит следующим образом:

date_trunc('hour', val) + date_part('minute', val)::int / 5 * interval '5 min'

Поскольку это критически важная часть запроса, мне интересно, является ли это самым быстрым решением, или есть ли какой-либо ярлык (совместимый с Postgres 8.1+), который я упустил.

Ответ 1

Я не думаю, что есть более быстрый метод.

И я не думаю, что вы должны беспокоиться о производительности выражения.

Все, что связано с выполнением вашего (SELECT, UPDATE,...) оператора, скорее всего, намного дороже (например, ввод-вывод для извлечения строк), чем этот расчет даты/времени.

Ответ 2

Я думал о том же самом. Я нашел два альтернативных способа сделать это, но тот, который вы предложили, был быстрее.

Я неофициально сравнил один из наших больших столов. Я ограничил запрос первыми 4 миллионами строк. Я чередовал два запроса, чтобы избежать несправедливого преимущества из-за кеширования БД.


Проходя через эпоху/юникс

SELECT to_timestamp(
    floor(EXTRACT(epoch FROM ht.time) / EXTRACT(epoch FROM interval '5 min'))
    * EXTRACT(epoch FROM interval '5 min')
) FROM huge_table AS ht LIMIT 4000000

(обратите внимание, что это выдает timestamptz, даже если вы использовали тип данных без часового пояса)

Результаты

  • Прогон 1: 39,368 секунд
  • Прогон 3: 39,526 секунд
  • Прогон 5: 39,883 секунды

Использование date_trunc и date_part

SELECT 
    date_trunc('hour', ht.time) 
    + date_part('minute', ht.time)::int / 5 * interval '5 min'
FROM huge_table AS ht LIMIT 4000000

Результаты

  • Прогон 2: 34,189 секунды
  • Прогон 4: 37,028 секунд
  • Прогон 6: 32,397 секунды

система

  • Версия БД: PostgreSQL 9.6.2 для x86_64-pc-linux-gnu, скомпилированная gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2, 64-битная версия
  • Ядра: Intel® Xeon®, E5-1650v2, Hexa-Core
  • Оперативная память: 64 ГБ, оперативная память DDR3 ECC

Заключение

Ваша версия кажется быстрее. Но не достаточно быстро для моего конкретного случая использования. Преимущество отсутствия указания часа делает версию эпохи более универсальной и упрощает параметризацию в коде на стороне клиента. Он обрабатывает интервалы 2 hour так же, как и интервалы 5 minute, без необходимости увеличивать аргумент единицы времени date_trunc. В заключение я хотел бы, чтобы вместо этого аргумент единицы времени был изменен на аргумент временного интервала.

Ответ 3

Полный запрос для интересующихся (на основе вопроса @DNS):

Предполагая, что у вас есть заказы, и вы хотите подсчитать их на кусочки 5 минут и shop_id:

SELECT date_trunc('hour', created_at) + date_part('minute', created_at)::int / 5 * interval '5 min' AS minute
      , shop_id, count(id) as orders_count
FROM orders
GROUP BY 1, shop_id
ORDER BY 1 ASC