J'utilise count
et group by
pour obtenir le nombre d'abonnés enregistrés chaque jour:
SELECT created_at, COUNT(email)
FROM subscriptions
GROUP BY created at;
Résultat:
created_at count
-----------------
04-04-2011 100
05-04-2011 50
06-04-2011 50
07-04-2011 300
Je souhaite plutôt obtenir le total cumulé des abonnés chaque jour. Comment est-ce que j'obtiens ceci?
created_at count
-----------------
04-04-2011 100
05-04-2011 150
06-04-2011 200
07-04-2011 500
Avec des jeux de données plus volumineux, les fonctions de la fenêtre sont le moyen le plus efficace d'effectuer ce type de requêtes - le tableau sera analysé uniquement une fois, au lieu d'une fois pour chaque date, comme le ferait une auto-jointure. Cela semble également beaucoup plus simple. :) PostgreSQL 8.4 et versions ultérieures prennent en charge les fonctions de fenêtre.
Voici à quoi ça ressemble:
SELECT created_at, sum(count(email)) OVER (ORDER BY created_at)
FROM subscriptions
GROUP BY created_at;
Ici OVER
crée la fenêtre; ORDER BY created_at
Signifie qu'il doit additionner les chiffres dans l'ordre de created_at
.
Modifier: Si vous souhaitez supprimer les e-mails en double en une seule journée, vous pouvez utiliser sum(count(distinct email))
. Malheureusement, cela ne supprimera pas les doublons qui traversent des dates différentes.
Si vous souhaitez supprimer tous les doublons, je pense que le plus simple est d'utiliser une sous-requête et DISTINCT ON
. Cela attribuera les e-mails à leur date la plus ancienne (car je trie par created_at dans l'ordre croissant, il choisira le plus ancien):
SELECT created_at, sum(count(email)) OVER (ORDER BY created_at)
FROM (
SELECT DISTINCT ON (email) created_at, email
FROM subscriptions ORDER BY email, created_at
) AS subq
GROUP BY created_at;
Si vous créez un index sur (email, created_at)
, Cette requête ne devrait pas non plus être trop lente.
(Si vous souhaitez tester, voici comment j'ai créé l'exemple de jeu de données)
create table subscriptions as
select date '2000-04-04' + (i/10000)::int as created_at,
'[email protected]' || (i%700000)::text as email
from generate_series(1,1000000) i;
create index on subscriptions (email, created_at);
Utilisation:
SELECT a.created_at,
(SELECT COUNT(b.email)
FROM SUBSCRIPTIONS b
WHERE b.created_at <= a.created_at) AS count
FROM SUBSCRIPTIONS a
SELECT
s1.created_at,
COUNT(s2.email) AS cumul_count
FROM subscriptions s1
INNER JOIN subscriptions s2 ON s1.created_at >= s2.created_at
GROUP BY s1.created_at
Je suppose que vous ne voulez qu'une seule ligne par jour et que vous souhaitez toujours afficher les jours sans abonnement (supposons que personne ne s'abonne pour une certaine date, voulez-vous afficher cette date avec le solde de la veille?). Si tel est le cas, vous pouvez utiliser la fonction 'avec':
with recursive serialdates(adate) as (
select cast('2011-04-04' as date)
union all
select adate + 1 from serialdates where adate < cast('2011-04-07' as date)
)
select D.adate,
(
select count(distinct email)
from subscriptions
where created_at between date_trunc('month', D.adate) and D.adate
)
from serialdates D