सभी गिनें पंक्तियाँ
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
। -
UNION ALL
का उपयोग करें , नहींUNION
। -
time_series
. के लिए आपका दुर्भाग्यपूर्ण विकल्प (डी, डब्ल्यू, एम) अच्छी तरह से सॉर्ट नहीं करता है, मैंने अंतिमORDER BY
बनाने के लिए नाम बदल दिया है आसान। -
यह क्वेरी प्रति दिन कई पंक्तियों से निपट सकती है। गणना में एक दिन के लिए सभी साथियों को शामिल किया जाता है।
-
DISTINCT ON
. के बारे में अधिक जानकारी :
प्रति दिन DISTINCT उपयोगकर्ता
प्रत्येक उपयोगकर्ता को प्रति दिन केवल एक बार गिनने के लिए, 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
DISTINCT उपयोगकर्ता गतिशील समयावधि में
आप हमेशा सहसंबंधित उपश्रेणियों का सहारा ले सकते हैं . बड़ी तालिकाओं के साथ धीमे होने की प्रवृत्ति रखें!
पिछली क्वेरी के आधार पर निर्माण:
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 गुना तेज़ हैं (जो सहसंबद्ध उपश्रेणियों के लिए बुरा नहीं है)।