Oracle SQL - 动态case语句

时间:2014-07-17 16:23:49

标签: sql oracle oracle11g

是否可以将以下Oracle SQL查询转换为动态查询?我的意思是,我已经将案例陈述的值硬编码为'INTERNET','SALES'ETC ......是否可以避免硬编码?我的源列是动态的。我在想一个for循环和数组,但在SQL中是否可用?如果有人能让我继续这样做,那就太好了。谢谢。

SELECT
        NVL(status, 'Grand Total') AS "ROW LABELS",
        COUNT(case when source = 'INTERNET' THEN 1 end) AS "INTERNET",
        COUNT(case when source = 'SALES' THEN 1 end) AS "SALES",
        COUNT(case when source = 'DEMO' THEN 1 end) AS "DEMO",
        COUNT(case when source = 'COM' THEN 1 end) AS "COM",
        COUNT(CASE WHEN order_source IN ('INTERNET', 'SALES', 'DEMO', 'COM') THEN 1 END) AS "Grand Total"
FROM
SOMETABLE
GROUP BY ROLLUP(status);

2 个答案:

答案 0 :(得分:2)

你无法在纯SQL中真正做到这一点。你可以创建一个动态生成结果的函数;可能比使用匿名块更容易处理:

create or replace function get_counts return sys_refcursor as
  query varchar2(32767);
  rc sys_refcursor;
begin
  query := 'select nvl(status, ''Grand Total'') as row_labels';
  for tmp in (select distinct source from sometable order by 1)
  loop
    query := query || ', count(case when source = ''' || tmp.source
      || ''' then 1 end) as "' || substr(tmp.source, 1, 30) || '"';
  end loop;
  query := query || ', count(*) as total';
  query := query || ' from sometable';
  query := query || ' group by rollup(status)';
  query := query || ' order by status';

  open rc for query;

  return rc;
end;
/

在SQL * Plus或SQL Developer中运行(使用“作为脚本运行”):

var rc refcursor;
exec :rc := get_counts;
print rc

您也可以将此作为查询:

select get_counts from dual;

在SQL Developer中,在查询输出窗口中显示无用的显示,但如果双击该值(看起来像{<ROW_LABELS=...>}),则会在最右侧显示一个编辑图标;单击该按钮,实际值将显示在新窗口中。

我假设'总'应该包括一切;如果不是,我不确定你如何确定动态包含什么。如果你可以这样做,虽然你可以保留一个单独的变量或集合,在循环时为总计构建case语句,然后再添加它。

答案 1 :(得分:2)

您需要具有动态列定义的PIVOT函数。最简单的方法是pivot xml:

create table tst_data (id int primary key, source varchar2(255));

insert into tst_data values (1, 'INTERNET');
insert into tst_data values (2, 'DEMO');
insert into tst_data values (3, 'INTERNET');
insert into tst_data values (4, 'SALES');
insert into tst_data values (5, 'INTERNET');
insert into tst_data values (6, 'DEMO');
insert into tst_data values (7, 'INTERNET');
insert into tst_data values (8, 'COM');

commit;

select * from (
  select source from tst_data
) 
pivot xml 
(
  count(1)
  for source in (select distinct t.source from tst_data t)
)  

需要处理XML数据后:

<PivotSet>
    <item>
        <column name = "SOURCE">COM</column>
        <column name = "COUNT(1)">1</column>
    </item>
    <item>
        <column name = "SOURCE">DEMO</column>
        <column name = "COUNT(1)">2</column>
    </item>
    <item>
        <column name = "SOURCE">INTERNET</column>
        <column name = "COUNT(1)">4</column>
    </item>
    <item>
        <column name = "SOURCE">SALES</column>
        <column name = "COUNT(1)">1</column>
    </item>
</PivotSet>

PIVOT XML支持动态列定义(for source in (select distinct t.source from tst_data t)),但它返回XML数据。 Extractvaluexmltable函数允许从服务器端的XML查询特定列,但您必须提前指定字段名称。所以我假设在客户端解析它。

如果你想在DB层上做所有事情,还有另一种方法。 PIVOT(不是XML)需要列名for source in ('INTERNET', 'DEMO', 'COM', ...)。可以生成这样的查询并将游标返回给客户端:

CREATE OR REPLACE FUNCTION FUNCTION1 RETURN SYS_REFCURSOR AS 
 cur sys_refcursor;
BEGIN
  open cur for 'select * from dual'; // generate PIVOT query here
  RETURN cur;
END FUNCTION1;

我不知道从游标(在服务器端)创建简单的无类型查询的任何方法,因此如果您希望使用纯SQL查询,请分两步执行:

  1. 在PL / SQL函数中生成带有命名列的PIVOT查询;
  2. 从您的客户端运行查询。