Zwróć tylko minuty z aktywnością
Najkrótszy
SELECT DISTINCT
date_trunc('minute', "when") AS minute
, count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM mytable
ORDER BY 1;
Użyj date_trunc() , zwraca dokładnie to, czego potrzebujesz.
Nie dołączaj id w zapytaniu, ponieważ chcesz GROUP BY minutowe plasterki.
count() jest zwykle używany jako zwykła funkcja agregująca. Dołączanie OVER klauzula sprawia, że jest to funkcja okna. Pomiń PARTITION BY w definicji okna - chcesz mieć bieżącą liczbę na wszystkich wierszach . Domyślnie liczy się to od pierwszego wiersza do ostatniego elementu równorzędnego w bieżącym wierszu, zgodnie z definicją ORDER BY . Instrukcja:
Domyślną opcją kadrowania jest RANGE UNBOUNDED PRECEDING , który jest taki sam jak RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW . Z ORDER BY , ustawia to ramkę na wszystkie wiersze od partycji, począwszy od ostatniego ORDER BY bieżącego wiersza równorzędny.
I tak się składa dokładnie czego potrzebujesz.
Użyj count(*) zamiast count(id) . Lepiej pasuje do Twojego pytania („liczba wierszy”). Zwykle jest nieco szybszy niż count(id) . I chociaż możemy założyć, że id jest NOT NULL , nie zostało to określone w pytaniu, więc count(id) jest nieprawidłowa , ściśle mówiąc, ponieważ wartości NULL nie są liczone za pomocą count(id) .
Nie możesz GROUP BY wycinki minutowe na tym samym poziomie zapytania. Funkcje agregujące są stosowane przed funkcje okna, funkcja okna count(*) widziałby w ten sposób tylko 1 wiersz na minutę.
Możesz jednak SELECT DISTINCT , ponieważ DISTINCT jest stosowany po funkcje okna.
ORDER BY 1 to po prostu skrót dla ORDER BY date_trunc('minute', "when") tutaj.1 jest referencją pozycyjną do pierwszego wyrażenia w SELECT lista.
Użyj to_char() jeśli potrzebujesz sformatować wynik. Na przykład:
SELECT DISTINCT
to_char(date_trunc('minute', "when"), 'DD.MM.YYYY HH24:MI') AS minute
, count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM mytable
ORDER BY date_trunc('minute', "when");
Najszybszy
SELECT minute, sum(minute_ct) OVER (ORDER BY minute) AS running_ct
FROM (
SELECT date_trunc('minute', "when") AS minute
, count(*) AS minute_ct
FROM tbl
GROUP BY 1
) sub
ORDER BY 1;
Podobnie jak powyżej, ale:
Używam podzapytania do agregowania i liczenia wierszy na minutę. W ten sposób otrzymujemy 1 wiersz na minutę bez DISTINCT w zewnętrznym SELECT .
Użyj sum() jako funkcja agregująca okna, aby dodać liczniki z podzapytania.
Odkryłem, że jest to znacznie szybsze przy wielu rzędach na minutę.
Uwzględnij minuty bez aktywności
Najkrótszy
@GabiMe zapytał w komentarzu, jak uzyskać jeden wiersz dla każdego minute w ramach czasowych, w tym tych, w których nie wystąpiło żadne zdarzenie (brak wiersza w tabeli bazowej):
SELECT DISTINCT
minute, count(c.minute) OVER (ORDER BY minute) AS running_ct
FROM (
SELECT generate_series(date_trunc('minute', min("when"))
, max("when")
, interval '1 min')
FROM tbl
) m(minute)
LEFT JOIN (SELECT date_trunc('minute', "when") FROM tbl) c(minute) USING (minute)
ORDER BY 1;
Generuj wiersz dla każdej minuty w przedziale czasowym między pierwszym a ostatnim zdarzeniem za pomocą generate_series() - tutaj bezpośrednio na podstawie zagregowanych wartości z podzapytania.
LEFT JOIN do wszystkich znaczników czasu obciętych do minuty i liczenia. NULL wartości (gdzie nie istnieje żaden wiersz) nie są dodawane do bieżącej liczby.
Najszybszy
Z CTE:
WITH cte AS (
SELECT date_trunc('minute', "when") AS minute, count(*) AS minute_ct
FROM tbl
GROUP BY 1
)
SELECT m.minute
, COALESCE(sum(cte.minute_ct) OVER (ORDER BY m.minute), 0) AS running_ct
FROM (
SELECT generate_series(min(minute), max(minute), interval '1 min')
FROM cte
) m(minute)
LEFT JOIN cte USING (minute)
ORDER BY 1;
Ponownie, agreguj i licz wiersze na minutę w pierwszym kroku, pomija to potrzebę późniejszego DISTINCT .
Różni się od count() , sum() może zwrócić NULL . Domyślnie 0 z COALESCE .
Z wieloma wierszami i indeksem "when" ta wersja z podzapytaniem była najszybsza spośród kilku wariantów, które testowałem z Postgresem 9.1 - 9.4:
SELECT m.minute
, COALESCE(sum(c.minute_ct) OVER (ORDER BY m.minute), 0) AS running_ct
FROM (
SELECT generate_series(date_trunc('minute', min("when"))
, max("when")
, interval '1 min')
FROM tbl
) m(minute)
LEFT JOIN (
SELECT date_trunc('minute', "when") AS minute
, count(*) AS minute_ct
FROM tbl
GROUP BY 1
) c USING (minute)
ORDER BY 1;