2009-06-22 68 views
2

我有一个数据的Oracle表看起来像这样:的Oracle SQL查询来总结统计,使用GROUP BY

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

也就是说,ID是主键,将有各多行“批处理”,并且每行将在STATUS列中具有状态码。还有其他一些列,但这些是重要的列。

我需要写一个查询,它将总结状态码每个批次;有三种可能的值,可以在状态栏中去,0,1,2,我想输出看起来是这样的:

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

这些数字将是数;对于批次1,存在其中STATUS设为

  • 2记录0
  • 1记录其中STATUS被设置为1,并
  • 没有记录,其中STATUS被设置为0 。

对于批2,有

  • 1分的记录,其中状态设置为0,并
  • 没有记录,其中状态设置为1或2

有没有一种方法,我可以在一个查询做到这一点,没有不得不重写每个状态码的查询?即我可以很容易地编写一个查询,并运行它三次:

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

我可以运行,然后再运行它在那里的地位= 1,并再次在那里的地位= 2,但我希望能在一个查询中完成。

如果它的确与众不同,除了从状态列有另一个列,我可能想总结以同样的方式 - 我不希望有选择后执行SELECT语句的另一个原因声明并合并所有结果。

回答

6
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; 

这通常被称为“转动”查询,我已经写了如何动态on my blog产生这些查询的文章。

版本使用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; 
+0

感谢您的支持! – imiric 2010-12-06 14:23:46

1
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 
1
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 
+1

COUNT的好处是它永远不会返回NULL,因此您的DECODE可以更加紧凑:count((decode(status,0,1))status0 – 2009-06-22 22:36:22

1

OP询问是否有一种方法(SUM)比其他(COUNT)的任何性能优势。在26K行的表上运行一个简单的测试表明COUNT方法明显更快。因人而异。

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 

我反复试了几次,以消除缓存的任何影响。我也交换了选择语句。结果全面相似。

编辑:这是我用来回答a similar question与使用相同的测试设备。