SQL 求股市连续跌的天数

题目
表a包含日期和股市涨跌标记(1:跌,0:涨),请用任意SQL得到连续跌的天数,得到例如表b的效果。
 
表a

adate(日期)tag(股市涨跌标记(1:跌,0:涨)
2023-1-161
2023-1-151
2023-1-141
2023-1-131
2023-1-120
2023-1-110
2023-1-100
2023-1-91
2023-1-80

表b

adate(日期)tag(股市涨跌标记(1:跌,0:涨)conti(连续跌的天数)
2023-1-1614
2023-1-1513
2023-1-1412
2023-1-1311
2023-1-1200
2023-1-1100
2023-1-1000
2023-1-911
2023-1-800

求连续天数类型的题,我们核心的思路就是找出能够将每组连续值分开的标记,下面介绍该题的两种解法。

方法一
第一种方法也是求连续类问题时用到的最常规的方法,对adate和tag两列得到排好的row_number,作差,根据差值即可得到分组。最后再对组内排序即可,完整代码如下

Select adate, tag,
	Case When tag = 0 Then 0 Else row_number()Over(Partition By diff Order By adate) End conti From
	(
		Select adate, tag,
				Case When tag = 1 Then
					row_number()Over(Order By adate desc) - 
					row_number()Over(Partition By tag Order By adate desc)
					End  diff
				From a
	)tmp
Order By adate desc

下面是逐步拆解
首先对两列进行分组排序,得到两列值。

Select adate, tag,
       row_number()Over(Order By adate desc) rn1, 
       row_number()Over(Partition By tag Order By adate desc)rn2
  From a

结果为

adate(日期)tag(股市涨跌标记(1:跌,0:涨)rn1rn2
2023-1-16111
2023-1-15122
2023-1-14133
2023-1-13144
2023-1-12051
2023-1-11062
2023-1-10073
2023-1-9185
2023-1-8094

对于tag=1时,观察 rn1 和 rn2 ,不难发现,对于连续跌的天,两值作差得到的数是一致的。

Select adate, tag,
		Case When tag = 1 Then
			row_number()Over(Order By adate desc) - 
			row_number()Over(Partition By tag Order By adate desc)
		End  diff
  From a
adate(日期)tag(股市涨跌标记(1:跌,0:涨)diff
2023-1-1610
2023-1-1510
2023-1-1410
2023-1-1310
2023-1-120
2023-1-110
2023-1-100
2023-1-913
2023-1-80

对作差得到的值作为标记进行分组,最后对分组以日期为顺序进行排序即可。

Select adate, tag,
	Case When tag = 0 Then 0 Else row_number()Over(Partition By diff Order By adate) End conti From
	(
		Select adate, tag,
				Case When tag = 1 Then
					row_number()Over(Order By adate desc) - 
					row_number()Over(Partition By tag Order By adate desc)
					End  diff
				From a
	)tmp
Order By adate desc
adate(日期)tag(股市涨跌标记(1:跌,0:涨)conti(连续跌的天数)
2023-1-1614
2023-1-1513
2023-1-1412
2023-1-1311
2023-1-1200
2023-1-1100
2023-1-1000
2023-1-911
2023-1-800

方法二
第二种方法是先用Lag开窗将发生涨跌变化的天标记为1,其他为0,然后用Sum开窗求和即可得到连续涨 / 跌天的统一分组标记。完整代码如下

Select adate, tag, 
		Case When tag = 1 Then row_number()Over(Partition By grp Order By adate Desc) Else 0 End conti
	From (
	    Select adate, tag, change_flag,
	        Sum(change_flag)Over(Order By adate Desc) grp
			    From (
			        Select adate, tag,
			            Case When Lag(tag)Over(Order By adate Desc) = tag Then 0 Else 1 End change_flag
			        From a Order By adate
			    ) t1
	) t2
Order By adate Desc;

下面是逐步拆解
首先对tag进行偏移,偏移列的值与原tag值不一致的即为发生涨跌变化的行,将发生变化的行标记为1,未发生变化的行标记为0。

 Select adate, tag,
     Case When Lag(tag)Over(Order By adate Desc) = tag Then 0 Else 1 End change_flag
 From a Order By adate Desc

得到

adate(日期)tag(股市涨跌标记(1:跌,0:涨)change_flag
2023-1-1611
2023-1-1510
2023-1-1410
2023-1-1310
2023-1-1201
2023-1-1100
2023-1-1000
2023-1-911
2023-1-801

对变化标记change_flag进行开窗求和,
由于变化行为1未变化行为0,此时窗口开到的部分,
对于未变化的行有:求和一致为上一次发生变化时的sum值;
对于发生变化的行有:求和为上一次变化时的sum值+1。

Select adate, tag, change_flag,
    Sum(change_flag)Over(Order By adate Desc) grp
  From (
      Select adate, tag,
          Case When Lag(tag)Over(Order By adate Desc) = tag Then 0 Else 1 End change_flag
      From a Order By adate
  ) 

得到

adate(日期)tag(股市涨跌标记(1:跌,0:涨)change_flaggrp
2023-1-16111
2023-1-15101
2023-1-14101
2023-1-13101
2023-1-12012
2023-1-11002
2023-1-10002
2023-1-9113
2023-1-8014

以grp将连续相同的值(连续跌/涨)分组,
分组后case when对tag = 1的行按照组进行row_number开窗即可得到结果。

Select adate, tag, 
		Case When tag = 1 Then row_number()Over(Partition By grp Order By adate Desc) Else 0 End conti
	From (
	    Select adate, tag, change_flag,
	        Sum(change_flag)Over(Order By adate Desc) grp
			    From (
			        Select adate, tag,
			            Case When Lag(tag)Over(Order By adate Desc) = tag Then 0 Else 1 End change_flag
			        From a Order By adate
			    ) t1
	) t2
Order By adate Desc;
adate(日期)tag(股市涨跌标记(1:跌,0:涨)conti(连续跌的天数)
2023-1-1614
2023-1-1513
2023-1-1412
2023-1-1311
2023-1-1200
2023-1-1100
2023-1-1000
2023-1-911
2023-1-800
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值