有一个困扰多日的问题,求高手解答:
记录格式如下:
cardno date time amt oper_id
-------- -------- ------ --------- -------
14010088 20110401 091102 -34.00 jack
14010028 20110401 100101 -100.00 jack
14010002 20110401 100205 -50.00 jack
14010135 20110401 100314 -25.00 jack
14010046 20110401 100546 -10.00 jack
14010970 20110401 100720 -18.00 jack
14010950 20110401 100725 -200.00 tom
14010009 20110401 113048 -30.00 jack
14010077 20110401 113150 -110.00 jack
14020076 20110401 160205 -90.00 simen
14020060 20110401 160508 -3.00 jack
14020111 20110401 160614 -320.00 jack
14010701 20110401 160734 -15.00 jack
14020049 20110402 090500 -10.00 john
14020057 20110402 090614 -600.00 john
14010356 20110402 090805 -50.00 john
......字段说明:
cardno --卡片号
date --日期
time --时间(hhmmss)
amt --金额
oper_id --操作员要求:
每天同一个oper_id在间隔3分钟之内的记录视为连续记录,将连续3笔以上的记录找出来,期望得出如下结果:日期 起始时间 起始卡号 终止时间 终止卡号 记录数 oper_id
-------- -------- -------- -------- -------- ------ -------
20110401 100101 14010028 100720 14010970 5 jack
20110401 160508 14020060 160734 14010701 3 jack
20110402 090500 14020049 090805 14010356 3 john
......
记录格式如下:
cardno date time amt oper_id
-------- -------- ------ --------- -------
14010088 20110401 091102 -34.00 jack
14010028 20110401 100101 -100.00 jack
14010002 20110401 100205 -50.00 jack
14010135 20110401 100314 -25.00 jack
14010046 20110401 100546 -10.00 jack
14010970 20110401 100720 -18.00 jack
14010950 20110401 100725 -200.00 tom
14010009 20110401 113048 -30.00 jack
14010077 20110401 113150 -110.00 jack
14020076 20110401 160205 -90.00 simen
14020060 20110401 160508 -3.00 jack
14020111 20110401 160614 -320.00 jack
14010701 20110401 160734 -15.00 jack
14020049 20110402 090500 -10.00 john
14020057 20110402 090614 -600.00 john
14010356 20110402 090805 -50.00 john
......字段说明:
cardno --卡片号
date --日期
time --时间(hhmmss)
amt --金额
oper_id --操作员要求:
每天同一个oper_id在间隔3分钟之内的记录视为连续记录,将连续3笔以上的记录找出来,期望得出如下结果:日期 起始时间 起始卡号 终止时间 终止卡号 记录数 oper_id
-------- -------- -------- -------- -------- ------ -------
20110401 100101 14010028 100720 14010970 5 jack
20110401 160508 14020060 160734 14010701 3 jack
20110402 090500 14020049 090805 14010356 3 john
......
insert into tb select '14010088','20110401','091102',-34.00,'jack'
insert into tb select '14010028','20110401','100101',-100.00,'jack'
insert into tb select '14010002','20110401','100205',-50.00,'jack'
insert into tb select '14010135','20110401','100314',-25.00,'jack'
insert into tb select '14010046','20110401','100546',-10.00,'jack'
insert into tb select '14010970','20110401','100720',-18.00,'jack'
insert into tb select '14010950','20110401','100725',-200.00,'tom'
insert into tb select '14010009','20110401','113048',-30.00,'jack'
insert into tb select '14010077','20110401','113150',-110.00,'jack'
insert into tb select '14020076','20110401','160205',-90.00,'simen'
insert into tb select '14020060','20110401','160508',-3.00,'jack'
insert into tb select '14020111','20110401','160614',-320.00,'jack'
insert into tb select '14010701','20110401','160734',-15.00,'jack'
insert into tb select '14020049','20110402','090500',-10.00,'john'
insert into tb select '14020057','20110402','090614',-600.00,'john'
insert into tb select '14010356','20110402','090805',-50.00,'john'
go
;WITH C1 AS(
select row_number()over(partition by oper_id order by [date],[time])rn,CARDNO,[DATE]+' '+STUFF(STUFF(time,3,0,':'),6,0,':')dt,[DATE],[time],amt,oper_id from tb
),c2 as(
select *,1 as flg from c1 a where not exists(select 1 from C1 where oper_id=a.oper_id and DATEDIFF(mi,dt,a.dt)<=3 and DATEDIFF(mi,dt,a.dt)>0)
union all
select a.*,b.flg+1 from C1 a inner join C2 b on a.oper_id=b.oper_id and a.rn=b.rn+1 and DATEDIFF(mi,b.dt,a.dt)<=3
),c3 as(
select * from c2 a where flg>=3 and not exists(select 1 from c2 where oper_id=a.oper_id and rn=a.rn+1 and flg=a.flg+1)
)select b.[date] 日期,b.[time]起始时间,b.cardno 起始卡号,a.[time]终止时间,a.cardno 终止卡号,a.flg 记录数,a.oper_id
from c3 a inner join C1 b on a.oper_id=b.oper_id and b.rn=a.rn-a.flg+1
/*
日期 起始时间 起始卡号 终止时间 终止卡号 记录数 oper_id
-------- ------ ---------- ------ ---------- ----------- ----------
20110401 100101 14010028 100720 14010970 5 jack
20110401 160508 14020060 160734 14010701 3 jack
20110402 090500 14020049 090805 14010356 3 john(3 行受影响)
*/
go
drop table tb
use tempdb;
go
declare @t table(
cardno char(8)
,[date] char(8)
,[time] char(6)
,amt decimal(9,2)
,oper_id varchar(8)
);
insert into @t select '14010088','20110401','091102',-34.00,'jack'
insert into @t select '14010028','20110401','100101',-100.00,'jack'
insert into @t select '14010002','20110401','100205',-50.00,'jack'
insert into @t select '14010135','20110401','100314',-25.00,'jack'
insert into @t select '14010046','20110401','100546',-10.00,'jack'
insert into @t select '14010970','20110401','100720',-18.00,'jack'
insert into @t select '14010950','20110401','100725',-200.00,'tom'
insert into @t select '14010009','20110401','113048',-30.00,'jack'
insert into @t select '14010077','20110401','113150',-110.00,'jack'
insert into @t select '14020076','20110401','160205',-90.00,'simen'
insert into @t select '14020060','20110401','160508',-3.00,'jack'
insert into @t select '14020111','20110401','160614',-320.00,'jack'
insert into @t select '14010701','20110401','160734',-15.00,'jack'
insert into @t select '14020049','20110402','090500',-10.00,'john'
insert into @t select '14020057','20110402','090614',-600.00,'john'
insert into @t select '14010356','20110402','090805',-50.00,'john'
;WITH
--增加排序
Result AS
(
SELECT ROW_NUMBER() over(order by date,time) as IndexNo,* FROM @t
),
--标记每条记录前一条和后一条是否在3分钟之内
Results as
(
SELECT * from Result r1
outer apply
(
SELECT CASE WHEN time<=r1.time+300 then 1 else null end as tag1
FROM Result
WHERE oper_id=r1.oper_id and date=r1.date and IndexNo=r1.IndexNo+1
) r2
outer apply
(
SELECT CASE WHEN time>=r1.time-300 then 1 else null end as tag2
FROM Result
WHERE oper_id=r1.oper_id and date=r1.date and IndexNo=r1.IndexNo-1
) r3
),
--连续记录第一条
Mins AS
(
SELECT IndexNo,cardno,date,time,oper_id
FROM Results
WHERE tag2 IS NULL and tag1=1
),
--连续记录最后条
Maxs AS
(
SELECT IndexNo,cardno,date,time,oper_id
FROM Results
WHERE tag1 IS NULL and tag2=1
),
--第一条跟最后一条记录编号关联
Finals AS
(
SELECT *
FROM Mins m1
CROSS APPLY
(
SELECT MIN(indexno) as IndexNo2
from Maxs
where oper_id=m1.oper_id and date=m1.date and IndexNo>m1.IndexNo
) m2
)
SELECT m1.date as [日期], m1.time as [起始时间],m1.cardno as [起始卡号], m2.time as [终止时间], m2.cardno as [终止卡号]
,m2.IndexNo-m1.IndexNo+1 as [记录数], m1.oper_id
FROM Finals m1 JOIN
Maxs m2 ON m1.IndexNo2=m2.IndexNo
WHERE m2.IndexNo-m1.IndexNo>=2/*结果集日期 起始时间 起始卡号 终止时间 终止卡号 记录数 oper_id
-------- -------- -------- ------ -------- ------ --------
20110401 100101 14010028 100720 14010970 5 jack
20110401 160508 14020060 160734 14010701 3 jack
20110402 090500 14020049 090805 14010356 3 john
*/
insert into tb select '14010088','20110401','091102',-34.00,'jack'
insert into tb select '14010028','20110401','100101',-100.00,'jack'
insert into tb select '14010002','20110401','100205',-50.00,'jack'
insert into tb select '14010135','20110401','100314',-25.00,'jack'
insert into tb select '14010046','20110401','100546',-10.00,'jack'
insert into tb select '14010970','20110401','100720',-18.00,'jack'
insert into tb select '14010950','20110401','100725',-200.00,'tom'
insert into tb select '14010009','20110401','113048',-30.00,'jack'
insert into tb select '14010077','20110401','113150',-110.00,'jack'
insert into tb select '14020076','20110401','160205',-90.00,'simen'
insert into tb select '14020060','20110401','160508',-3.00,'jack'
insert into tb select '14020111','20110401','160614',-320.00,'jack'
insert into tb select '14010701','20110401','160734',-15.00,'jack'
insert into tb select '14020049','20110402','090500',-10.00,'john'
insert into tb select '14020057','20110402','090614',-600.00,'john'
insert into tb select '14010356','20110402','090805',-50.00,'john'
go
;WITH C1 AS(
--用stuff函数将时间格式化后与日期连接,成为一个完整的时间日期型值,同时,以oper_id分组,时间排序,设置分组序号 rn
select row_number()over(partition by oper_id order by [date],[time])rn,CARDNO,[DATE]+' '+STUFF(STUFF(time,3,0,':'),6,0,':')dt,[DATE],[time],amt,oper_id from tb
),c2 as(
--先找出某个记录前3分钟内该 id 没有记录的记录,作为递归切入点,flg是一个标志,表明它是递归的第一条
select *,1 as flg from c1 a where not exists(select 1 from C1 where oper_id=a.oper_id and DATEDIFF(mi,dt,a.dt)<=3 and DATEDIFF(mi,dt,a.dt)>0)
union all
--递归,找出从递归切入点处开始的,时间与递归循环内前一条记录的id相同,且时间小于等于3分钟的记录,并以找不到3分钟内的记录为递归边界条件
--同时,每一次递归,循环数标志加1
select a.*,b.flg+1 from C1 a inner join C2 b on a.oper_id=b.oper_id and a.rn=b.rn+1 and DATEDIFF(mi,b.dt,a.dt)<=3
),c3 as(
--找出循环数>=3的最后一次递归的记录
select * from c2 a where flg>=3 and not exists(select 1 from c2 where oper_id=a.oper_id and rn=a.rn+1 and flg=a.flg+1)
)--以连接的方式获得递归循环起始和终止的时间和卡号,并输出
select b.[date] 日期,b.[time]起始时间,b.cardno 起始卡号,a.[time]终止时间,a.cardno 终止卡号,a.flg 记录数,a.oper_id
from c3 a inner join C1 b on a.oper_id=b.oper_id and b.rn=a.rn-a.flg+1
/*
日期 起始时间 起始卡号 终止时间 终止卡号 记录数 oper_id
-------- ------ ---------- ------ ---------- ----------- ----------
20110401 100101 14010028 100720 14010970 5 jack
20110401 160508 14020060 160734 14010701 3 jack
20110402 090500 14020049 090805 14010356 3 john(3 行受影响)
*/
go
drop table tb
递归可以用循环程序来完成,递归就是循环.
明白了算法,剩下的只有语法了.
消息 530,级别 16,状态 1,第 1 行
语句被终止。完成执行语句前已用完最大递归 100。
实际记录条数约10万条,继续盼望晴天的指点!