PostgreSQL
 sql >> डेटाबेस >  >> RDS >> PostgreSQL

प्रति सप्ताह रिकॉर्ड्स की कुल संख्या

सरल तरीका यह होगा कि इसे @jpw द्वारा प्रदर्शित क्रॉस जॉइन के साथ हल किया जाए। हालांकि, कुछ छिपी हुई समस्याएं हैं :

  1. प्रदर्शन बिना शर्त CROSS JOIN पंक्तियों की बढ़ती संख्या के साथ जल्दी खराब हो जाता है। इस विशाल व्युत्पन्न तालिका को एकत्रीकरण में संसाधित करने से पहले, पंक्तियों की कुल संख्या को आपके द्वारा परीक्षण किए जा रहे सप्ताहों की संख्या से गुणा किया जाता है। इंडेक्स मदद नहीं कर सकते।

  2. 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 पोस्ट करें . की आवश्यकता है ।

SQL Fiddle.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. हेरोकू त्रुटि:एक्शन व्यू ::टेम्पलेट ::त्रुटि (#<संदेश:0x007fc9df016930> के लिए अपरिभाषित विधि 'कैप्चा')

  2. एक अल्पविराम से अलग सूची के रूप में परिणाम वापस करने के लिए PostgreSQL क्वेरी

  3. PostgreSQL में भारी मात्रा में डेटा लोड करने का सबसे अच्छा तरीका क्या है?

  4. ऑर्डर बाय के साथ विभाजन में पंक्तियों की गणना करें

  5. एप्लिकेशन, कनेक्शन पूलिंग और पोस्टग्रेएसक्यूएल के लिए एक सुरक्षा प्रणाली - एलडीएपी के लिए मामला