我试图每次为每个成员循环一次日期,并定义一个成员的状态。该查询工作得很好,但是这要花费很多时间。我想不出另一种方法来获得此结果集。
逻辑
For every member, look for minimum date, then add 30 to it, if any date falls under 30, then status is initial_1. If it is greater than 30 and less than 30+15, then Reauth_1_1, if date greater than 30+16 , then it is intial_2.
MemberID DOS Status
HHH00031200 7/17/2014 Initial_1
HHH00031200 7/29/2014 Initial_1
HHH00031200 8/21/2014 Re-Auth_1_1
HHH00031200 8/27/2014 Re-Auth_1_1
HHH50000000 5/23/2016 Initial_1
HHH50000000 7/19/2016 Initial_2
HHH50000000 9/13/2016 Initial_3
HHH88844900 9/19/2015 Initial_1
HHH88844900 10/22/2015 Re-Auth_1_1
HHH88844900 11/24/2015 Re-Auth_1_2
HHH88844900 12/10/2015 Re-Auth_1_2
HHH22227700 1/16/2014 Initial_1
HHH22227700 2/21/2014 Re-Auth_1_1
HHH22227700 2/25/2014 Re-Auth_1_1
HHH22227700 3/5/2014 Re-Auth_1_1
HHH22227700 1/1/2015 Initial_2
HHH22227700 1/15/2015 Initial_2
HHH22227700 1/20/2015 Initial_2
HHH22227700 2/10/2015 Re-Auth_2_1
HHH22227700 2/12/2015 Re-Auth_2_1
HHH22227700 2/17/2015 Re-Auth_2_1
HHH22227700 2/19/2015 Re-Auth_2_1
HHH22227700 2/25/2015 Re-Auth_2_1
HHH22227700 2/26/2015 Re-Auth_2_1
查询:
--drop table #Temp_SO_Check
Select
*
Into #Temp_SO_Check
From #auth;
CREATE INDEX IDX_C_Users_UserID ON #Temp_SO_Check(MemberID,DOS,LoopLogic);
While Exists
(
Select Top 1
MemberID
From #Temp_SO_Check
Where LoopLogic Is Null
)
Begin
Select Top 1
@ID = MemberID
, @StartDate = DOS
From #Temp_SO_Check
Where LoopLogic Is Null
Order By
MemberID
, DOS;
If @PrevID <> @ID
Begin
set @Flag=1;
Set @LoopLogic ='Initial_'+Cast(@Flag as nvarchar(50)) ;
Set @PrevID = @ID;
set @ReauthFlag=0;
end
else
Begin
if((@LookupDate is not null) and (datediff(day,@LookupDate,@StartDate))<14)
Begin
Set @LoopLogic ='Re-Auth_'+Cast(@Flag as nvarchar(50)) ;
End
Else if ((@LookupDate is not null) and (datediff(day,@LookupDate,@StartDate))>14)
Begin
set @Flag=@Flag+1;
set @ReauthFlag=0;
Set @LoopLogic ='Initial_'+Cast(@Flag as nvarchar(50));
End
End
Set @LookupDate = DateAdd(Day, 30, @StartDate);
if( (@LoopLogic like '%Re-Auth_%') and (@LookupDate<>@LookupDate1))
Begin
Set @ReauthFlag=@ReauthFlag+1;
Set @LoopLogic ='Re-Auth_'+Cast(@Flag as nvarchar(50))+'_'+Cast(@ReauthFlag as nvarchar(50)) ;
End
set @LookupDate1=@LookupDate;
Update
#Temp_SO_Check
Set
LoopLogic = @LoopLogic
Where
MemberID = @ID
And DOS Between @StartDate
And @LookupDate;
End;
#auth表包含我的数据,然后是循环统计信息。我有766000条记录,并且耗时超过1小时30分钟,并且仍在运行。
有人可以帮助我微调此查询吗?
答案 0 :(得分:0)
您根本不需要循环...而SQL Server中的循环是性能杀手。这是一个简单的解决方案
--determine the minimum date for the table, and add 30 days to it
declare @minDate date = (select dateadd(day,30,min(DOS)) DT from YourTable)
--based on your logic in the question, update the status column
update YouTable
set [Status] = case
when DOS < @minDate then 'initial_1'
when DOS > @minDate and DOS < dateadd(day,15,@minDate) then 'Reauth_1_1'
else 'intial_2'
end
如果最短日期是每个员工的日期,那么我会在CTE中处理
;with cte as(
select
MemberID
,DOS
,[Status]
,MinDOS = dateadd(day,30,min(DOS) over (partition by MemberID))
from YourTable)
update cte
set [Status] = case
when DOS < MinDOS then 'initial_1'
when DOS > MinDOS and DOS < dateadd(day,15,MinDOS) then 'Reauth_1_1'
else 'intial_2'
end
答案 1 :(得分:0)
我不知道如何在评论中复制此内容,也不想回答问题。 如果有更好的方式发布详细信息,请纠正我。
这是要求:
1.For any member, look for minimum DOS, now add +30 to this date.
2. As a first step we need to slice the data by 30 days window.
3.For example, minimum DOS for memberid HHH00031200 is 7/17/2014.
4. Add 30 days to it and it is 8/16/2014.
5. Now for any date between 7/17/2014-8/16/2014 the status is Initial_1(This is the first window).
6.Now again add + 30 days to 8/16/2018.
7.But in next window, if any date is within 15 days(i.e.8/16/2014+ 15 days) , it will be Reauth_1_1.if the date is > 15 days, then Initial_2 and this goes on till we loop all the records.
8.For example, Let us calculate the window for HHH88844900.
i. First Window-09/19/2015+30=10/19/2015
ii. Second Window-10/19/2015+30=11/21/2015
iii.third Window-11/21/2015+30=12/24/2015
9.First Loop Logic is always Initial_1.
10.Second Date is 10/22/2015 which is 3 days after the first window and that is why it is Re-Auth_1_1.
11.Third Date is 11/24/2015 which is 3 days after second window and that is why it is Re-Auth_1_2. similarlly last one.
12.If the dates were > 15 days from the 30 days window, it would be Initial_2 ,for example MemeberId HHH50000000.
13.For memberId HHH22227700 , once we have the Initial_2, then the next dates would be Re-Auth_2_1 and so on..
让我知道这是否有意义。感谢您的所有帮助。
答案 2 :(得分:0)
这是一个非常有趣的问题。最初,我曾希望使用lag()
之类的窗口函数来找到一种解决方案,但是由于没有很好的方法来知道有多少条记录将给定记录与代表相同间隔开始的先前记录分开,因此我最终有了使用递归。我不确定这在非常大的数据集上的效果如何,并且如果每个MemberID
都有大量条目,则可能必须添加一个MAXRECURSION
query hint,但我希望这样做至少会比迭代解决方案更好。查询中的注释说明了正在发生的事情。
-- Sample data from the question.
declare @SampleData table (MemberID varchar(32), DOS date);
insert @SampleData values
('HHH00031200', '20140717'),
('HHH00031200', '20140729'),
('HHH00031200', '20140821'),
('HHH00031200', '20140827'),
('HHH50000000', '20160523'),
('HHH50000000', '20160719'),
('HHH50000000', '20160913'),
('HHH88844900', '20150919'),
('HHH88844900', '20151022'),
('HHH88844900', '20151124'),
('HHH88844900', '20151210'),
('HHH22227700', '20140116'),
('HHH22227700', '20140221'),
('HHH22227700', '20140225'),
('HHH22227700', '20140305'),
('HHH22227700', '20150101'),
('HHH22227700', '20150115'),
('HHH22227700', '20150120'),
('HHH22227700', '20150210'),
('HHH22227700', '20150212'),
('HHH22227700', '20150217'),
('HHH22227700', '20150219'),
('HHH22227700', '20150225'),
('HHH22227700', '20150226');
-- First, assign every record from our data set a sequence number, where the
-- record having the earliest DOS for any MemberID has [Sequence] = 1 and the
-- value of [Sequence] increases by 1 for each subsequent DOS.
with OrderedDataCTE as
(
select
S.MemberID,
S.DOS,
[Sequence] = row_number() over (partition by S.MemberID order by S.DOS)
from
@SampleData S
),
-- Here's the recursive CTE, where the real work is done. Its purpose is to
-- split the initial data set into intervals. I use the term "interval" to mean
-- a set of records that have the same MemberID and the same status.
IntervalCTE as
(
-- Base case: Any record with [Sequence] = 1 is the first record for its
-- MemberID, so we know this represents the beginning of a new interval
-- that will ultimately have the status Initial_1.
select
O.MemberID,
O.DOS,
O.[Sequence],
-- IntervalStartDate will be the DOS of the record that defines the
-- start of the current interval. Since every record in the base case
-- marks a new interval, this is always the DOS.
IntervalStartDate = O.DOS,
-- Given that our final record statuses will either be of the form
-- Initial_X or Re-Auth_X_Y, AuthNumber will represent the value X. At
-- the start of the first interval for a new MemberID, the value is
-- always 1, because every member's first status is Initial_1.
AuthNumber = 1,
-- ReAuthNumber will represent the value Y (see comments above), and
-- will be set to 0 for statuses of the form Initial_X. At the start of
-- the first interval for a new MemberID, the value is always 0.
ReAuthNumber = 0
from
OrderedDataCTE O
where
O.[Sequence] = 1
union all
-- Recursive case: For each MemberID we find the record in the original
-- data set having the next-highest [Sequence] number, and compare it to
-- the record for that MemberID with the preceding [Sequence] number in
-- order to figure out its status.
select
This.MemberID,
This.DOS,
This.[Sequence],
-- If this record occurs within 30 days of the start of the interval
-- that includes the preceding record, then this record is part of the
-- same interval and thus has the same interval start date. Otherwise,
-- this record marks the beginning of a new interval, which starts on
-- this record's DOS.
IntervalStartDate = case when datediff(day, Prev.IntervalStartDate, This.DOS) < 30 then Prev.IntervalStartDate else This.DOS end,
-- If this record occurs within 45 days of the start of the interval
-- that includes the preceding record, then its AuthNumber is the same
-- as the preceding record. Otherwise it increments by 1.
AuthNumber = case when datediff(day, Prev.IntervalStartDate, This.DOS) < 45 then Prev.AuthNumber else Prev.AuthNumber + 1 end,
-- If this record occurs within 30 days of the start of the interval
-- that includes the preceding record, then this record is part of the
-- same interval and thus has the same ReAuthNumber.
--
-- If the above is not true but the record occurs within 45 days of the
-- start of the interval that includes the preceding record, then the
-- ReAuthNumber increments by 1.
--
-- If neither of the above is true, then we know that the AuthNumber
-- will have increased (above), so the ReAuthNumber resets to 0.
ReAuthNumber =
case
when datediff(day, Prev.IntervalStartDate, This.DOS) < 30 then Prev.ReAuthNumber
when datediff(day, Prev.IntervalStartDate, This.DOS) < 45 then Prev.ReAuthNumber + 1
else 0
end
from
IntervalCTE Prev
inner join OrderedDataCTE This on
Prev.MemberID = This.MemberID and
Prev.[Sequence] = This.[Sequence] - 1
)
-- At this point we can trivially construct each record's status from the
-- AuthNumbers and ReAuthNumbers assigned above.
select
I.MemberID,
I.DOS,
[Status] =
case
when I.ReAuthNumber = 0 then 'Initial_' + convert(varchar, I.AuthNumber)
else 'Re-Auth_' + convert(varchar, I.AuthNumber) + '_' + convert(varchar, I.ReAuthNumber)
end
from
IntervalCTE I
order by
I.MemberID,
I.[Sequence];
结果:
MemberID DOS Status
------------------------------------------------
HHH00031200 2014-07-17 Initial_1
HHH00031200 2014-07-29 Initial_1
HHH00031200 2014-08-21 Re-Auth_1_1
HHH00031200 2014-08-27 Re-Auth_1_1
HHH22227700 2014-01-16 Initial_1
HHH22227700 2014-02-21 Re-Auth_1_1
HHH22227700 2014-02-25 Re-Auth_1_1
HHH22227700 2014-03-05 Re-Auth_1_1
HHH22227700 2015-01-01 Initial_2
HHH22227700 2015-01-15 Initial_2
HHH22227700 2015-01-20 Initial_2
HHH22227700 2015-02-10 Re-Auth_2_1
HHH22227700 2015-02-12 Re-Auth_2_1
HHH22227700 2015-02-17 Re-Auth_2_1
HHH22227700 2015-02-19 Re-Auth_2_1
HHH22227700 2015-02-25 Re-Auth_2_1
HHH22227700 2015-02-26 Re-Auth_2_1
HHH50000000 2016-05-23 Initial_1
HHH50000000 2016-07-19 Initial_2
HHH50000000 2016-09-13 Initial_3
HHH88844900 2015-09-19 Initial_1
HHH88844900 2015-10-22 Re-Auth_1_1
HHH88844900 2015-11-24 Re-Auth_1_2
HHH88844900 2015-12-10 Re-Auth_1_2