【问题标题】:Finding nth not null value from table从表中查找第 n 个非空值
【发布时间】:2021-06-16 14:30:54
【问题描述】:

我有下面这样的表格,里面有类似的数据

| cust  |   val1  | val2  | val3  | val4  | val5  |
---------------------------------------------------
|  1    |    x1   |       |  x3   |       |       | 
|  2    |         |       |       | x4    |       | 
|  3    |    x1   |       |       |       |       |     
|  4    |    x1   |  x2   |  x3   | x4    |  x5   |

我想输出为

cust val1 val2  val3 val4  val5
1    x1    x3   
2    x4 
3    x1 
4    x1    x2   x3    x4    x5
create table test_nth_nonull (cust varchar2(3),val1 varchar2(3),val2 varchar2(3),val3 varchar2(3), val4 varchar2(3), val5 varchar2(3));
insert into  test_nth_nonull values(1,'x1',null,'x3',null,null);
insert into  test_nth_nonull values(2,null,null,null,'x4',null);
insert into  test_nth_nonull values(3,'x1',null,null,null,null);
insert into  test_nth_nonull values(4,'x1','x2','x3','x4','x5');

select* from test_nth_nonull;

请帮助在查询中实现这一点

【问题讨论】:

    标签: sql oracle plsql oracle11g


    【解决方案1】:

    您可以使用pivotunpivot 子句。首先将unpivot 列到行以获取行号(该子句默认忽略空值),然后将pivot "shook-down" 值返回到它们的新位置:

    with test_nth_nonull (cust,val1,val2,val3,val4,val5) as (
      select 1,'x1',null,'x3',null,null from dual union all
      select 2,null,null,null,'x4',null from dual union all
      select 3,'x1',null,null,null,null from dual union all
      select 4,'x1','x2','x3','x4','x5' from dual
    ), unpivotted as (
      select *
      from test_nth_nonull
      unpivot (val for col in (val1 as 1, val2 as 2, val3 as 3, val4 as 4, val5 as 5))
    ), numbered as (
      select u.cust, row_number() over (partition by u.cust order by u.col) as rn, u.val
      from unpivotted u
    )
    select *
    from numbered
    pivot (max(val) for rn in (1 as val1, 2 as val2, 3 as val3, 4 as val4, 5 as val5))
    

    Db fiddle here.

    【讨论】:

      【解决方案2】:

      这是一个选项。

      这就是你所拥有的:

      SQL> select* from test_nth_nonull order by cust;
      
      CUST   VAL1  VAL2  VAL3  VAL4  VAL5
      ------ ----- ----- ----- ----- -----
      1      x1          x3
      2                        x4
      3      x1
      4      x1    x2    x3    x4    x5
      

      这就是你想要的(在代码中读取 cmets):

      SQL> with
        2  temp as
        3    -- concatenate all values
        4    (select cust, val1 ||';'|| val2||';'|| val3||';'|| val4||';'|| val5 val
        5     from test_nth_nonull
        6    ),
        7  temp2 as
        8    -- split concatenated column to rows
        9    (select cust,
       10       regexp_substr(val, '[^;]+', 1, column_value) val,
       11       column_value cv
       12     from temp
       13       cross join table(cast(multiset(select level from dual
       14                                      connect by level <= 5
       15                                     ) as sys.odcinumberlist))
       16    )
       17  -- final result
       18  select cust,
       19    max(case when cv = 1 then val end) v1,
       20    max(case when cv = 2 then val end) v2,
       21    max(case when cv = 3 then val end) v3,
       22    max(case when cv = 4 then val end) v4,
       23    max(case when cv = 5 then val end) v5
       24  from temp2
       25  group by cust
       26  order by cust;
      
      CUST   V1   V2   V3   V4   V5
      ------ ---- ---- ---- ---- ----
      1      x1   x3
      2      x4
      3      x1
      4      x1   x2   x3   x4   x5
      
      SQL>
      

      【讨论】:

        【解决方案3】:

        使用 nvl() 和 decode() 以及 cte 会给你你想要的。 如果 val1 为空,nvl(val1,val2) 将返回 val2,否则返回 val1。 decode(val2,val1,null,val2) 表示如果 val2 =val1 那么 null 否则 val2

        with cte as (
        select cust , nvl(nvl(nvl(nvl(val1 ,val2) ,val3 ), val4 ), val5 )val1,nvl(nvl(nvl(val2 ,val3 ), val4 ), val5 )val2,nvl(nvl(val3, val4 ), val5 )val3,nvl( val4 , val5 )val4,val5
         from test_nth_nonull
         )
         select cust,val1,decode(val2,val1,null,val2)val2,decode(val3,val2,null,val1,null,val3)val3,decode(val4,val3,null,val2,null,val1,null,val4)val4,decode(val5,val4,null,val3,null,val2,null,val1,null,val5) val5 from cte
        

        【讨论】:

          【解决方案4】:

          我将使用横向连接对数据进行反透视和旋转

          select t.*, x.*
          from test_nth_nonull t cross join lateral
               (select max(case when seqnum = 1 then val end) as val_1,
                       max(case when seqnum = 2 then val end) as val_2,
                       max(case when seqnum = 3 then val end) as val_3,
                       max(case when seqnum = 4 then val end) as val_4,
                       max(case when seqnum = 5 then val end) as val_5
                from (select val, row_number() over (order by n) as seqnum
                      from (select 1 as n, t.val1 as val from dual union all
                            select 2, t.val2 as val from dual union all
                            select 3, t.val3 as val from dual union all
                            select 4, t.val4 as val from dual union all
                            select 5, t.val5 as val from dual
                           ) x
                      where val is not null
                     ) x
               ) x;
          

          与将值放入字符串并处理字符串相比,此版本的优势在于保留了列的原始数据类型。

          在整个表上对pivot/unpivot 执行此操作的优势应该是性能。旋转只在每一行内,应该更快。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-12-03
            • 1970-01-01
            • 2020-06-02
            • 2011-01-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多