Postgres - كيفية إرجاع الصفوف ذات العدد 0 للبيانات المفقودة؟

StackOverflow https://stackoverflow.com/questions/346132

سؤال

لقد قمت بتوزيع البيانات بشكل غير متساو (تاريخ الكتابة) لبضع سنوات (2003-2008).أريد الاستعلام عن البيانات لمجموعة معينة من تاريخ البدء والانتهاء، وتجميع البيانات حسب أي من الفواصل الزمنية المدعومة (اليوم، الأسبوع، الشهر، الربع، السنة) في PostgreSQL 8.3 (http://www.postgresql.org/docs/8.3/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC).

المشكلة هي أن بعض الاستعلامات تعطي النتائج مستمرة خلال الفترة المطلوبة ، كما هي:

select to_char(date_trunc('month',date), 'YYYY-MM-DD'),count(distinct post_id) 
from some_table where category_id=1 and entity_id = 77  and entity2_id = 115 
and date <= '2008-12-06' and date >= '2007-12-01' group by 
date_trunc('month',date) order by date_trunc('month',date);
          to_char   | count 
        ------------+-------
         2007-12-01 |    64
         2008-01-01 |    31
         2008-02-01 |    14
         2008-03-01 |    21
         2008-04-01 |    28
         2008-05-01 |    44
         2008-06-01 |   100
         2008-07-01 |    72
         2008-08-01 |    91
         2008-09-01 |    92
         2008-10-01 |    79
         2008-11-01 |    65
        (12 rows)

لكن بعضها يفتقد بعض الفواصل الزمنية لعدم وجود بيانات، مثل هذه:

select to_char(date_trunc('month',date), 'YYYY-MM-DD'),count(distinct post_id) 
from some_table where category_id=1 and entity_id = 75  and entity2_id = 115 
and date <= '2008-12-06' and date >= '2007-12-01' group by 
date_trunc('month',date) order by date_trunc('month',date);

        to_char   | count 
    ------------+-------

     2007-12-01 |     2
     2008-01-01 |     2
     2008-03-01 |     1
     2008-04-01 |     2
     2008-06-01 |     1
     2008-08-01 |     3
     2008-10-01 |     2
    (7 rows)

حيث تكون مجموعة النتائج المطلوبة هي:

  to_char   | count 
------------+-------
 2007-12-01 |     2
 2008-01-01 |     2
 2008-02-01 |     0
 2008-03-01 |     1
 2008-04-01 |     2
 2008-05-01 |     0
 2008-06-01 |     1
 2008-07-01 |     0
 2008-08-01 |     3
 2008-09-01 |     0
 2008-10-01 |     2
 2008-11-01 |     0
(12 rows)

عدد 0 للإدخالات المفقودة.

لقد رأيت مناقشات سابقة حول Stack Overflow لكنها لم تحل مشكلتي على ما يبدو، نظرًا لأن فترة التجميع الخاصة بي هي واحدة من (يوم، أسبوع، شهر، ربع سنة، سنة) وتم تحديد وقت التشغيل بواسطة التطبيق.لذا فإن نهجًا مثل الانضمام الأيسر مع جدول تقويم أو جدول تسلسل لن يساعد في التخمين.

الحل الحالي لهذا هو ملء هذه الفجوات في Python (في تطبيق Turbogears) باستخدام وحدة التقويم.

هل هناك طريقة أفضل للقيام بذلك.

هل كانت مفيدة؟

المحلول

يمكنك إنشاء قائمة من كل الأيام الأولى من العام الماضي (ويقول) مع

select distinct date_trunc('month', (current_date - offs)) as date 
from generate_series(0,365,28) as offs;
          date
------------------------
 2007-12-01 00:00:00+01
 2008-01-01 00:00:00+01
 2008-02-01 00:00:00+01
 2008-03-01 00:00:00+01
 2008-04-01 00:00:00+02
 2008-05-01 00:00:00+02
 2008-06-01 00:00:00+02
 2008-07-01 00:00:00+02
 2008-08-01 00:00:00+02
 2008-09-01 00:00:00+02
 2008-10-01 00:00:00+02
 2008-11-01 00:00:00+01
 2008-12-01 00:00:00+01

وبعد ذلك يمكنك الانضمام مع هذه السلسلة.

نصائح أخرى

هذا السؤال قديم.ولكن بما أن زملائي المستخدمين اختاروه كنسخة رئيسية لنسخة مكررة جديدة، فأنا أقوم بإضافة إجابة مناسبة.

الحل المناسب

SELECT *
FROM  (
   SELECT day::date
   FROM   generate_series(timestamp '2007-12-01'
                        , timestamp '2008-12-01'
                        , interval  '1 month') day
   ) d
LEFT   JOIN (
   SELECT date_trunc('month', date_col)::date AS day
        , count(*) AS some_count
   FROM   tbl
   WHERE  date_col >= date '2007-12-01'
   AND    date_col <= date '2008-12-06'
-- AND    ... more conditions
   GROUP  BY 1
   ) t USING (day)
ORDER  BY day;
  • يستخدم LEFT JOIN, ، بالطبع.

  • generate_series() يمكن أن ينتج جدولًا للطوابع الزمنية بسرعة وبسرعة كبيرة.

  • بشكل عام، يكون التجميع أسرع قبل تنضم.لقد قدمت مؤخرًا حالة اختبار على sqlfiddle.com في هذه الإجابة ذات الصلة:

  • يلقي timestamp ل date (::date) للتنسيق الأساسي.لمزيد من الاستخدام to_char().

  • GROUP BY 1 هو اختصار بناء الجملة للإشارة إلى عمود الإخراج الأول.ممكن ان يكون GROUP BY day كذلك، ولكن قد يتعارض ذلك مع عمود موجود يحمل نفس الاسم.أو GROUP BY date_trunc('month', date_col)::date ولكن هذا طويل جدًا بالنسبة لذوقي.

  • يعمل مع وسيطات الفاصل الزمني المتاحة ل date_trunc().

  • count() لا تنتج أبدا NULL (0 لعدم وجود صفوف)، ولكن LEFT JOIN يفعل.
    لكي ترجع 0 بدلاً من NULL في الخارج SELECT, ، يستخدم COALESCE(some_count, 0) AS some_count. الدليل.

  • ل حل أكثر عمومية أو فترات زمنية تعسفية خذ بعين الاعتبار هذه الإجابة ذات الصلة الوثيقة:

هل يمكن إنشاء جدول مؤقت في وقت التشغيل واليسار الانضمام على ذلك. ويبدو أن تجعل اكثر احساسا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top