使用 CASE 语句问题选择聚合数据

Posted

技术标签:

【中文标题】使用 CASE 语句问题选择聚合数据【英文标题】:SELECTING aggregate data using CASE statement issue 【发布时间】:2016-07-21 19:30:24 【问题描述】:

我正在尝试创建查询以获取区域数据,以准备将列移动到另一个表。我们的数据库是如何建立的,我们有资产和电缆。一个资产可以有很多电缆,但一个电缆不能有多个资产。我们目前正在尝试将 Zone 字段从 Cable 表(例如下面)移动到 Asset 表。

A_ID    C_ID       Zone        
--------------------------
1       1          Green
1       2          Green   
1       3          Yellow
2       4          Green
2       5          Red
3       6          Yellow
3       7          Yellow
3       8          Yellow
3       9          Red

我希望为资产表设置它的方式是,如果资产包含具有不同区域的多条电缆,如果其中一个区域为黄色,则默认为黄色(例如 3 根绿色电缆,1 根黄色电缆 - Asset_ID有黄色区域)。接下来,如果它没有任何黄色但至少有 1 条红色,则默认为红色(例如 2 条绿色电缆,3 条红色电缆 - Asset_ID 具有红色区域)。只有当它只有绿色区域时,它才会默认为绿色。

使用上面的示例表,这些是我所期望的结果。

Expected Results    

A_ID   Zone
-------------
1      Yellow
2      Red
3      Yellow

我正在尝试使用 CASE 语句,但我在制定查询以正确分组结果时遇到了困难。

任何帮助将不胜感激,在此先感谢您。

【问题讨论】:

尝试使用具有窗口功能的 CTE。像 RowNum 之类的东西(当黄色然后 1,红色然后 2,绿色 3 时按案例区域按资产顺序分区),然后在外部查询中从每个组中选择第一行。 【参考方案1】:

一种使用条件聚合和case 表达式的方法。

select a_id
,case when yellow_count >=1 then 'Yellow'
      when yellow_count = 0 and red_count >=1 then 'Red'
      when yellow_count = 0 and red_count = 0 and green_count >=1 then 'Green'
 end zone
from (select a_id,
      count(case when zone = 'Yellow' then 1 end) yellow_count,
      count(case when zone = 'Red' then 1 end) red_count
      count(case when zone = 'Green' then 1 end) green_count
      from cable_table
      group by a_id) t

【讨论】:

你不认为 count 和 case 会比 join 和 row_number() 慢吗?【参考方案2】:

不需要 case 或 if 语句。分组思考。您需要为优先级分配一个优先级连接,并首先选择具有最高优先级的连接。像这样

WITH Priority AS
(
   SELECT * FROM (
   VALUES
     ('Yellow', 1),
     ('Red', 2),
     ('Green', 3)
   ) AS P(Zone,P)
)
SELECT A_ID, Zone
FROM (     
  SELECT A_ID, Zone
         ROW_NUMBER() OVER (PARTITION BY A_ID ORDER BY P.P ASC) AS RN
  FROM AssetTable A
  LEFT JOIN Priority P ON A.Zone = P.Zone
) SUB
WHERE RN = 1 

不确定我的语法是否适用于 CTE for Oracle 中的 VALUES。如果出现错误,请替换为:

WITH Priority AS
(
   SELECT 'Yellow' AS Zone, 1 AS P
     UNION ALL
   SELECT 'Red' AS Zone, 2 AS P
     UNION ALL
   SELECT 'Green' AS Zone, 3 AS P
)

【讨论】:

【参考方案3】:

为什么不做一个简单的:

SELECT A_ID, MAX( Zone )
FROM table
GROUP BY A_ID

如果某个 A_ID 具有 Yellow,则 max( Zone ) 返回 Yellow 如果某个 A_ID 没有Yellow,但有Red,则 max(Zone) 返回 Red<br> otherwise (noYellownorRed) max( Zone ) returnsGreen`

和例子:

with data as (
select 1  a_id,   1 c_id ,         'Green' zone from dual union all
select 1   ,    2  ,        'Green'  from dual union all
select 1 ,      3,          'Yellow' from dual union all
select 2 ,      4 ,         'Green' from dual union all
select 2 ,      5 ,         'Red' from dual union all
select 3  ,     6  ,       'Yellow' from dual union all
select 3   ,    7   ,       'Yellow' from dual union all
select 3   ,    8        ,  'Yellow' from dual union all
select 3   ,    9   ,       'Red' from dual 
)
select a_id, max( zone )
from data
group by a_id


      A_ID MAX(ZO
---------- ------
         1 Yellow
         2 Red   
         3 Yellow

【讨论】:

以上是关于使用 CASE 语句问题选择聚合数据的主要内容,如果未能解决你的问题,请参考以下文章

聚合case语句中sum和count的区别

谷歌数据工作室中的条件聚合

DB2 - 在光标中聚合大小写

sql聚合函数的应用

SqlServer如何用Sql语句自定义聚合函数

再看case语句