【问题标题】:CASE statement with LIKE during LISTAGG OperationLISTAGG 操作期间带有 LIKE 的 CASE 语句
【发布时间】:2017-04-10 14:41:45
【问题描述】:
+-----------+------------+-----------+-----------+
| Person_ID | First_Name | Last_Name | Igroup_ID |
+-----------+------------+-----------+-----------+
|         2 | Rick       | Hudson    |       100 |
|         2 | Rick       | Hudson    |        50 |
|         2 | Rick       | Hudson    |        28 |
|         2 | Rick       | Hudson    |        15 |
|         3 | John       | Hardy     |       150 |
|         3 | John       | Hardy     |       100 |
|         4 | Tom        | Johnson   |       200 |
|         4 | Tom        | Johnson   |       150 |
|         4 | Tom        | Johnson   |       100 |
+-----------+------------+-----------+-----------+

请参考上表使用操作 LISTAGG 背后的原因,我得到多行 Person ID 因为我每个人都与多个 Group ID 相关联,所以我想使用 LISTAGG 连接 Group ID 并隔离值.

而且由于IGROUP值有这么多每个人都可以关联的值,但是我们担心前2个值,在屏幕截图中

当某物返回 100|50 时 'GroupA' 当某事返回 150|100 则 'GroupB' 当某些东西返回 200|150 Then 'GroupC'

(CASE LISTAGG(G.IGROUP_ID , '|') WITHIN GROUP (ORDER BY G.igroup_id) 
          WHEN  '100|50' THEN 'GroupA'
          WHEN  '150|100' THEN 'GroupB'
          WHEN  '200|150 THEN 'GroupC'
     END) AS SERVICES

但这里的问题是,如果我使用上述方式,我必须在 CASE 语句中定义所有组合

有什么方法可以使用WHEN LIKE '100|50%' THEN GroupA

【问题讨论】:

  • 我必须说这个问题/方法感觉有些不对劲。我强烈建议您为整个查询/问题添加一个新问题
  • 还是不清楚。(1)你只关心50,100,150和200吗?这些价值观有什么特别之处?其他值呢? (2) 你打算如何处理结果? (3) 在您原来的问题中,每个人 ID 可能会出现多次组 ID,它怎么会出现在这个数据结构中? (4)为什么数据一开始就没有归一化?为什么除了人名之外还有名字?我认为,如果您给出全貌,解决方案将完全不同。附:请将图片替换为文字(可以ctrl+k格式化)
  • 1) 不是那样,我只是让它很容易理解。 2)那些价值观是人的技能,我需要根据技能来隔离这个人,例如,如果他有 50 和 100,他属于一个组 3)组 ID 是一种技能,人可以拥有多种技能(但是如果他满足 2 个技能,我可以识别 GroupA、GroupB、GroupC 技能)4)我没有保留这个选项,当然我会工作并用文字替换图片
  • 我唯一的意图是我会得到很多像下面这样的组合,但是当我做 LISTAGG 操作时,我得到的值如下。所以不要每次都写 WHEN 语句。我想使用 WHEN LIKE ( 50|100%) Then GroupA

标签: sql oracle sql-like listagg


【解决方案1】:

至于CASE+LIKE,需要用到CASE所谓的“搜索语法”:

CASE WHEN <condition> THEN <result>
    [WHEN <condition> THEN <result>
     ...]
    [ELSE <result>] 
END

如果您想保留默认的 ELSE NULL 但又不想重复 LISTAGG,则需要包装您的查询。例如

SELECT CASE WHEN la LIKE '50|100%' THEN 'Yes'
            WHEN la LIKE '100|100%' THEN 'No
        END services
  FROM ( SELECT LISTAGG(G.IGROUP_ID , '|') WITHIN GROUP (ORDER BY G.igroup_id) la
          ....
       ) t

但是,使用 LISTAGG 测试组条件并非不常见。在聚合中使用 CASE 更为常见。例如。要检查是否有一个 igroup_id = 50 的组成员,请使用此条件

COUNT(CASE WHEN igroup_id = 50 THEN 1 END) = 1

