在 Oracle SQL 中使用 RANK() 和 ROLLUP 对分组行进行排序和汇总

问题描述 投票:0回答:2

给定一个任务、人员和时间表,我希望能够看到在每项任务上花费最多时间的人、他们在该任务上花费了多少时间以及下方的摘要行。

示例表:

task        person  time
----        ------  ----
Admin       Sue     0.5
Admin       Ted     0.25
Meetings    Ted     1.25
Meetings    Sue     0.75

期望的结果:

task        top_person
----        ----------
Admin       Sue (0.5)
Meetings    Ted (1.25)
Overall     Ted (1.5)

所以我们可以看到每个任务排名最高的人以及总体排名最高的人以及他们在所有任务中的总时间。

到目前为止我有什么:

SELECT
    DECODE(GROUPING(task), 0, task, 'Overall') AS task,
    MAX(person || ' (' || time || ')') KEEP (DENSE_RANK FIRST ORDER BY time DESC) AS top_person
FROM time_table
GROUP BY ROLLUP(task)

输出:

task        top_person
----        ----------
Admin       Sue (0.5)
Meetings    Ted (1.25)
Overall     Ted (1.25)

此查询按任务对表进行分组,按时间对组内的任务进行排序,保留第一行,然后从保留的行中显示人员及其时间。除了汇总行外,这行得通,似乎这条行对各个任务组执行相同的逻辑,但跨所有任务除外。相反,我需要的是所有任务的时间总和,按人分组,按小时排序,但仅适用于汇总行,而不适用于其他行。关于如何实现这一目标的任何想法?

sql oracle
2个回答
1
投票

也许您可以考虑对此类查询使用 MODEL 子句。行间/列间操作非常方便
示例数据:

WITH
    tbl (TASK, PERSON, TASK_TIME) AS
        (
            Select 'Admin',     'Sue',    0.5     FRom Dual Union All
            Select 'Admin',     'Ted',   0.25     FRom Dual Union All
            Select 'Meetings',  'Ted',   1.25     FRom Dual Union All
            Select 'Meetings',  'Sue',   0.75     FRom Dual 
        )

SQL 问题中的结果:

Select *
From    ( Select      TASK_STR "TASK", PERSON_STR "TOP_PERSON"
          From    (   Select    CAST(TASK as Varchar2(20)) "TASK", 
                                CAST(TASK as Varchar2(20)) "TASK_STR",
                                CAST(PERSON as VarChar2(20)) "PERSON", 
                                TASK_TIME "TASK_TIME",
                                '(' || PERSON || ' ' || TASK_TIME || ')' "PERSON_STR" 
                      From tbl
                  )
              MODEL Dimension By (TASK, PERSON)
                    Measures(TASK_TIME, TASK_STR, PERSON_STR)
                    Rules (
                              TASK_STR[ANY, ANY] = Case When TASK_TIME[CV(), CV()] = Max(TASK_TIME)[CV(), ANY] Then CV(TASK) END, 
                              TASK_STR['Overall', 'Time'] = CV(TASK) || ' ' || CV(PERSON),
                              PERSON_STR['Overall', 'Time'] = Sum(TASK_TIME)[TASK != CV(), PERSON != CV()]
                          )
        )
Where TASK Is Not Null

R e s u l t :                
TASK                           TOP_PERSON                          
------------------------------ ------------------------------------
Admin                          (Sue .5)                            
Meetings                       (Ted 1.25)                          
Overall Time                   2.75                               

更改 MODEL 子句的 RULES 部分可以为您提供您可能需要或想从数据中获得的任何其他信息:

