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

Най-добрият начин за преброяване на записи на произволни интервали от време в Rails+Postgres

За щастие използвате PostgreSQL. Прозоречната функция generate_series() е твой приятел.

Тестов случай

Като се има предвид следната тестова таблица (която вие трябваше да предостави):

CREATE TABLE event(event_id serial, ts timestamp);
INSERT INTO event (ts)
SELECT generate_series(timestamp '2018-05-01'
                     , timestamp '2018-05-08'
                     , interval '7 min') + random() * interval '7 min';

Едно събитие на всеки 7 минути (плюс 0 до 7 минути, произволно).

Основно решение

Тази заявка отчита събития за произволен интервал от време. 17 минути в примера:

WITH grid AS (
   SELECT start_time
        , lead(start_time, 1, 'infinity') OVER (ORDER BY start_time) AS end_time
   FROM  (
      SELECT generate_series(min(ts), max(ts), interval '17 min') AS start_time
      FROM   event
      ) sub
   )
SELECT start_time, count(e.ts) AS events
FROM   grid       g
LEFT   JOIN event e ON e.ts >= g.start_time
                   AND e.ts <  g.end_time
GROUP  BY start_time
ORDER  BY start_time;
  • Заявката извлича минимални и максимални ts от основната таблица, за да покрие целия времеви диапазон. Вместо това можете да използвате произволен времеви диапазон.

  • Предоставетевсяко времеви интервал според нуждите.

  • Създава един ред за всеки времеви слот. Ако не се случи събитие през този интервал, броят е 0 .

  • Не забравяйте да се справите с горната и долната граница правилно:

    • Неочаквани резултати от SQL заявка с МЕЖДУ времеви марки
  • Прозоречната функция lead() има често пренебрегвана функция:може да предостави по подразбиране, когато не съществува водещ ред. Предоставяне на 'infinity' в примера. В противен случай последният интервал ще бъде отрязан с горна граница NULL .

Минимален еквивалент

Горната заявка използва CTE и lead() и многословен синтаксис. Елегантно и може би по-лесно за разбиране, но малко по-скъпо. Ето по-кратка, по-бърза, минимална версия:

SELECT start_time, count(e.ts) AS events
FROM  (SELECT generate_series(min(ts), max(ts), interval '17 min') FROM event) g(start_time)
LEFT   JOIN event e ON e.ts >= g.start_time
                   AND e.ts <  g.start_time + interval '17 min'
GROUP  BY 1
ORDER  BY 1;

Пример за "на всеки 15 минути през последната седмица"`

И форматиране с to_char() .

SELECT to_char(start_time, 'YYYY-MM-DD HH24:MI'), count(e.ts) AS events
FROM   generate_series(date_trunc('day', localtimestamp - interval '7 days')
                     , localtimestamp
                     , interval '15 min') g(start_time)
LEFT   JOIN event e ON e.ts >= g.start_time
                   AND e.ts <  g.start_time + interval '15 min'
GROUP  BY start_time
ORDER  BY start_time;

Все още ORDER BY и GROUP BY върху основната стойност на времевата марка , а не на форматирания низ. Това е по-бързо и по-надеждно.

db<>цигулка тук

Свързан отговор, генериращ текущ брой във времевия период:

  • 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. Трябва ли да посоча както INDEX, така и UNIQUE INDEX?

  2. Как да направите динамични изявления, подготвени за postgres в PHP

  3. Как работи Round() в PostgreSQL

  4. SpringBoot+Kotlin+Postgres и JSONB:org.hibernate.MappingException:Няма диалектно съпоставяне за JDBC тип

  5. Как да конфигурирам postgresql за първи път?