在oracle数据库中动态透视行

Posted

技术标签:

【中文标题】在oracle数据库中动态透视行【英文标题】:Pivoting row dynamically in oracle database 【发布时间】:2015-06-01 06:31:09 【问题描述】:

下面是场景:

(* denotes primary key)
**CompanyUnit**(*Unit_id, unit_loc,Year);

**Employees**(Unit_id,Dept_id,no_of_emp); (unit_id and dept_id are foreign keys)

**ref_department**(*dept_id,dept_name,dept_desc);

样本数据:


unit_id       unit_loc        year
------------------------------------
1             Delhi            2003
2             Mumbai           2004
------------------------------------
dept_id       dept_name        dept_desc
----------------------------------------
101           ABC              ABC-AI
102           ABC              ABC-BI
103           ABC              ABC-CS
104           XYZ              XYZ-Testing
105           XYZ              XYZ-Development
----------------------------------------------
unit_id       dept_id          no_of_emp
----------------------------------------------
1             101              5000
2             102              3000
1             103              4000
1             104              2000
2             105              1000
2             101              3000
----------------------------------------------

Required output: A dynamic view or select:
---------------------------------------------------------------------------
unit_id  unit_loc   ABC-AI  ABC-BI   ABC-CS   XYZ-Testing   XYZ-Development
---------------------------------------------------------------------------
1        Delhi      5000             4000     2000                         
2        Mumbai     3000     3000                           1000
---------------------------------------------------------------------------

问题是 ref_department 中的每个新部门都对应于 view/select 查询中的一个新列。

我写了下面的查询:

variable DEPARTMENTS VARCHAR2(100)
BEGIN
:DEPARTMENTS :=NULL;
  FOR cur_rec IN (SELECT DISTINCT DEPT_DESC FROM REF_DEPARTMENT) LOOP
    :DEPARTMENTS := :DEPARTMENTS || ''''|| cur_rec.DEPT_DESC|| '''' || ',' ;
  END LOOP;
  dbms_output.put_line(rtrim(:DEPARTMENTS,',')); 
select * from
(select uid,uloc,uyear, depdesc, emp from 
(select C.unit_id as uid, C.unit_loc as uloc,C.year as uyear, E.DEPT_ID as depid,R.DEPT_NAME as depname,R.DEPT_DESC as depdesc,S.NO_OF_EMP as emp
FROM Unit U INNER JOIN Employees E ON U.Unit_id=E.unit_id INNER JOIN REF_DEPARTMENT R ON  E.DEPT_ID=R.DEPT_ID))
pivot
(min(emp) for depdesc in (:departments));
END;

错误:PL/SQL:ORA-56901:pivot|unpivot 不允许非常量表达式

我参考了以下链接: Pivoting rows into columns dynamically in Oracle; http://www.orafaq.com/forum/t/187172/

【问题讨论】:

你可以绝对动态地做到这一点,如果你使用PIVOT XML,那么你可以使用IN (ANY)。否则,您必须每次都动态构建旋转查询,然后执行该选择。不是最简单的:-) 谢谢@Koshinae,我已经检查了这个解决方案,但问题是如果我使用 XML,那么我需要在我的 java 代码中处理 XML,这也会消耗一些内存和时间,因为我需要将所有记录(超过 15k 条记录,超过 40 列)从生成的视图(包括多个连接)导出到 excel 文件。出于这个原因,我正在寻找第二种方法。 【参考方案1】:

很久以前(在 Oracle 的 PIVOT 查询出现之前)我写了一个 blog post on "Pivot" Queries,它为您提供了一个包的代码来帮助构建此类查询。

根据您的要求,您可以这样做:

declare
   rc sys_refcursor;
begin
   rc := pivot.pivot_cursor
               ( group_cols   => 'u.unit_id, u.unit_loc'
               , pivot_col    => 'rd.dept_desc'
               , tables       => 'CompanyUnit x, Employees e, ref_departmnent rd'
               , value_cols   => 'e.no_of_emp'
               , agg_types    => 'sum'
               , where_clause => 'e.dept_id = rd.dept_id and e.unit_id = c.unit_id' 
               );
end;
/

由于 ref 游标可以有可变数量的列,您需要使用 DBMS_SQL 包来解析它,找出列名,然后运行它。起点是将引用游标转换为 DBMS_SQL 游标:

n := dbms_sql.to_cursor_number(rc);

您需要参考 DBMS_SQL 包文档示例以进一步了解这一点。

【讨论】:

以上是关于在oracle数据库中动态透视行的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 组和数据透视 - Oracle 中的动态数据透视

SQL - Oracle - 具有动态数据的数据透视表

使用 Sql Developer Oracle 的动态数据透视查询

如何使用数据透视在 Oracle 中将行转换为列

oracle sql中的动态数据透视

oracle sql中的动态数据透视