Oracle SQL - 动态案例语句
Posted
技术标签:
【中文标题】Oracle SQL - 动态案例语句【英文标题】:Oracle SQL - dynamic case statement 【发布时间】:2014-07-17 16:23:49 【问题描述】:是否可以将下面的 Oracle SQL 查询变成动态查询?我的意思是,我已将 case 语句的值硬编码为“INTERNET”、“SALES”等……是否可以避免硬编码?我的来源专栏是动态的。我在想一个 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);
【问题讨论】:
您如何知道要在“总计”中包含哪些值?或者您是否包括所有内容 - 如果是这样,那么这种情况有点毫无意义。 是的,您可以使这个查询动态化。我将使用您使用的任何代码语言、c#、php 等动态构建查询。如果您不能这样做,您可以在 oracle 中使用 while 循环或游标。让我们更多地了解您的要求以及您如何确定案例陈述中的价值。 也许您是在询问 PIVOT? 【参考方案1】:您需要一个具有动态列定义的 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 数据。 Extractvalue
和 xmltable
函数允许在服务器端从 XML 中查询特定列,但您必须提前指定字段名称。所以我假设在客户端解析它。
如果您想在 DB 层上做所有事情,还有另一种方法。 PIVOT
(不是 XML)需要列名 for source in ('INTERNET', 'DEMO', 'COM', ...)
。可以生成这样的查询并将 cursor 返回到客户端:
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 查询,请分两步完成:
-
在 PL/SQL 函数中使用命名列生成 PIVOT 查询;
从您的客户端运行查询。
【讨论】:
嘿,我将如何处理 XML 数据? @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 语句,然后再添加它。
【讨论】:
以上是关于Oracle SQL - 动态案例语句的主要内容,如果未能解决你的问题,请参考以下文章