Oracle 查询:动态循环列以创建行
Posted
技术标签:
【中文标题】Oracle 查询:动态循环列以创建行【英文标题】:Oracle Query: Loop through columns dynamically to create rows 【发布时间】:2014-11-21 14:13:51 【问题描述】:我有以下 Oracle 数据库,它可以包含任意数量的列,这些列可能会随着用户对 Web 应用程序的干预而增长。
ID | Name | Football | WhoCreated | When | Baseball | Cheerleading | Swimming
1 | Billy | (null) | sam, smith | (Timestamp)| 1 | (null) | 1
2 | Susie | 1 | sam, smith | (Timestamp)| (null) | 1 | 1
3 | Johnny | 1 | Homer | (Timestamp)| 1 | (null) | (null)
我正在尝试生成一个看起来像这样的输出
2, Susie, Football
3, Johnny, Football
1, Billy, Baseball
3, Johnny, Baseball
2, Susie, Cheerleading
1, Billy, Swimming
2, Susie, Swimming
我可以使用 UNION 来做到这一点,但我必须针对特定的名称字段进行调整。我已经有大约 50 列(50 个联合),并且系统中的用户可以随时增长。更复杂的是,我在列表中间有几列用于审计目的。我真的需要某种动态的方式来遍历列,我已经搜索过了,但似乎没有一个能解决我遇到的问题。
【问题讨论】:
规范化你的数据模型怎么样? 我没有设计这个数据库,我只是在做报告。我很想诅咒那些认为这是构建它的最佳方式的人。 【参考方案1】:这应该会给你一个想法。我无法测试它,但相当容易理解。
create or replace package test_pkg AS
TYPE REP_CURS IS REF CURSOR;
TYPE output_REC IS RECORD(
id_ number,
name_ varchar2(50),
field_ varchar2(50));
TYPE output_TAB IS TABLE OF output_REC;
FUNCTION Get_Data RETURN output_TAB
PIPELINED;
END test_pkg;
CREATE OR REPLACE PACKAGE BODY test_pkg IS
FUNCTION Get_Data RETURN output_TAB
PIPELINED IS
output_REC_ output_REC;
rep_lines_ REP_CURS;
stmt_ VARCHAR2(5000);
table_rec_ yourtable%ROWTYPE;
begin
stmt_ := ' YOUR QUERY HERE ';
OPEN rep_lines_ FOR stmt_;
LOOP
FETCH rep_lines_
INTO table_rec_;
EXIT WHEN rep_lines_%NOTFOUND;
output_REC_.id_ := table_rec_.id;
output_REC_.name_ := table_rec_.name;
if table_rec_.football IS not null then
output_REC_.field_ := table_rec_.football;
PIPE ROW(output_REC_);
end if;
if table_rec_.Baseball IS not null then
output_REC_.field_ := table_rec_.Baseball;
PIPE ROW(output_REC_);
end if;
if table_rec_.Cheerleading IS not null then
output_REC_.field_ := table_rec_.Cheerleading;
PIPE ROW(output_REC_);
end if;
if table_rec_.Swimming IS not null then
output_REC_.field_ := table_rec_.Swimming;
PIPE ROW(output_REC_);
end if;
END LOOP;
CLOSE rep_lines_;
RETURN;
exception
when others then
DBMS_OUTPUT.put_line('Error:' || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE ||
DBMS_UTILITY.FORMAT_ERROR_STACK ||
DBMS_UTILITY.FORMAT_CALL_STACK);
END Get_Data;
END test_pkg;
【讨论】:
谢谢,我将不得不在非生产服务器上进行测试。 我的 Oracle 许可证受到供应商的严格限制,我收到权限不足错误。我想我将不得不在 SQL 之外执行此操作。不过,我会坚持使用该代码!【参考方案2】:你应该创建一个函数然后使用它,请考虑下面的代码:
CREATE OR REPLACE FUNCTION commalist (par_id NUMBER) RETURN VARCHAR2 IS
TYPE curs IS REF CURSOR;
v_emp_cursor curs;
fieldvalue VARCHAR2(4000);
var_out VARCHAR2(4000);
CURSOR col IS
SELECT column_name
FROM user_tab_columns
WHERE table_name = 'TABLE';
BEGIN
FOR reccol IN col LOOP
OPEN v_emp_cursor FOR ('SELECT '||reccol.column_name||' val
FROM TABLE
WHERE id = '||par_id);
LOOP
FETCH v_emp_cursor INTO fieldvalue;
IF fieldvalue IS NOT NULL THEN
var_out := var_out||fieldvalue||', ';
END IF;
EXIT WHEN v_emp_cursor%NOTFOUND;
END LOOP;
CLOSE v_emp_cursor;
END LOOP;
RETURN SubStr(var_out, 0, Length(var_out) - 2);
END;
【讨论】:
我没有足够的权限来创建函数。不幸的是,我的许可证受到供应商的限制。这些答案虽然是金矿,但我将把它保存起来以备不时之需。我将尝试将表格转储到 Crystal 报表。【参考方案3】:试试这个
with src as (select 1 ID, 'Billy' name, 0 Football, 'sam smith' WhoCreated, sysTimestamp when,
1 Baseball, cast(null as number) Cheerleading, 1 Swimming
from dual
union all
select 2, 'Susie', 1, 'sam smith', sysTimestamp, null, 1, 1
from dual
union all
select 3, 'Johnny', 1, 'Homer', sysTimestamp, 1, null, null
from dual),
unpivottbl as (select *
from src
UNPIVOT
(
VAL
FOR descript
IN (Football, Baseball, Cheerleading, Swimming)
))
select ID, name, descript
from unpivottbl
where VAL = 1
虽然它不是动态解决方案,但您需要添加要取消透视数据的列。这是unpivot的参考。
【讨论】:
这是一个静态解决方案。请检查标题 不幸的是,看起来静态解决方案可能是我唯一的希望。为什么我会使用 unpiviot 表而不是仅使用 Union 语句?记得我的真实数据有 50 左右列。以上是关于Oracle 查询:动态循环列以创建行的主要内容,如果未能解决你的问题,请参考以下文章