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;