2014-07-17 293 views
1

是否可以将下面的Oracle SQL查询转换为动态查询?我的意思是,我已经硬编码案例陈述的价值为'互联网','销售'等...是否有可能避免硬编码?我的源列是动态的。我在想一个for循环和数组,但是在SQL中可用吗?如果有人能让我这样做,那会很好。谢谢。Oracle SQL - 动态case语句

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); 
+0

您如何知道在'total'中包含哪些值?或者你是否包括了一切 - 如果这样的话有点意思。 –

+0

是的,你可以使这个查询动态。我会动态地使用您正在使用的代码语言,c#,php等动态构建查询。如果您不能这样做,可以在oracle中使用while循环或游标。让我们更多地了解您的需求,以及您如何确定您的病例陈述中的价值。 – Vulcronos

+1

也许你在问一个PIVOT? – Randy

回答

2

您需要一个具有动态列定义的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中查询特定的列,但是您必须提前指定字段名称。所以我假设在客户端解析它。

如果你想在数据库层上做所有事情,还有另一种方法。 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. 从客户端运行查询。
+0

嘿,我将如何处理XML数据? –

+0

@NomanArain你需要在哪里?我的意思是你可以在客户端或服务器端执行它吗? –

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语句,然后添加它。