要实现与您的 LISTAGG 方法相同的逻辑(恰好一个 50,至少一个 100,并且没有其他值

CASE WHEN COUNT(CASE WHEN igroup_id = 50 THEN 1 END) = 1
      AND COUNT(CASE WHEN igroup_id = 100 THEN 1 END) >= 1
      AND COUNT(CASE WHEN igroup_id < 100 THEN 1 END) = 1 -- one for 50
     THEN 'Yes',

    WHEN COUNT(CASE WHEN igroup_id = 100 THEN 1 END) >= 2
     AND COUNT(CASE WHEN igroup_id < 100 THEN 1 END) = 0
    THEN 'No'
END

这可能看起来过于复杂,但很可能是因为我重新实现了您的 LISTAGG 方法。了解附带条件(例如,如果 igroup_id 恰好是唯一的)可能会使这种方法不那么笨重。

这是我写的一些关于...的文章

...案例:http://modern-sql.com/feature/case

...在聚合中使用 CASE 的技巧:http://modern-sql.com/feature/filter

... LISTAGG:http://modern-sql.com/feature/listagg(但我认为使用 listagg 对您的目的不利)。

【讨论】:

  • 谢谢,我已经更新了问题,你能帮我找到更好的方法吗?
  • @Batman:你需要对任何新信息提出一个新问题,修改问题并再次询问不是好方法
  • @Gemeiswar 我会确保这一点。在这里,我刚刚用其他信息更新了相同的问题
【解决方案2】:

为您感兴趣的每个值创建一个标志列。
拥有它后,您可以轻松进行所需的任何分析。

select    person_id

         ,max (case when igroup_id =  15 then 1 else 0 end) as is_15
         ,max (case when igroup_id =  28 then 1 else 0 end) as is_28
         ,max (case when igroup_id =  50 then 1 else 0 end) as is_50
         ,max (case when igroup_id = 100 then 1 else 0 end) as is_100
         ,max (case when igroup_id = 150 then 1 else 0 end) as is_150
         ,max (case when igroup_id = 200 then 1 else 0 end) as is_200

from      mytable

group by  person_id

order by  person_id      

+-----------+-------+-------+-------+--------+--------+--------+
| PERSON_ID | IS_15 | IS_28 | IS_50 | IS_100 | IS_150 | IS_200 |
+-----------+-------+-------+-------+--------+--------+--------+
|         2 |     1 |     1 |     1 |      1 |      0 |      0 |
|         3 |     0 |     0 |     0 |      1 |      1 |      0 |
|         4 |     0 |     0 |     0 |      1 |      1 |      1 |
+-----------+-------+-------+-------+--------+--------+--------+

只提取前 2 个值并检查它们

(
    CASE    regexp_substr
            (
                LISTAGG(G.IGROUP_ID , '|') WITHIN GROUP (ORDER BY G.igroup_id)
               ,'[^|]*\|[^|]*'
            )

        WHEN '50|100'  THEN 'Yes'
        WHEN '100|100' THEN 'No'
    END
) AS SERVICES

【讨论】:

  • 我会试试这个
  • 感谢 Dudu 对我来说,你提供的 regexp_substr 解决方案更好,我的 Oracle 有问题,一旦实例启动,我会尝试并确认!!。
【解决方案3】:

您可以在子查询中获取该 listagg 值并将其与主表连接,然后根据您的要求编写 case 语句。

Select t.*, case when list_value like '100|50%' THEN 'GroupA'
          WHEN list_value like '150|100%' THEN 'GroupB'
          WHEN  list_value like '200|150%' THEN 'GroupC' end as services
From 
(Select distinct Person_ID,First_Name,Last_Name from urtable) t
Join
(Select Person_ID,
LISTAGG(G.IGROUP_ID , '|') WITHIN GROUP (ORDER BY G.igroup_id desc) list_value from urtable group by Person_ID) g on (t.Person_ID=g.Person_ID)

【讨论】:

    【解决方案4】:

    根据我的理解,我猜您需要每个Person_Id 的一条记录以及根据您提供的案例的结果,即Group_A/Group_B/Group_C

    WITH Test_Result AS 
    (SELECT person_id,first_name,last_name,listagg(igroup_id,'|') within GROUP 
    (ORDER BY rownum) list_agg_res FROM sof_3 GROUP BY 
    person_id,first_name,last_name)
    
    SELECT person_id,first_name,last_name,list_agg_res,
    CASE WHEN list_agg_res LIKE '100|50%' THEN 'Group_A' 
    WHEN  list_agg_res LIKE '150|100%' THEN 'Group_B' 
    WHEN list_agg_res LIKE '200|150%' THEN 'Group_C' 
    ELSE NULL END Final_Res FROM Test_Result;
    

    希望以上查询对您有所帮助!!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-28
      • 2019-06-11
      • 1970-01-01
      • 2021-10-12
      • 1970-01-01
      • 1970-01-01
      • 2014-05-22
      • 1970-01-01
      相关资源
      最近更新 更多