处理连续行计算

Posted

技术标签:

【中文标题】处理连续行计算【英文标题】:Dealing with consecutive rows calculations 【发布时间】:2012-09-12 02:59:54 【问题描述】:

假设以下情况:

第 1 周: 0 以前的案例 10 个新病例 3 已解决的案例 第 2 周: 7 以前的案例 13 个新病例 15 解决案例 第 3 周: 5 以前的案例 6 个新病例 7 解决案例

此信息存储在排序的恢复表中:

RESUME_TABLE:
WEEK    | TOTAL_NEW |   TOTAL_SOLVED
1       |   10      |   3
2       |   13      |   15
3       |   6       |   7

我很难构建查询以获得以下结果:

REPORT_TABLE:
WEEK    |   PREV_TOTAL  |   NEW_CASES   |   SOLVED_CASES    |   NEW_TOTAL
1       |   0           |   10          |   3               |   7
2       |   7           |   13          |   15              |   5
3       |   5           |   6           |   7               |   4

NEW_TOTAL = PREV_TOTAL + NEW_CASES - SOLVED_CASES 这个想法似乎很简单,尽管我一直在努力将PREV_TOTAL 带到下一行以便继续。

我正在尝试使用 RESUME 表 (Oracle 11g) 上的视图来执行此操作。

有人可以帮我提供一些示例代码吗?

【问题讨论】:

【参考方案1】:

分析函数非常简单整洁:

12:57:06 HR@vm_xe> l                                                                    
  1  select week                                                                        
  2         ,lag(total_cases_by_now - total_solved_by_now) over (order by week) prev_total
  3         ,total_new new_cases                                                        
  4         ,total_solved solved_cases                                                  
  5         ,total_cases_by_now - total_solved_by_now new_total                         
  6    from (                                                                           
  7    select week                                                                      
  8           ,total_new                                                                
  9           ,total_solved                                                             
 10           ,sum(total_new) over(order by week asc) as total_cases_by_now             
 11           ,sum(total_solved) over (order by week asc) as total_solved_by_now        
 12      from resume_table                                                              
 13* )                                                                                  
12:57:07 HR@vm_xe> /                                                                    

      WEEK   PREV_TOTAL  NEW_CASES SOLVED_CASES  NEW_TOTAL                                
---------- ------------ ---------- ------------ ----------                                
         1                      10            3          7                                
         2            7         13           15          5                                
         3            5          6            7          4                                

3 rows selected.                                                                        

Elapsed: 00:00:00.01                                                                    

【讨论】:

很酷,我试图达到这样的目标,尽管我从来没有花时间好好学习分析:( 我发现这个解决方案非常优雅,但我在添加更多细节时遇到了问题。例如,我还想按类型分隔案例,所以我尝试添加 ,sum(total_solved) over (partition by case_type, week order by week asc) 但没有奏效(是的,我在简历表上有“case_type”列,对 total_cases_by_now 字段也做了同样的事情并将其添加到许多 select 语句中)。这是正确的做法吗? partition by 中删除week 以供初学者使用。这会将分析窗口折叠为仅一周,而不是当前类型的所有周。而且您也需要对滞后功能进行分区,顺便说一句 @filippo 你能用你当前的分区测试用例更新你的初始帖子吗? 嗨,最后一次更新,我设法重现了确切的问题。介意看看这个小提琴:sqlfiddle.com/#!4/aaedc/1【参考方案2】:

您可以使用MODEL 子句解决这个问题:

with resume_table as
(
    select 1 week, 10 total_new, 3 total_solved from dual union all
    select 2 week, 13 total_new, 15 total_solved from dual union all
    select 3 week, 6 total_new,  7 total_solved from dual
)
select week, prev_total, total_new, total_solved, new_total
from resume_table
model
    dimension by (week)
    measures (total_new, total_solved, 0 prev_total, 0 new_total)
    rules sequential order
    (
        new_total[any] order by week = 
            nvl(new_total[cv(week)-1], 0) + total_new[cv()] - total_solved[cv()]
        ,prev_total[any] order by week = nvl(new_total[cv(week)-1], 0)
    )
order by week;

尽管这假设 WEEK 始终是一个连续的数字。如果不是这样,您将需要添加row_number()。否则,-1 可能不会引用之前的值。

看到这个SQL Fiddle。

【讨论】:

这很花哨。我会仔细看一看。谢谢。 嘿,关于你的模型的几个问题。一周的事情实际上不一定是连续的,但我并没有像你提到的那样添加row_number() 而不会破坏它(对不起,我以前从未使用过模型)。实际上,我不仅在尝试这样做,而且还在数据中添加了更多维度,这在我尝试这样做的任何方面都给我带来了麻烦。如果你能帮我一把:sqlfiddle.com/#!4/aaedc/1 .. 外部group by 只是为了检查数学,我真正感兴趣的是with 子句中的查询。谢谢!【参考方案3】:

RESUME_TABLE 中添加一列(或者创建一个视图,我认为可能会更好):

RESUME_LEFT
WEEK | LEFT
1    | 7
2    | -2
3    | -1

类似这样的:

CREATE VIEW resume_left
  (SELECT week,total_new-total_solved "left" FROM resume_table)

所以在REPORT_TABLE,你可以有这样的定义:

PREV_TOTAL=(SELECT sum(left) FROM RESUME_LEFT WHERE week<REPORT_TABLE.week)

编辑

好的,视图是不必要的:

PREV_TOTAL=(SELECT sum(total_new)-sum(total_solved)
  FROM resume_table
  WHERE week<REPORT_TABLE.week)

【讨论】:

谢谢,虽然它并没有真正起作用。我实际的“周”列是一个日期,这会是一个问题吗?此外,我对示例(数字)进行了一些更改以使其更清晰。 @filippo 它应该可以工作,因为sum(total_new)-sum(total_solved) where week&lt;report.week 实际上是您的prev_total 的数学定义。 date 类型应该不是问题,因为它在 Oracle 中具有可比性,并且与示例中的 week 具有相同的顺序(越旧意味着值越小)。 是的,我认为这是有道理的,尽管它只会返回空列。这是实际的代码:pastebin.com/Cs9BLr5w(很简单,只是更改了标签以匹配这里的问题)。介意看看吗? @filippo 抱歉,我现在没有可访问的 Oracle,所以我必须考虑调试实际代码...你能看看这是否有效吗? pastebin.com/bnSQ5bHG 确实如此。先生,您让我的夜晚值得。非常感谢。

以上是关于处理连续行计算的主要内容,如果未能解决你的问题,请参考以下文章

按列计算连续行和组的距离

SQL 查询 - 计算值大于 X 的连续行数

如何计算连续行的时差

查询以计算Mysql中连续行中距离(经度,纬度)的总和

计算每天 Ms-Sql 总行中的最大连续行

每个客户的连续行之间的Haversine距离