在 oracle 中转置表
Posted
技术标签:
【中文标题】在 oracle 中转置表【英文标题】:Transposing a table in oracle 【发布时间】:2016-12-21 02:39:19 【问题描述】:我们如何编写一个将表 A 的值转换为表 B 的 plsql 代码。
表A
rec_id || col1 || col2 || col3
2 val1 val2 val3
3 val4 val5 val6
表 B 中的期望输出
表 B
rec_id || type || value
2 col1 val1
2 col2 val2
2 col3 val3
3 col4 val5
3 col5 val5
3 col6 val6
仅当 val1 或 val2 或 val3 不为空时。如果其中任何一个值为 null,则表 B 中不应存在 rec。例如,如果 val2 为 null,则
rec_id || type || value
2 col1 val1
2 col3 val3
【问题讨论】:
。 .我不是 100% 确定我理解NULL
条件。是否只是为了过滤掉NULL
值?
是的,如果表 A 中的任何列有任何 NULL 值,那么它不应该出现在表 B 中的类型和值列中
什么是“它”?排?列?
【参考方案1】:
在 Oracle 11.1 及更高版本中,您可以使用UNPIVOT
。我添加了一些测试数据来展示NULL
的处理。 (请记住,在 Oracle 中,空字符串 ''
与 NULL
相同。)
with
table_a ( rec_id, col1, col2, col3 ) as (
select 2, 'val1', 'val2', 'val3' from dual union all
select 3, 'val4', '' , 'val6' from dual union all
select 8, '' , '' , '' from dual
)
-- end of test data; query begins below this line
select *
from table_a
unpivot ( value for type in ( col1 as 'col1', col2 as 'col2', col3 as 'col3') )
;
REC_ID TYPE VALUE
------ ---- -----
2 col1 val1
2 col2 val2
2 col3 val3
3 col1 val4
3 col3 val6
【讨论】:
【参考方案2】:您可以使用union all
(以及其他方法)来做到这一点:
select rec_id, 'col1' as type, col1 as value from t where col1 is not null union all
select rec_id, 'col2' as type, col2 as value from t where col2 is not null union all
select rec_id, 'col3' as type, col3 as value from t where col3 is not null;
您似乎想要消除出现任何NULL
的行和列:
select rec_id, type, value
from (select t.*,
(case when count(*) over (partition by type) = count(value) over (partition by type)
then 1 else 0
end) as no_null_type,
(case when count(*) over (partition by recid) = count(value) over (partition by recid)
then 1 else 0
end) as no_null_recid
from (select rec_id, 'col1' as type, col1 as value from t union all
select rec_id, 'col2' as type, col2 as value from t union all
select rec_id, 'col3' as type, col3 as value from t
) t
) t
where no_null_type = 1 and no_null_recid = 1;
在 Oracle 12c 中,您可以使用横向连接:
select t.rec_id, x.type, x.value
from t cross apply
(select 'col1' as type, col1 as value from dual union all
select 'col2' as type, col2 as value from dual union all
select 'col3' as type, col3 as value from dual
) x
where value is not null;
这样做的好处是只扫描表一次。您可以应用类似的逻辑来删除NULL
s。
【讨论】:
这个查询是否覆盖了空条件? @mathguy 。 . . OP 似乎希望同时删除行和列,因此它对两者使用相同的方法。【参考方案3】:我知道您显示的表名不是您想要运行查询的实际表,它可能有超过 3 列。
所以我想出了一个 PLSQL 过程(如果你想要在 sql 中,那么其他答案会帮助你)
DECLARE
SQLSELECT VARCHAR2(1000);
SQLINSERT VARCHAR2(1000);
TYPE TCur IS REF CURSOR;
cur TCur;
TYPE TRec IS RECORD (ID INT,
COL_VAL VARCHAR2(20));
v_rec TRec;
BEGIN
FOR rec IN
(SELECT column_name
FROM all_tab_columns
WHERE table_name = 'TABLEA')
LOOP
IF (rec.column_name <> 'REC_ID') THEN
SQLSELECT:= 'SELECT ID,'||rec.column_name||' column_name FROM TABLEA
where '||rec.column_name||' is not null';
OPEN cur FOR SQLSELECT;
LOOP
FETCH cur INTO v_rec;
EXIT WHEN cur%notfound;
-- DBMS_OUTPUT.PUT_LINE(v_rec.ID||' '||v_rec.COL_VAL||' '||rec.column_name);
SQLINSERT:= 'INSERT INTO TABLEB VALUES(:1, :2, :3)';
--DBMS_OUTPUT.PUT_LINE(SQLINSERT);
EXECUTE IMMEDIATE SQLINSERT USING v_rec.ID,v_rec.COL_VAL,rec.column_name;
END LOOP;
END IF;
END LOOP;
END;
在这里,您必须将TABLEA
更改为您的表名,并根据您的表ID 更改REC_ID
,您必须在运行代码之前创建目标表并将TABLEB
替换为您的目标表名。它还将涵盖空条件。注释语句在调试代码时很有帮助
【讨论】:
以上是关于在 oracle 中转置表的主要内容,如果未能解决你的问题,请参考以下文章
在 Oracle 的 to_char 中转义非格式字符的最佳方法是啥?