在 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;

这样做的好处是只扫描表一次。您可以应用类似的逻辑来删除NULLs。

【讨论】:

这个查询是否覆盖了空条件? @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 XDB 中转义控制字符

如何在 Oracle 中转置? [复制]

如何在 oracle 中转义特殊的正则表达式字符?

在 Oracle 的 to_char 中转义非格式字符的最佳方法是啥?

NamedParameterJdbcTemplate Oracle SQL,如何在命名查询中转义单引号?

具有分析功能的oracle转置[重复]