Question

J'ai une table Oracle avec des données qui ressemblent à ceci:

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

C’est-à-dire que ID est la clé primaire, il y aura plusieurs lignes pour chaque "lot", " et chaque ligne aura un code d'état dans la colonne ÉTAT . Il y a beaucoup d'autres colonnes, mais ce sont les plus importantes.

Je dois rédiger une requête qui récapitule les codes d'état pour chaque lot . Trois valeurs possibles peuvent figurer dans la colonne STATUS, 0, 1 et 2, et j'aimerais un résultat ressemblant à ceci:

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

Ces chiffres seraient des comptes; pour le lot 1, il y a

  • 2 enregistrements où STATUS est défini sur 0
  • 1 enregistrement où STATUS est défini sur 1 et
  • aucun enregistrement où STATUS est défini sur 0.

Pour le lot 2, il y a

  • 1 enregistrement où STATUS est défini sur 0 et
  • aucun enregistrement où STATUS est défini sur 1 ou 2.

Est-il possible de le faire en une seule requête sans avoir à réécrire la requête pour chaque code d'état? Je peux facilement écrire une requête de ce type et l'exécuter trois fois:

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

Je pourrais l'exécuter, puis l'exécuter à nouveau où status = 1, et encore où status = 2, mais j'espère pouvoir le faire en une seule requête.

Si cela fait une différence, mis à part la colonne STATUS , vous trouverez une autre colonne que je pourrais vouloir résumer de la même manière - une autre raison pour laquelle je ne le fais pas vouloir exécuter une instruction SELECT après une instruction SELECT et fusionner tous les résultats.

Était-ce utile?

La solution

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;

Ceci est souvent appelé un "pivot". J'ai écrit un article sur la génération dynamique de ces requêtes sur mon blog .

Version utilisant DECODE (spécifique à Oracle mais moins commentée):

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;

Autres conseils

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 demande s'il y a un avantage de performance d'une approche (SUM) sur l'autre (COUNT). L'exécution d'un test simpleminded sur une table avec 26K lignes montre que l'approche COUNT est nettement plus rapide. 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;
/

Ceci a généré le résultat suivant:

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

J'ai répété le test plusieurs fois pour éliminer les effets de la mise en cache. J'ai également échangé les déclarations sélectionnées. Les résultats étaient similaires dans tous les domaines.

EDIT: c’est le même harnais de test que j’ai utilisé pour répondre à une question similaire avec.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top