【问题标题】:Case when to return multiple values返回多个值的情况
【发布时间】:2017-12-12 14:34:36
【问题描述】:

这是我的表格,我会在日常场景中使用解码来显示以下值,但是,我需要在特定报告的 1 列中显示所有值,因为它们对应于同一个项目。

T1:

Id V1 V2 V3 V4 V5 V5_Text   
1  1  1  1  0  0   Other    
2  0  1  1  0  1   Other     
3  0  0  1  0  1   QWE    
4  0  0  1  1  0   ABC    
5  1  0  0  0  1   Other 

我使用 case when 但我似乎不能返回超过 1 个值。

select id,
case when V1=1, then 'A'
     when V2=1, then 'B'
     when V3=1, then 'C'
     when V4=1, then 'D'
     when V5=1, then v5_text

预期输出:

1  A,B,C,Other   
2  B,C,Other    
3  C,QWE    
4  C,D,ABC   
5  A,Other  

实际输出:

1  Other    
2  Other    
3  QWE     
4  ABC    
5  Other   

【问题讨论】:

  • 你必须连接你的字符串,或者创建一个 JSON 来返回“多个”值,因为 OR 是惰性的:一旦满足条件,就不会进一步评估

标签: sql oracle


【解决方案1】:

您需要连接这些值:

select id,
       trim(',' from
            (case when V1 = 1 then 'A,' end) ||
             case when V2 = 1 then 'B,' end) ||
             case when V3 = 1 then 'C,' end) ||
             case when V4 = 1 then 'D,' end) ||
             case when V5 = 1 then v5_text
            )
           )

【讨论】:

  • 如果我要连接,我什至需要一个案例吗?我可以使用解码来实现这一点吗?
  • @Skn 。 . . casedecode() 更可取,因为它是 ANSI 标准功能。
  • decode 本质上是case 的替代语法。使用您喜欢的任何一个,尽管case 通常更具可读性。
【解决方案2】:

不确定您还有什么想法,也不知道这张表有多大,但另一种方法是“取消透视”该 v1...v5 结构,然后使用 listagg()

顺便说一句:第一行

Id V1 V2 V3 V4 V5 V5_Text   
1  1  1  1  0  0   Other   
   A  B  C                   -- there is no "other" here

SQL Fiddle

Oracle 11g R2 架构设置

CREATE TABLE Table1
    (ID int, V1 int, V2 int, V3 int, V4 int, V5 int, V5_TEXT varchar2(5))
;

INSERT ALL 
    INTO Table1 (ID, V1, V2, V3, V4, V5, V5_TEXT)
         VALUES (1, 1, 1, 1, 0, 0, 'Other')
    INTO Table1 (ID, V1, V2, V3, V4, V5, V5_TEXT)
         VALUES (2, 0, 1, 1, 0, 1, 'Other')
    INTO Table1 (ID, V1, V2, V3, V4, V5, V5_TEXT)
         VALUES (3, 0, 0, 1, 0, 1, 'QWE')
    INTO Table1 (ID, V1, V2, V3, V4, V5, V5_TEXT)
         VALUES (4, 0, 0, 1, 1, 0, 'ABC')
    INTO Table1 (ID, V1, V2, V3, V4, V5, V5_TEXT)
         VALUES (5, 1, 0, 0, 0, 1, 'Other')
SELECT * FROM dual
;

查询 1

select
      id, listagg(alpha,',') within group (order by alpha) codes_concat
from (
    select id, v1, V5_TEXT, 'A' alpha from table1 where v1 = 1 union all
    select id, v2, V5_TEXT, 'B' alpha from table1 where v2 = 1 union all
    select id, v3, V5_TEXT, 'C' alpha from table1 where v3 = 1 union all
    select id, v4, V5_TEXT, 'D' alpha from table1 where v4 = 1 union all
    select id, v5, V5_TEXT, 'Other'   from table1 where v5 = 1 
    ) d
group by
      id

Results

| ID | CODES_CONCAT |
|----|--------------|
|  1 |        A,B,C |
|  2 |    B,C,Other |
|  3 |      C,Other |
|  4 |          C,D |
|  5 |      A,Other |

编辑

如果union都得罪了,那么还有其他办法:

select
      id, listagg(alpha,',') within group (order by alpha) codes_concat
from (
      select 
            id
          , V5_TEXT
          , case when cj.n = 1 then v1
                 when cj.n = 2 then v2
                 when cj.n = 3 then v3
                 when cj.n = 4 then v4
                 when cj.n = 5 then v5 end v1
          , case when cj.n = 1 then 'A'
                 when cj.n = 2 then 'B'
                 when cj.n = 3 then 'C'
                 when cj.n = 4 then 'D'
                 when cj.n = 5 then V5_TEXT end alpha
      from table1
      cross join (
          Select level n from dual connect by level<=5
          ) cj
    ) d
where v1 <> 0
group by
      id

;

或者可以使用 unpivot 等等。

select
      id, listagg(alpha,',') within group (order by alpha) codes_concat
from (
      select
            id, v5_text, v1
          , case when vname = 'V1' then 'A'
                 when vname = 'V2' then 'B'
                 when vname = 'V3' then 'C'
                 when vname = 'V4' then 'D'
                 when vname = 'V5' then v5_text end alpha
      from table1
      UNPIVOT ( 
              v1 FOR( vname ) IN ( v1, v2, v3, v4, v5
              )
          )
    ) d
where v1 <> 0
group by
      id
;

【讨论】:

  • 好主意...但请不要通过 UNION ALL 和多次传递相同的数据来取消数据透视。要么使用 UNPIVOT 运算符(需要 Oracle 11.1 或更高版本),要么通过交叉产品进行反透视,因为反透视是在 11.1 之前完成的。
  • @mathguy 很高兴您添加请求的重写
  • 我几乎做到了 - 然后我意识到 Gordon 的解决方案无论如何都更有效。 Unpivoting 会丢失信息:在 unpivoting 之后,我们需要再次按 id 分组,这是一个昂贵的操作。这些行已经在基表中按 id 分组,我们不妨利用它。与排序相同:“令牌”已经排序(按列顺序);如果我们取消透视,我们必须在listagg() 中订购(在您的符号中通过n)。
【解决方案3】:

希望对你有帮助,我用你的原始数据测试这段代码

select id,
rtrim((case v1 when 1 then 'A,' else '' end ||
case v2   when 1 then 'B,' else '' end ||
case v3   when 1 then 'C,' else '' end ||
case  v4  when 1 then 'D,' else '' end ||
case  v5 when 1 then v5_text || ',' else '' end),',')
from table1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-16
    • 1970-01-01
    • 2015-08-03
    • 1970-01-01
    • 2013-04-26
    • 1970-01-01
    • 1970-01-01
    • 2013-05-29
    相关资源
    最近更新 更多