Renvoyer uniquement les minutes avec activité
Le plus court
SELECT DISTINCT
date_trunc('minute', "when") AS minute
, count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM mytable
ORDER BY 1;
Utilisez date_trunc()
, il renvoie exactement ce dont vous avez besoin.
Ne pas inclure id
dans la requête, puisque vous voulez GROUP BY
tranches minute.
count()
est généralement utilisé comme fonction d'agrégat simple. Ajouter un OVER
clause en fait une fonction de fenêtre. Omettre PARTITION BY
dans la définition de la fenêtre - vous voulez un nombre cumulé sur toutes les lignes . Par défaut, cela compte de la première ligne au dernier pair de la ligne actuelle comme défini par ORDER BY
. Le manuel :
L'option de cadrage par défaut est
RANGE UNBOUNDED PRECEDING
, qui est identique àRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
. AvecORDER BY
, cela définit le cadre sur toutes les lignes depuis le début de la partition jusqu'au dernierORDER BY
de la ligne actuelle pair.
Et c'est exactement ce dont vous avez besoin.
Utilisez count(*)
plutôt que count(id)
. Cela correspond mieux à votre question ("nombre de lignes"). Il est généralement légèrement plus rapide que count(id)
. Et, bien que nous puissions supposer que id
est NOT NULL
, il n'a pas été spécifié dans la question, donc count(id)
est faux , à proprement parler, car les valeurs NULL ne sont pas comptées avec count(id)
.
Vous ne pouvez pas GROUP BY
tranches d'une minute au même niveau de requête. Les fonctions d'agrégation sont appliquées avant fonctions de fenêtre, la fonction de fenêtre count(*)
ne verrait qu'une ligne par minute de cette façon.
Vous pouvez cependant SELECT DISTINCT
, car DISTINCT
est appliqué après fonctions de fenêtre.
ORDER BY 1
est juste un raccourci pour ORDER BY date_trunc('minute', "when")
ici.1
est une référence de position à la 1ère expression dans le SELECT
liste.
Utilisez to_char()
si vous devez formater le résultat. Comme :
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");
Le plus rapide
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;
Un peu comme ci-dessus, mais :
J'utilise une sous-requête pour agréger et compter les lignes par minute. De cette façon, nous obtenons 1 ligne par minute sans DISTINCT
dans le SELECT
externe .
Utilisez sum()
en tant que fonction d'agrégation de fenêtre maintenant pour additionner les décomptes de la sous-requête.
J'ai trouvé cela beaucoup plus rapide avec de nombreuses lignes par minute.
Inclure les minutes sans activité
Le plus court
@GabiMe a demandé dans un commentaire comment obtenir une ligne pour chaque minute
dans la période, y compris celles où aucun événement ne s'est produit (pas de ligne dans la table de base) :
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;
Générez une ligne pour chaque minute dans le laps de temps entre le premier et le dernier événement avec generate_series()
- ici directement basé sur les valeurs agrégées de la sous-requête.
LEFT JOIN
à tous les horodatages tronqués à la minute et au décompte. NULL
les valeurs (où aucune ligne n'existe) ne s'ajoutent pas au nombre cumulé.
Le plus rapide
Avec 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;
Encore une fois, agrégez et comptez les lignes par minute dans la première étape, cela omet le besoin de DISTINCT
plus tard .
Différent de count()
, sum()
peut renvoyer NULL
. Par défaut à 0
avec COALESCE
.
Avec de nombreuses lignes et un index sur "when"
cette version avec une sous-requête était la plus rapide parmi quelques variantes que j'ai testées avec Postgres 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;