استعلام Oracle SQL لتلخيص الإحصائيات باستخدام GROUP BY

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

  •  06-07-2019
  •  | 
  •  

سؤال

لدي جدول أوراكل يحتوي على بيانات تبدو كما يلي:

ID   BATCH   STATUS
1    1       0
2    1       0
3    1       1
4    2       0

إنه، بطاقة تعريف هو المفتاح الأساسي، وسيكون هناك صفوف متعددة لكل "دفعة"، وسيكون لكل صف رمز الحالة في الملف حالة عمود.هناك مجموعة من الأعمدة الأخرى، لكن هذه هي الأهم.

أحتاج إلى كتابة استعلام يلخص رموز الحالة لكل حزمة;هناك ثلاث قيم محتملة يمكن إدراجها في عمود الحالة، 0 و1 و2، وأرغب في أن يبدو الناتج كما يلي:

BATCH  STATUS0  STATUS1  STATUS2
1      2        1        0
2      1        0        0

ستكون هذه الأرقام تهمًا.للدفعة 1، هناك

  • 2 سجلات حيث حالة تم ضبطه على 0
  • 1 سجل حيث حالة تم ضبطه على 1، و
  • لا توجد سجلات حيث حالة تم ضبطه على 0.

بالنسبة للدفعة 2، هناك

  • 1 سجل حيث حالة تم ضبطه على 0، و
  • لا توجد سجلات حيث حالة تم ضبطه على 1 أو 2.

هل هناك طريقة يمكنني من خلالها القيام بذلك في استعلام واحد، دون الحاجة إلى إعادة كتابة الاستعلام لكل رمز حالة؟أي.يمكنني بسهولة كتابة استعلام مثل هذا وتشغيله ثلاث مرات:

SELECT batch, COUNT(status)
FROM table
WHERE status = 0
GROUP BY batch

يمكنني تشغيل ذلك، ثم تشغيله مرة أخرى حيث الحالة = 1، ومرة ​​أخرى حيث الحالة = 2، ولكنني آمل أن أفعل ذلك في استعلام واحد.

إذا كان يحدث فرقا، بصرف النظر عن حالة العمود هناك آخر العمود الذي قد أرغب في تلخيصه بنفس الطريقة - سبب آخر لعدم رغبتي في تنفيذ عبارة SELECT بعد عبارة SELECT ودمج كل النتائج.

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

المحلول

select batch 
,      count(case when status=1 then 1 end) status1
,      count(case when status=2 then 1 end) status2
,      count(case when status=3 then 1 end) status3
from   table
group by batch;

يُطلق على هذا غالبًا اسم الاستعلام "المحوري"، وقد كتبت مقالًا حول كيفية إنشاء هذه الاستعلامات ديناميكيًا على مدونتي.

الإصدار الذي يستخدم DECODE (خاص بـ Oracle ولكنه أقل تفصيلاً):

select batch 
,      count(decode(status,1,1)) status1
,      count(decode(status,2,1)) status2
,      count(decode(status,3,1)) status3
from   table
group by batch;

نصائح أخرى

select batch,
sum(select case when status = 0 then 1 else 0 end) status0,
sum(select case when status = 1 then 1 else 0 end) status1,
sum(select case when status = 2 then 1 else 0 end) status2
from table
group by batch
select batch,
sum((decode(status,0,1,0)) status0,
sum((decode(status,1,1,0)) status1,
sum((decode(status,2,1,0)) status2,
from table
group by batch

يسأل OP عما إذا كان هناك أي فائدة أداء لنهج واحد (SUM) على الآخر (COUNT).يُظهر إجراء اختبار بسيط على جدول يحتوي على 26 ألف صف أن أسلوب COUNT أسرع بشكل ملحوظ.YMMV.

DECLARE
  CURSOR B IS
     select batch_id
       FROM batch
      WHERE ROWNUM < 2000;

  v_t1  NUMBER;
  v_t2  NUMBER;
  v_c1  NUMBER;
  v_c2  NUMBER;
  v_opn INTEGER;
  v_cls INTEGER;
  v_btc VARCHAR2(100);
BEGIN
-- Loop using SUM
  v_t1 := dbms_utility.get_time;
  v_c1 := dbms_utility.get_cpu_time;
  FOR R IN B LOOP
     FOR R2 IN (SELECT batch_type_code
                     , SUM(decode(batch_status_code, 'CLOSED', 1, 0)) closed
                     , SUM(decode(batch_status_code, 'OPEN', 1, 0)) OPEN
                     , SUM(decode(batch_status_code, 'REWORK', 1, 0)) rework
                  FROM batch
                 GROUP BY batch_type_code) LOOP 
        v_opn := R2.open;
        v_cls := R2.closed;
     END LOOP;
  END LOOP;
  v_t2 := dbms_utility.get_time;
  v_c2 := dbms_utility.get_cpu_time;
  dbms_output.put_line('For loop using SUM:');
  dbms_output.put_line('CPU seconds used: '||(v_c2 - v_c1)/100);
  dbms_output.put_line('Elapsed time: '||(v_t2 - v_t1)/100);

-- Loop using COUNT
  v_t1 := dbms_utility.get_time;
  v_c1 := dbms_utility.get_cpu_time;
  FOR R IN B LOOP
     FOR R2 IN (SELECT batch_type_code
                     , COUNT(CASE WHEN batch_status_code = 'CLOSED' THEN 1 END) closed
                     , COUNT(CASE WHEN batch_status_code = 'OPEN' THEN 1 END) OPEN
                     , COUNT(CASE WHEN batch_status_code = 'REWORK' THEN 1 END) rework
                  FROM batch
                 GROUP BY batch_type_code) LOOP 
        v_opn := R2.open;
        v_cls := R2.closed;
     END LOOP;
  END LOOP;
  v_t2 := dbms_utility.get_time;
  v_c2 := dbms_utility.get_cpu_time;
  dbms_output.put_line('For loop using COUNT:');
  dbms_output.put_line('CPU seconds used: '||(v_c2 - v_c1)/100);
  dbms_output.put_line('Elapsed time: '||(v_t2 - v_t1)/100);
END;
/

أدى هذا إلى الإخراج التالي:

For loop using SUM:
CPU seconds used: 40
Elapsed time: 40.09
For loop using COUNT:
CPU seconds used: 33.26
Elapsed time: 33.34

كررت الاختبار عدة مرات للتخلص من أي آثار للتخزين المؤقت.لقد قمت أيضًا بتبديل عبارات التحديد.وكانت النتائج مماثلة في جميع المجالات.

يحرر:هذا هو نفس أداة الاختبار التي استخدمتها للإجابة سؤال مماثل مع.

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