सरल तरीका यह होगा कि इसे @jpw द्वारा प्रदर्शित क्रॉस जॉइन के साथ हल किया जाए। हालांकि, कुछ छिपी हुई समस्याएं हैं :
-
प्रदर्शन बिना शर्त
CROSS JOIN
पंक्तियों की बढ़ती संख्या के साथ जल्दी खराब हो जाता है। इस विशाल व्युत्पन्न तालिका को एकत्रीकरण में संसाधित करने से पहले, पंक्तियों की कुल संख्या को आपके द्वारा परीक्षण किए जा रहे सप्ताहों की संख्या से गुणा किया जाता है। इंडेक्स मदद नहीं कर सकते। -
1 जनवरी से शुरू होने वाले सप्ताह विसंगतियों की ओर ले जाते हैं। आईएसओ सप्ताह एक विकल्प हो सकता है। नीचे देखें।
निम्नलिखित सभी क्वेरीज़ में इंडेक्स exam_date
का भारी उपयोग किया जाता है . एक होना सुनिश्चित करें।
केवल प्रासंगिक पंक्तियों में शामिल हों
बहुत तेज़ होना चाहिए :
SELECT d.day, d.thisyr
, count(t.exam_date) AS lastyr
FROM (
SELECT d.day::date, (d.day - '1 year'::interval)::date AS day0 -- for 2nd join
, count(t.exam_date) AS thisyr
FROM generate_series('2013-01-01'::date
, '2013-01-31'::date -- last week overlaps with Feb.
, '7 days'::interval) d(day) -- returns timestamp
LEFT JOIN tbl t ON t.exam_date >= d.day::date
AND t.exam_date < d.day::date + 7
GROUP BY d.day
) d
LEFT JOIN tbl t ON t.exam_date >= d.day0 -- repeat with last year
AND t.exam_date < d.day0 + 7
GROUP BY d.day, d.thisyr
ORDER BY d.day;
यह आपके मूल में 1 जनवरी से शुरू होने वाले सप्ताहों की तरह है। जैसा कि टिप्पणी की गई है, यह कुछ विसंगतियां पैदा करता है:सप्ताह प्रत्येक वर्ष एक अलग दिन से शुरू होते हैं और चूंकि हम वर्ष के अंत में कट जाते हैं, वर्ष के अंतिम सप्ताह में केवल 1 या 2 दिन (लीप वर्ष) होते हैं।पी>
ISO सप्ताहों के साथ भी ऐसा ही
आवश्यकताओं के आधार पर, ISO सप्ताह पर विचार करें इसके बजाय, जो सोमवार से शुरू होता है और हमेशा 7 दिनों तक चलता है। लेकिन वे वर्षों के बीच सीमा पार करते हैं। प्रति दस्तावेज़ EXTRACT()
:
उपरोक्त क्वेरी को ISO सप्ताहों के साथ फिर से लिखा गया:
SELECT w AS isoweek
, day::text AS thisyr_monday, thisyr_ct
, day0::text AS lastyr_monday, count(t.exam_date) AS lastyr_ct
FROM (
SELECT w, day
, date_trunc('week', '2012-01-04'::date)::date + 7 * w AS day0
, count(t.exam_date) AS thisyr_ct
FROM (
SELECT w
, date_trunc('week', '2013-01-04'::date)::date + 7 * w AS day
FROM generate_series(0, 4) w
) d
LEFT JOIN tbl t ON t.exam_date >= d.day
AND t.exam_date < d.day + 7
GROUP BY d.w, d.day
) d
LEFT JOIN tbl t ON t.exam_date >= d.day0 -- repeat with last year
AND t.exam_date < d.day0 + 7
GROUP BY d.w, d.day, d.day0, d.thisyr_ct
ORDER BY d.w, d.day;
4 जनवरी हमेशा साल के पहले आईएसओ सप्ताह में होता है। तो इस अभिव्यक्ति को दिए गए वर्ष के पहले आईएसओ सप्ताह के सोमवार की तारीख मिलती है:
date_trunc('week', '2012-01-04'::date)::date
EXTRACT()
चूंकि ISO सप्ताह EXTRACT()
. द्वारा लौटाए गए सप्ताह की संख्याओं के साथ मेल खाते हैं , हम क्वेरी को सरल बना सकते हैं। पहला, एक छोटा और सरल रूप:
SELECT w AS isoweek
, COALESCE(thisyr_ct, 0) AS thisyr_ct
, COALESCE(lastyr_ct, 0) AS lastyr_ct
FROM generate_series(1, 5) w
LEFT JOIN (
SELECT EXTRACT(week FROM exam_date)::int AS w, count(*) AS thisyr_ct
FROM tbl
WHERE EXTRACT(isoyear FROM exam_date)::int = 2013
GROUP BY 1
) t13 USING (w)
LEFT JOIN (
SELECT EXTRACT(week FROM exam_date)::int AS w, count(*) AS lastyr_ct
FROM tbl
WHERE EXTRACT(isoyear FROM exam_date)::int = 2012
GROUP BY 1
) t12 USING (w);
अनुकूलित क्वेरी
अधिक विवरण के साथ वही और प्रदर्शन के लिए अनुकूलित
WITH params AS ( -- enter parameters here, once
SELECT date_trunc('week', '2012-01-04'::date)::date AS last_start
, date_trunc('week', '2013-01-04'::date)::date AS this_start
, date_trunc('week', '2014-01-04'::date)::date AS next_start
, 1 AS week_1
, 5 AS week_n -- show weeks 1 - 5
)
SELECT w.w AS isoweek
, p.this_start + 7 * (w - 1) AS thisyr_monday
, COALESCE(t13.ct, 0) AS thisyr_ct
, p.last_start + 7 * (w - 1) AS lastyr_monday
, COALESCE(t12.ct, 0) AS lastyr_ct
FROM params p
, generate_series(p.week_1, p.week_n) w(w)
LEFT JOIN (
SELECT EXTRACT(week FROM t.exam_date)::int AS w, count(*) AS ct
FROM tbl t, params p
WHERE t.exam_date >= p.this_start -- only relevant dates
AND t.exam_date < p.this_start + 7 * (p.week_n - p.week_1 + 1)::int
-- AND t.exam_date < p.next_start -- don't cross over into next year
GROUP BY 1
) t13 USING (w)
LEFT JOIN ( -- same for last year
SELECT EXTRACT(week FROM t.exam_date)::int AS w, count(*) AS ct
FROM tbl t, params p
WHERE t.exam_date >= p.last_start
AND t.exam_date < p.last_start + 7 * (p.week_n - p.week_1 + 1)::int
-- AND t.exam_date < p.this_start
GROUP BY 1
) t12 USING (w);
यह सूचकांक समर्थन के साथ बहुत तेज होना चाहिए और आसानी से पसंद के अंतराल के लिए अनुकूलित किया जा सकता है। निहित JOIN LATERAL
generate_series()
. के लिए अंतिम क्वेरी में 9.3 पोस्ट करें . की आवश्यकता है ।