... ... ...
              MODEL Dimension By (TASK, PERSON)
                    Measures(TASK_TIME, TASK_STR, PERSON_STR)
                    Rules (
                              TASK_STR[ANY, ANY] = Case When TASK_TIME[CV(), CV()] = Max(TASK_TIME)[CV(), ANY] Then CV(TASK) END, 
                              --
                              TASK_STR['Overall', 'Data'] = 'Overall Data Below',
                              PERSON_STR['Overall', 'Data'] = LPAD('-', 15, '-'),
                              --
                              TASK_STR['Time', 'Total'] = CV(TASK) || ' ' || CV(PERSON),
                              PERSON_STR['Time', 'Total'] = Sum(TASK_TIME)[TASK != CV(), PERSON != CV()],
                              --
                              TASK_STR['Rows', 'Count'] = 'Rows Count',
                              PERSON_STR['Rows', 'Count'] = Count(PERSON_STR)[TASK != CV() And TASK NOT IN('Overall', 'Time', 'Rows'), PERSON != CV() And PERSON NOT IN('Data', 'Total', 'Count', 'Average')],
                              --
                              TASK_STR['Time', 'Average'] = 'Time Average',
                              PERSON_STR['Time', 'Average'] = Avg(TASK_TIME)[TASK != CV() And TASK != 'Overall', PERSON != CV() And PERSON NOT IN('Data', 'Total', 'Count', 'Average')]
                          )
        )
Where TASK Is Not Null
Order By TASK

R e s u l t :  
TASK                 TOP_PERSON                                   
-------------------- ----------------------------------------------
Admin                (Sue .5)                                       
Meetings             (Ted 1.25)                                     
Overall Data Below   ---------------                                
Rows Count           4                                              
Time Average         .6875                                          
Time Total           2.75     

1
投票

虽然 Model 子句工作得非常好,但是如果你想要更多 SQL 特定的解决方案,你可以尝试 -

WITH max_data AS (SELECT task, person, time,
                         ROW_NUMBER() OVER(PARTITION BY task ORDER BY time DESC) RN1
                    FROM your_table
                 ),
overall_person_data AS (SELECT person, SUM(time) sum_time
                          FROM your_table
                         GROUP BY person
                       )
SELECT task, person || '(' || max_time || ')'
  FROM max_data
 WHERE RN1 = 1
 UNION ALL
SELECT 'Overall', person || '(' || MAX(sum_time) || ')'
  FROM max_data
 GROUP BY person;

更正了原始答案的语法

WITH
    tbl (TASK, PERSON, TASK_TIME) AS
        (
            Select 'Admin',     'Sue',    0.5     FRom Dual Union All
            Select 'Admin',     'Ted',   0.25     FRom Dual Union All
            Select 'Meetings',  'Ted',   1.25     FRom Dual Union All
            Select 'Meetings',  'Sue',   0.75     FRom Dual 
        ),
 max_data AS 
        (SELECT task, person, task_time,
                ROW_NUMBER() OVER(PARTITION BY task ORDER BY task_time DESC) RN1
         FROM tbl
        ),
overall_person_data AS 
        (SELECT person, SUM(task_time) sum_time
         FROM max_data
         GROUP BY person
        )
Main SQL 1.
    SELECT  task, person || '(' || task_time || ')' "TOP_PERSON"
    FROM    max_data
    WHERE RN1 = 1
UNION ALL
    SELECT  'Overall', person || '(' || SUM(sum_time) || ')'
    FROM    overall_person_data
    GROUP BY person
RESULT 1
TASK     TOP_PERSON                                  
-------- ---------------------------------------------
Admin    Sue(.5)                                       
Meetings Ted(1.25)                                     
Overall  Sue(1.25)                                     
Overall  Ted(1.5)       

Main SQL 2.
    SELECT  task, person || '(' || task_time || ')' "TOP_PERSON"
    FROM    max_data
    WHERE RN1 = 1
UNION ALL
    SELECT  'Overall', '(' || SUM(sum_time) || ')'
    FROM    overall_person_data
RESULT 2.
TASK     TOP_PERSON                                  
-------- ---------------------------------------------
Admin    Sue(.5)                                       
Meetings Ted(1.25)                                     
Overall  (2.75)                                        

© www.soinside.com 2019 - 2024. All rights reserved.