Đếm tất cả hàng
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
-
Các cột của bạn
week_beg
vàmonth_beg
100% dư thừa và có thể dễ dàng thay thế bằngdate_trunc('week', date + 1) - 1
vàdate_trunc('month', date)
tương ứng. -
Tuần của bạn dường như bắt đầu vào Chủ nhật (giảm từng người một), do đó
+ 1 .. - 1
. -
khung mặc định của hàm cửa sổ với
ORDER BY
trongOVER
mệnh đề sử dụng làRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
. Đó chính xác là những gì bạn cần. -
Sử dụng
UNION ALL
, không phảiUNION
. -
Sự lựa chọn đáng tiếc của bạn cho
time_series
(D, W, M) không được sắp xếp tốt, tôi đã đổi tên để tạoORDER BY
cuối cùng dễ dàng hơn. -
Truy vấn này có thể xử lý nhiều hàng mỗi ngày. Số lượng bao gồm tất cả các đồng nghiệp trong một ngày.
-
Tìm hiểu thêm về
DISTINCT ON
:
DISTINCT người dùng mỗi ngày
Để đếm mọi người dùng chỉ một lần mỗi ngày, hãy sử dụng CTE với 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
DISTINCT người dùng trong khoảng thời gian động
Bạn luôn có thể sử dụng truy vấn phụ tương quan . Có xu hướng chậm với các bảng lớn!
Xây dựng dựa trên các truy vấn trước đó:
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 cho cả ba giải pháp.
Nhanh hơn với dense_rank()
@Clodoaldo
đã đưa ra một cải tiến lớn:sử dụng chức năng cửa sổ dense_rank()
. Đây là một ý tưởng khác cho một phiên bản được tối ưu hóa. Việc loại trừ các bản sao hàng ngày ngay lập tức sẽ nhanh hơn. Hiệu suất tăng theo số hàng mỗi ngày.
Xây dựng dựa trên mô hình dữ liệu được đơn giản hóa và hoàn thiện - không có cột thừa- day
dưới dạng tên cột thay vì day
day
là một từ dành riêng trong SQL chuẩn
và tên kiểu cơ bản trong PostgreSQL và không nên được sử dụng làm định danh.
CREATE TABLE uniques(
day date -- instead of "date"
,user_id int
);
Truy vấn được cải thiện:
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
thể hiện hiệu suất của 4 biến thể nhanh hơn. Điều này phụ thuộc vào việc phân phối dữ liệu của bạn để nhanh nhất cho bạn.
Tất cả chúng đều nhanh hơn khoảng 10 lần so với phiên bản truy vấn con tương quan (điều này không tệ đối với các truy vấn con tương quan).