如何通过Oracle中的两个不同列有条件地进行分组?

时间:2016-11-02 17:26:22

标签: oracle group-by

假设我有一个包含以下数据的表:

+----------+-----+--------+
| CLASS_ID | Day | Period |
+----------+-----+--------+
| 1        | A   | CCR    |
+----------+-----+--------+
| 1        | B   | CCR    |
+----------+-----+--------+
| 2        | A   | 1      |
+----------+-----+--------+
| 2        | A   | 2      |
+----------+-----+--------+
| 3        | A   | 3      |
+----------+-----+--------+
| 3        | B   | 4      |
+----------+-----+--------+
| 4        | A   | 5      |
+----------+-----+--------+

正如您可能从数据的性质中猜测的那样,我正在研究从学生信息系统中提取课程安排数据的Oracle SQL查询。我试图将一个类"期间表达式",一个包含DayPeriod字段的计算值放入单个字段中。让我们首先考虑我的期望:

  • 如果期间匹配,则期间应为GROUP BY字段,Day应为聚合字段(通过LISTAGG函数),因此计算字段为{{1 }}
  • 如果天数匹配,则日期应为CCR (A-B)字段,GROUP BY应为聚合字段,因此计算字段为Period

我只知道如何单独执行每个1-2 (A),类似于GROUP BY匹配的地方:

Day

,反之亦然,匹配SELECT day, LISTAGG(period, '-') WITHIN GROUP (ORDER BY period) FROM schedule GROUP BY day ,但我没有看到我如何动态地为Periods Period做同样的事情。查询。

您还会注意到示例数据集中的最后一行不会跨越多天或多个时段,因此我还需要考虑不需要{{1}的课程毕竟。

修改

最终结果应为:

Day

2 个答案:

答案 0 :(得分:1)

如何与having count(*) = 1结合?

select LISTAGG(period, '-') list WITHIN GROUP (ORDER BY period) 
  from schedule 
  group by CLASS_ID, day 
  having count(*) = 1
union all
select LISTAGG(day, '-') list WITHIN GROUP (ORDER BY day) 
  from schedule 
  group by CLASS_ID, period 
  having count(*) = 1 

答案 1 :(得分:1)

我真的不清楚为什么要以这种方式输出。它没有提供任何有用的信息(我不认为) - 你无法分辨,例如class_id = 3,实际使用了哪些日期和时期的组合。有四种可能的组合(根据输出),但实际上只有两种组合在课程表中。

无论如何 - 你可能有你的理由。这是你如何做到的。您似乎想要LISTAGGday period(均由class_id分组,它们不会相互分组)。困难在于您只需要聚合列表中的不同值 - 没有重复。因此,您需要select distinct,分别为periodday,然后是列表聚合,然后在内部联接中连接结果。

这样的事情:

with
     test_data ( class_id, day, period ) as ( 
       select 1, 'A', 'CCR' from dual union all
       select 1, 'B', 'CCR' from dual union all
       select 2, 'A', '1'   from dual union all
       select 2, 'A', '2'   from dual union all
       select 3, 'A', '3'   from dual union all
       select 3, 'B', '4'   from dual union all
       select 4, 'A', '5'   from dual
   )
--  end of test data; the actual solution (SQL query) begins below this line
select a.class_id, a.list_per || '(' || b.list_day || ')' as expression
from   ( select class_id, 
                listagg(period, '-') within group (order by period) as list_per
           from ( select distinct class_id, period from test_data )
           group by class_id
       ) a
   inner join
       ( select class_id, 
                listagg(day, '-') within group (order by day) as list_day
           from ( select distinct class_id, day from test_data )
           group by class_id
       ) b
on a.class_id = b.class_id
;


CLASS_ID  EXPRESSION
--------  ----------
1         CCR(A-B)
2         1-2(A)
3         3-4(A-B)
4         5(A)