繁体   English   中英

Oracle SQL ROW_NUMBER()窗口,其条件基于滞后

[英]Oracle SQL ROW_NUMBER() window with conditions based on lag

使用Oracle SQL只选择权限,我需要根据条件提供ROW_NUMBER输出。 使用游标或循环这很容易,但目前我必须只使用SQL来执行此任务。

我一直在修补row_number() over子句,我认为这是正确的方法,但我现在卡住了。

我目前的代码 - 或者至少是它的代理:

    WITH MYTABLE (FK_ID,FK_NAME,PK_ID,BIN_FLAG,MONTH,YEAR)AS (
      SELECT 10000,'VARCHAR DESCRIPTION',75057,1,1,2016 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,1,2,2016 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,1,3,2016 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,0,4,2016 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,1,5,2016 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,0,6,2016 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,0,7,2016 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,1,8,2016 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,0,9,2016 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,0,10,2016 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,1,11,2016 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,0,12,2016 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,0,1,2017 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,0,2,2017 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,0,3,2017 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,0,4,2017 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,0,5,2017 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,0,6,2017 FROM DUAL
UNION SELECT 10000,'VARCHAR DESCRIPTION',75057,0,7,2017 FROM DUAL

)

SELECT
   FK_ID
,  FK_NAME
,  PK_ID
,  BIN_FLAG
,  ROW_NUMBER() OVER (PARTITION BY FK_ID,PK_ID,BIN_FLAG ORDER BY YEAR,MONTH,FK_ID,PK_ID ASC) NEEDED_CALC
,  MONTH
,  YEAR
FROM MYTABLE
ORDER BY FK_ID,PK_ID,YEAR,MONTH

这将返回一个如下所示的数据集:

FK_ID   FK_NAME             PK_ID       BIN_FLAG    NEEDED_CALC MONTH   YEAR
10000   VARCHAR DESCRIPTION 75057       1           1           1       2016
10000   VARCHAR DESCRIPTION 75057       1           2           2       2016
10000   VARCHAR DESCRIPTION 75057       1           3           3       2016
10000   VARCHAR DESCRIPTION 75057       0           1           4       2016
10000   VARCHAR DESCRIPTION 75057       1           4           5       2016
10000   VARCHAR DESCRIPTION 75057       0           2           6       2016
10000   VARCHAR DESCRIPTION 75057       0           3           7       2016
10000   VARCHAR DESCRIPTION 75057       1           5           8       2016
10000   VARCHAR DESCRIPTION 75057       0           4           9       2016
10000   VARCHAR DESCRIPTION 75057       0           5           10      2016
10000   VARCHAR DESCRIPTION 75057       1           6           11      2016
10000   VARCHAR DESCRIPTION 75057       0           6           12      2016
10000   VARCHAR DESCRIPTION 75057       0           7           1       2017
10000   VARCHAR DESCRIPTION 75057       0           8           2       2017
10000   VARCHAR DESCRIPTION 75057       0           9           3       2017
10000   VARCHAR DESCRIPTION 75057       0           10          4       2017
10000   VARCHAR DESCRIPTION 75057       0           11          5       2017
10000   VARCHAR DESCRIPTION 75057       0           12          6       2017
10000   VARCHAR DESCRIPTION 75057       0           13          7       2017

我需要的是NEEDED_CALC列,以便在上个月的bin_flag改变时重置calc。

因此,如果bin_flag = 1且前一个月的bin_flag (通过lag函数识别)不同,则NEEDED_CALC的计数器列将重置并再次从1开始。

这是一个“差距和岛屿”问题。 关键是将组标识符分配给具有相同值的相邻行。 有两种简单的方法可以做到这一点:一种是基于lag() ,另一种是row_number()的差异。

第二个是更简单,只需要一个子查询级别:

select t.*,
       row_number() over (partition by fk_id, bin_flag, seqnum_ym - seqnum_bym
                          order by year, month
                         ) as needed_calc
from (select t.*,
             row_number() over (partition by fk_id order by year, month) as seqnum_ym,
             row_number() over (partition by fk_id order by bin_flag, year, month) as seqnum_bym
      from mytable t
     ) t;

行数的差异并不难理解,但确实需要概念上的飞跃。 我建议您运行子查询,看看seqnum_ymseqnum_bym的值是什么,以了解它是如何工作的。

我很幸运能够在12c数据库上工作,你可以使用match recognition子句来完成这项工作。 在这种情况下,我告诉匹配引擎使用与问题中相同的顺序查找其中bin_flag等于前一行的bin_flag的0行或更多行的组。

WITH MYTABLE (FK_ID,FK_NAME,PK_ID,BIN_FLAG,MONTH,YEAR)AS (
 <<<as from the question>>>> 
)
SELECT
 FK_ID 
,FK_NAME
,PK_ID
,BIN_FLAG
,seq NEEDED_CALC
,MONTH
,YEAR
FROM MYTABLE
MATCH_RECOGNIZE (
  ORDER BY year,month,fk_id,pk_id asc
  MEASURES
    count(*) +1 seq 
  ALL ROWS PER MATCH
  PATTERN (a*)
  DEFINE 
  a AS bin_flag = prev(bin_flag)
  )
ORDER BY FK_ID,PK_ID,YEAR,MONTH

匹配识别对于此类查询而言是一种有用的工具,您可以在这些查询中查找跨行组的模式。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM