暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Oracle 分析vs子查询

askTom 2017-02-22
245

问题描述

我有一个示例数据集,其中我想计算给定事件的RET状态数。

这是一个简单的查询,但现在他们希望计数重置时,有一个不同的created_by。
Barney在这个例子中会将event_id = 1的计数重置为1,而事件5仍然会返回2。

EVENT_ID CREATED_DATE CREATED_BY STATUS
1 18/SEP/2016 22:25:00 FRED S1
1 18/SEP/2016 22:25:47 FRED S2
1 21/SEP/2016 23:04:56 FRED S3
1 13/OCT/2016 16:21:52 FRED RET -- don't count me anymore
1 06/NOV/2016 13:46:15 FRED S1
1 07/NOV/2016 20:26:37 FRED S2
1 08/NOV/2016 14:26:05 FRED S3
1 13/NOV/2016 17:59:27 FRED RET -- don't count me anymore
1 31/DEC/2016 12:03:00 FRED S1
1 08/JAN/2017 15:08:31 FRED RET -- don't count me anymore
1 10/JAN/2017 10:09:00 BARNEY S1
1 19/FEB/2017 19:22:33 FRED RET -- count me

5 22/SEP/2016 05:37:00 FRED S1
5 22/SEP/2016 05:37:47 FRED S2
5 25/SEP/2016 06:16:56 FRED S3
5 16/OCT/2016 23:33:52 FRED RET -- count me
5 09/NOV/2016 20:58:15 FRED S1
5 11/NOV/2016 03:38:37 FRED S2
5 11/NOV/2016 21:38:05 FRED S3
5 17/NOV/2016 01:11:27 FRED RET -- count me


我认为下面的查询解决了问题
select count(*)
from recent_eg r
where r.status = 'RET'
and r.event_id = :event
and r.created_date > 
 coalesce( -- get most recent instance of differing name
   (select max(s.created_date)
    from recent_eg s
    where r.event_id = s.event_id
    and r.created_by != s.created_by
    )
 ,to_date(1,'j') -- dawn of time
 )


但是我开始探索分析的问题,但无法弄清楚如何做到这一点-或者if 应该这样做。它将取决于所需的输出吗?应用程序只需要计数,所以它可以应用业务规则,它不需要在行/col格式,但我认为也许在内联视图中使用的分析可能比子查询便宜?

select event_id, created_date, created_by, status
 ,count(case when status='RET' then 1 end) over (partition by event_id order by created_date) cumulative_return_count
 -- desired outcome, but need this to be dynamic
 ,case rownum when 20 then 2 when 12 then 1 end return_count_reset_from_barney
from recent_eg 
order by event_id, created_date;


实际表具有代理pk,event_id上的单独索引,created_date

livesql链接应该更好地显示数据?

(回复: 404链接地址准确,我不得不重新共享脚本。显然仍然习惯于关于编辑、运行、保持最新的livesql。我不太使用它。asktom测试用例需要共享给公众吗?)

专家解答

您可以在同一事件上有多个RET事务created_by不同的人吗?

假设答案是否定的,您可以使用分析来解决这个问题!:)

以下是方法:

1.查找谁为事件创建了第一个RET事务
2.查找不是第1步找到的人提交的交易的最长日期
3.对created_date大于2的值的RET事务进行计数如果2返回null,则只需对事件使用第一个事务

那么这在代码中看起来如何?

那么你可以用first_value解决步骤1。确保你 “忽略空值”:

CREATE TABLE "RECENT_EG"  
   ("EVENT_ID" NUMBER NOT NULL ENABLE, 
  "CREATED_DATE" DATE NOT NULL ENABLE, 
  "CREATED_BY" VARCHAR2(50 BYTE) NOT NULL ENABLE, 
  "STATUS" VARCHAR2(10 BYTE) 
   );

insert into recent_eg(event_id, created_date, created_by, status) values (1,to_date('18/9/2016 22:25:00','dd/mm/yyyy hh24:mi:ss'),'FRED','S1');
insert into recent_eg(event_id, created_date, created_by, status) values (1,to_date('18/9/2016 22:25:47','dd/mm/yyyy hh24:mi:ss'),'FRED','S2');
insert into recent_eg(event_id, created_date, created_by, status) values (1,to_date('21/9/2016 23:04:56','dd/mm/yyyy hh24:mi:ss'),'FRED','S3');
insert into recent_eg(event_id, created_date, created_by, status) values (1,to_date('13/10/2016 16:21:52','dd/mm/yyyy hh24:mi:ss'),'FRED','RET');
insert into recent_eg(event_id, created_date, created_by, status) values (1,to_date('06/11/2016 13:46:15','dd/mm/yyyy hh24:mi:ss'),'FRED','S1');
insert into recent_eg(event_id, created_date, created_by, status) values (1,to_date('07/11/2016 20:26:37','dd/mm/yyyy hh24:mi:ss'),'FRED','S2');
insert into recent_eg(event_id, created_date, created_by, status) values (1,to_date('08/11/2016 14:26:05','dd/mm/yyyy hh24:mi:ss'),'FRED','S3');
insert into recent_eg(event_id, created_date, created_by, status) values (1,to_date('13/11/2016 17:59:27','dd/mm/yyyy hh24:mi:ss'),'FRED','RET');
insert into recent_eg(event_id, created_date, created_by, status) values (1,to_date('31/12/2016 12:03:00','dd/mm/yyyy hh24:mi:ss'),'FRED','S1');
insert into recent_eg(event_id, created_date, created_by, status) values (1,to_date('08/1/2017 15:08:31','dd/mm/yyyy hh24:mi:ss'),'FRED','RET');
insert into recent_eg(event_id, created_date, created_by, status) values (1,to_date('10/1/2017 10:09:00','dd/mm/yyyy hh24:mi:ss'),'BARNEY','S1');
insert into recent_eg(event_id, created_date, created_by, status) values (1,to_date('19/2/2017 19:22:33','dd/mm/yyyy hh24:mi:ss'),'FRED','RET');
insert into recent_eg(event_id, created_date, created_by, status) values (5,to_date('22/9/2016 05:37:00','dd/mm/yyyy hh24:mi:ss'),'FRED','S1');
insert into recent_eg(event_id, created_date, created_by, status) values (5,to_date('22/9/2016 05:37:47','dd/mm/yyyy hh24:mi:ss'),'FRED','S2');
insert into recent_eg(event_id, created_date, created_by, status) values (5,to_date('25/9/2016 06:16:56','dd/mm/yyyy hh24:mi:ss'),'FRED','S3');
insert into recent_eg(event_id, created_date, created_by, status) values (5,to_date('16/10/2016 23:33:52','dd/mm/yyyy hh24:mi:ss'),'FRED','RET');
insert into recent_eg(event_id, created_date, created_by, status) values (5,to_date('09/11/2016 20:58:15','dd/mm/yyyy hh24:mi:ss'),'FRED','S1');
insert into recent_eg(event_id, created_date, created_by, status) values (5,to_date('11/11/2016 03:38:37','dd/mm/yyyy hh24:mi:ss'),'FRED','S2');
insert into recent_eg(event_id, created_date, created_by, status) values (5,to_date('11/11/2016 21:38:05','dd/mm/yyyy hh24:mi:ss'),'FRED','S3');
insert into recent_eg(event_id, created_date, created_by, status) values (5,to_date('17/11/2016 01:11:27','dd/mm/yyyy hh24:mi:ss'),'FRED','RET');

select event_id, created_date, created_by, status  
      ,first_value(case when status='RET' then created_by end) 
  ignore nulls over (partition by event_id order by created_date) fv
from recent_eg;

EVENT_ID  CREATED_DATE          CREATED_BY  STATUS  FV    
1         18-SEP-2016 22:25:00  FRED        S1            
1         18-SEP-2016 22:25:47  FRED        S2            
1         21-SEP-2016 23:04:56  FRED        S3            
1         13-OCT-2016 16:21:52  FRED        RET     FRED  
1         06-NOV-2016 13:46:15  FRED        S1      FRED  
1         07-NOV-2016 20:26:37  FRED        S2      FRED  
1         08-NOV-2016 14:26:05  FRED        S3      FRED  
1         13-NOV-2016 17:59:27  FRED        RET     FRED  
1         31-DEC-2016 12:03:00  FRED        S1      FRED  
1         08-JAN-2017 15:08:31  FRED        RET     FRED  
1         10-JAN-2017 10:09:00  BARNEY      S1      FRED  
1         19-FEB-2017 19:22:33  FRED        RET     FRED  
5         22-SEP-2016 05:37:00  FRED        S1            
5         22-SEP-2016 05:37:47  FRED        S2            
5         25-SEP-2016 06:16:56  FRED        S3            
5         16-OCT-2016 23:33:52  FRED        RET     FRED  
5         09-NOV-2016 20:58:15  FRED        S1      FRED  
5         11-NOV-2016 03:38:37  FRED        S2      FRED  
5         11-NOV-2016 21:38:05  FRED        S3      FRED  
5         17-NOV-2016 01:11:27  FRED        RET     FRED 


然后,您可以通过返回最大created_date在步骤2中找到日期 (如果存在),其中:

-在步骤1中计算的created_by <> fv
-对于分区中的所有行 “之前无界和之后无界之间的行”

在此阶段还返回事件的最小日期:

with rws as (
  select event_id, created_date, created_by, status  
    ,first_value(case when status='RET' then created_by end) 
       ignore nulls over (partition by event_id order by created_date) fv
  from recent_eg  
)
  select r.*,
         max(case when created_by <> fv then created_date end) 
           over (partition by event_id order by created_date 
             rows between unbounded preceding and unbounded following) mx,
         min(created_date) over (partition by event_id order by created_date) mn
  from   rws r;

EVENT_ID  CREATED_DATE          CREATED_BY  STATUS  FV    MX                    MN                    
1         18-SEP-2016 22:25:00  FRED        S1            10-JAN-2017 10:09:00  18-SEP-2016 22:25:00  
1         18-SEP-2016 22:25:47  FRED        S2            10-JAN-2017 10:09:00  18-SEP-2016 22:25:00  
1         21-SEP-2016 23:04:56  FRED        S3            10-JAN-2017 10:09:00  18-SEP-2016 22:25:00  
1         13-OCT-2016 16:21:52  FRED        RET     FRED  10-JAN-2017 10:09:00  18-SEP-2016 22:25:00  
1         06-NOV-2016 13:46:15  FRED        S1      FRED  10-JAN-2017 10:09:00  18-SEP-2016 22:25:00  
1         07-NOV-2016 20:26:37  FRED        S2      FRED  10-JAN-2017 10:09:00  18-SEP-2016 22:25:00  
1         08-NOV-2016 14:26:05  FRED        S3      FRED  10-JAN-2017 10:09:00  18-SEP-2016 22:25:00  
1         13-NOV-2016 17:59:27  FRED        RET     FRED  10-JAN-2017 10:09:00  18-SEP-2016 22:25:00  
1         31-DEC-2016 12:03:00  FRED        S1      FRED  10-JAN-2017 10:09:00  18-SEP-2016 22:25:00  
1         08-JAN-2017 15:08:31  FRED        RET     FRED  10-JAN-2017 10:09:00  18-SEP-2016 22:25:00  
1         10-JAN-2017 10:09:00  BARNEY      S1      FRED  10-JAN-2017 10:09:00  18-SEP-2016 22:25:00  
1         19-FEB-2017 19:22:33  FRED        RET     FRED  10-JAN-2017 10:09:00  18-SEP-2016 22:25:00  
5         22-SEP-2016 05:37:00  FRED        S1                                  22-SEP-2016 05:37:00  
5         22-SEP-2016 05:37:47  FRED        S2                                  22-SEP-2016 05:37:00  
5         25-SEP-2016 06:16:56  FRED        S3                                  22-SEP-2016 05:37:00  
5         16-OCT-2016 23:33:52  FRED        RET     FRED                        22-SEP-2016 05:37:00  
5         09-NOV-2016 20:58:15  FRED        S1      FRED                        22-SEP-2016 05:37:00  
5         11-NOV-2016 03:38:37  FRED        S2      FRED                        22-SEP-2016 05:37:00  
5         11-NOV-2016 21:38:05  FRED        S3      FRED                        22-SEP-2016 05:37:00  
5         17-NOV-2016 01:11:27  FRED        RET     FRED                        22-SEP-2016 05:37:00  


您现在要做的就是返回日期大于或等于上面计算的最大日期 (如果为null,则返回最小值) 的RET事务。然后分组并计算结果:

with rws as (
  select event_id, created_date, created_by, status  
    ,first_value(case when status='RET' then created_by end) 
       ignore nulls over (partition by event_id order by created_date) fv
  from recent_eg  
), dates as (
  select r.*,
         max(case when created_by <> fv then created_date end) 
           over (partition by event_id order by created_date 
             rows between unbounded preceding and unbounded following) mx,
         min(created_date) over (partition by event_id order by created_date) mn
  from   rws r
)
  select event_id, count(*) from dates
  where  status = 'RET'
  and    created_date >= coalesce (mx, mn)
  group  by event_id
  order by event_id;

EVENT_ID  COUNT(*)  
1         1         
5         2    


说到哪个 “更好”:

解析解只引用该表一次。所以这应该做得更少。但这确实取决于您的数据和可用的索引。确保您对真实数据进行测试和验证!

在LiveSQL上-是的,请让您提交给AskTOM public的所有脚本!
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论