Преброяване на всички редове
SELECT date, '1_D' AS time_series, count(DISTINCT user_id) AS cnt
FROM uniques
GROUP BY 1
UNION ALL
SELECT DISTINCT ON (1)
date, '2_W', count(*) OVER (PARTITION BY week_beg ORDER BY date)
FROM uniques
UNION ALL
SELECT DISTINCT ON (1)
date, '3_M', count(*) OVER (PARTITION BY month_beg ORDER BY date)
FROM uniques
ORDER BY 1, time_series
-
Вашите колони
week_beg
иmonth_beg
са 100 % излишни и могат лесно да бъдат заменени сdate_trunc('week', date + 1) - 1
иdate_trunc('month', date)
съответно. -
Седмицата ви изглежда започва в неделя (отстъпка с едно), следователно
+ 1 .. - 1
. -
кадър по подразбиране на функция за прозорец с
ORDER BY
вOVER
използваната клауза еRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
. Точно това ви трябва. -
Използвайте
UNION ALL
, а неUNION
. -
Вашият неудачен избор за
time_series
(D, W, M) не сортира добре, преименувах, за да направя крайнияORDER BY
по-лесно. -
Тази заявка може да обработва няколко реда на ден. Преброяването включва всички връстници за един ден.
-
Повече за
DISTINCT ON
:
ОТДЕЛНИ потребители на ден
За да броите всеки потребител само веднъж на ден, използвайте CTE с DISTINCT ON
:
WITH x AS (SELECT DISTINCT ON (1,2) date, user_id FROM uniques)
SELECT date, '1_D' AS time_series, count(user_id) AS cnt
FROM x
GROUP BY 1
UNION ALL
SELECT DISTINCT ON (1)
date, '2_W'
,count(*) OVER (PARTITION BY (date_trunc('week', date + 1)::date - 1)
ORDER BY date)
FROM x
UNION ALL
SELECT DISTINCT ON (1)
date, '3_M'
,count(*) OVER (PARTITION BY date_trunc('month', date) ORDER BY date)
FROM x
ORDER BY 1, 2
ОТДЕЛНИ потребители за динамичен период от време
Винаги можете да прибегнете до свързани подзаявки . Склонни да бъдат бавни с големи маси!
Надграждане на предишните заявки:
WITH du AS (SELECT date, user_id FROM uniques GROUP BY 1,2)
,d AS (
SELECT date
,(date_trunc('week', date + 1)::date - 1) AS week_beg
,date_trunc('month', date)::date AS month_beg
FROM uniques
GROUP BY 1
)
SELECT date, '1_D' AS time_series, count(user_id) AS cnt
FROM du
GROUP BY 1
UNION ALL
SELECT date, '2_W', (SELECT count(DISTINCT user_id) FROM du
WHERE du.date BETWEEN d.week_beg AND d.date )
FROM d
GROUP BY date, week_beg
UNION ALL
SELECT date, '3_M', (SELECT count(DISTINCT user_id) FROM du
WHERE du.date BETWEEN d.month_beg AND d.date)
FROM d
GROUP BY date, month_beg
ORDER BY 1,2;
SQL Fiddle и за трите решения.
По-бързо с dense_rank()
@Clodoaldo
излезе със значително подобрение:използвайте прозоречната функция dense_rank()
. Ето още една идея за оптимизирана версия. Трябва да е още по-бързо, за да изключите ежедневните дубликати веднага. Увеличаването на производителността нараства с броя на редовете на ден.
Надграждане на опростен и дезинфекциран модел на данни - без излишните колони - day
като име на колона вместо date
date
е запазена дума в стандартен SQL
и име на основен тип в PostgreSQL и не трябва да се използва като идентификатор.
CREATE TABLE uniques(
day date -- instead of "date"
,user_id int
);
Подобрена заявка:
WITH du AS (
SELECT DISTINCT ON (1, 2)
day, user_id
,date_trunc('week', day + 1)::date - 1 AS week_beg
,date_trunc('month', day)::date AS month_beg
FROM uniques
)
SELECT day, count(user_id) AS d, max(w) AS w, max(m) AS m
FROM (
SELECT user_id, day
,dense_rank() OVER(PARTITION BY week_beg ORDER BY user_id) AS w
,dense_rank() OVER(PARTITION BY month_beg ORDER BY user_id) AS m
FROM du
) s
GROUP BY day
ORDER BY day;
SQL Fiddle
демонстрирайки производителността на 4 по-бързи варианта. Зависи от вашето разпространение на данни кое е най-бързо за вас.
Всички те са около 10 пъти по-бързи от версията на корелирани подзаявки (което не е лошо за корелирани подзаявки).