PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Najlepszy sposób na liczenie rekordów w dowolnych odstępach czasu w Rails+Postgres

Na szczęście używasz PostgreSQL. Funkcja okna generate_series() jest twoim przyjacielem.

Przypadek testowy

Biorąc pod uwagę następującą tabelę testową (którą ty powinien był podać):

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';

Jedno wydarzenie na każde 7 minut (plus od 0 do 7 minut, losowo).

Podstawowe rozwiązanie

To zapytanie zlicza zdarzenia w dowolnym przedziale czasu. 17 minut w przykładzie:

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;
  • Zapytanie pobiera minimalny i maksymalny ts z tabeli podstawowej, aby objąć cały zakres czasu. Zamiast tego możesz użyć dowolnego zakresu czasu.

  • Podaj dowolne przedział czasu w razie potrzeby.

  • Tworzy jeden wiersz dla każdego przedział czasu. Jeśli w tym okresie nie wydarzyło się żadne zdarzenie, liczba wynosi 0 .

  • Pamiętaj, aby poradzić sobie z górną i dolną granicą poprawnie:

    • Nieoczekiwane wyniki zapytania SQL ze znacznikami czasu BETWEEN
  • Funkcja okna lead() ma często pomijaną funkcję:może zapewnić wartość domyślną, gdy nie istnieje żaden wiersz wiodący. Zapewnienie 'infinity' w przykładzie. W przeciwnym razie ostatni interwał zostałby odcięty górną granicą NULL .

Minimalny odpowiednik

Powyższe zapytanie używa CTE i lead() i pełna składnia. Elegancki i może łatwiejszy do zrozumienia, ale nieco droższy. Oto krótsza, szybsza i minimalna wersja:

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;

Przykład „co 15 minut w ostatnim tygodniu”

I formatowanie za pomocą 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;

Nadal ORDER BY i GROUP BY na podstawie sygnatury czasowej wartość , a nie na sformatowanym ciągu. To jest szybsze i bardziej niezawodne.

db<>graj tutaj

Powiązana odpowiedź dająca działającą liczbę w ramach czasowych:

  • PostgreSQL:liczenie wierszy dla zapytania „po minucie”



  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 - psql \i :jak wykonać skrypt w podanej ścieżce

  2. Django, mod_wsgi, psycopg2 Nieprawidłowo skonfigurowane:Błąd podczas ładowania modułu psycopg2:Brak modułu o nazwie _psycopg

  3. właściwa adnotacja hibernacji dla byte[]

  4. Dodaj dni do daty w PostgreSQL

  5. Idź postgresql LIKE zapytania