Frage

Ich habe eine Oracle-Tabelle mit Daten, die wie folgt aussieht:

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

Das heißt, ID ist der Primärschlüssel, gibt es mehrere Zeilen für jeden sein "Batch", und jede Zeile wird ein Statuscode im Status Spalte haben. Es gibt eine Reihe von anderen Spalten, aber diese sind die wichtigsten.

Ich brauche eine Abfrage zu schreiben, die die Statuscodes für jeden Charge zusammenfassen ; gibt es drei mögliche Werte, die in der Spalte Status, 0, 1 und 2, und ich möchte Ausgabe, die etwa wie folgt aussieht gehen kann:

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

Diese Zahlen würden zählt; für Batch-1 gibt es

  • 2 Datensätze, in denen Status 0
  • gesetzt
  • 1 Datensatz, in dem Status auf 1 gesetzt ist, und
  • keine Datensätze, in denen Status auf 0
  • gesetzt

Für Batch-2 gibt es

  • 1 Datensatz, in dem Status auf 0 gesetzt ist, und
  • keine Datensätze, in denen Status auf 1 gesetzt ist oder 2.

Gibt es eine Möglichkeit, dass ich dies in einer Abfrage zu tun, ohne die Abfrage für jeden Statuscode neu zu schreiben? das heißt ich eine Abfrage wie dies leicht schreiben kann, und es dreimal laufen:

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

ich laufen kann, ist es dann wieder laufen, wo Status = 1, und wieder in dem Status = 2, aber ich hoffe, es in einer Abfrage zu tun.

Wenn es einen Unterschied macht, abgesehen von dem Status Spalte gibt es andere Spalt, die ich auf die gleiche Weise zusammenfassen mag - einen weiteren Grund, dass ich nicht tun haben wollen SELECT-Anweisung nach SELECT-Anweisung auszuführen und alle Ergebnisse amalgamieren.

War es hilfreich?

Lösung

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;

Dies ist oft eine „Pivot“ Abfrage genannt, und ich habe einen Artikel darüber geschrieben, wie diese Abfragen dynamisch generiert auf meinem Blog .

Version mit DECODE (Oracle-spezifischer, aber weniger ausführlich):

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;

Andere Tipps

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 fragt, ob es einen Performance-Vorteil von einem Ansatz ist (SUM) über den anderen (COUNT). einen einfältigen Test auf einer Tabelle mit 26K Reihen läuft, dass der COUNT Ansatz deutlich schneller ist. 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;
/

Dies ergab die folgende Ausgabe:

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

Ich wiederholte den Test ein paar Mal alle Auswirkungen des Caching zu beseitigen. Ich tauschte auch die Select-Anweisungen. Die Ergebnisse waren auf der ganzen Linie ähnlich.

EDIT: Dies ist der gleiche Test-Harnisch ich eine ähnliche Frage mit.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top