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 数据。 Extractvaluexmltable 函数允许在服务器端从 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 中,查询输出窗口中的显示看起来毫无帮助,但如果您双击该值(类似于 &lt;ROW_LABELS=...&gt;),则最右侧会出现一个编辑图标;单击它,实际值将显示在新窗口中。

我假设“总计”应该包括所有内容;如果不是,我不确定您如何确定要动态包含的内容。如果你能做到这一点,尽管你可以保留一个单独的变量或集合,在循环时为总数构建 case 语句,然后再添加它。

【讨论】:

以上是关于Oracle SQL - 动态案例语句的主要内容,如果未能解决你的问题,请参考以下文章

Mysql 动态sql foreach 使用案例

常用sql语句及案例(oracle)

Oracle SQL:案例语句

Oracle 动态SQL语句

经典案例:如何优化Oracle使用DBlink的SQL语句

Oracle SQL 中的复杂案例语句