问题
I have to check for minimum date each time and add 30 days to it and set the field accordingly.
For Example:
My Table:
ID StartDate EndDate
1 2017-01-01 2017-02-01
1 2017-01-09 2017-01-28
1 2017-04-01 2017-04-30
1 2017-04-05 2017-05-20
1 2017-04-20 2017-06-12
2 2017-06-02 2017-06-20
2 2017-06-14 2017-07-31
2 2017-06-14 2017-07-31
2 2017-06-19 2017-07-31
2 2017-06-19 2017-07-31
so, here min(startdate) is 2017-01-01. Now I add 30 days to it and my look up date is 2017-01-31. for any record which has start date less than this look up date I have to set flag_1.
My table will look like:
ID StartDate EndDate flag
1 2017-01-01 2017-02-01 flag_1
1 2017-01-09 2017-01-28 flag_1
1 2017-04-01 2017-04-30 null
1 2017-04-05 2017-05-20 null
1 2017-04-20 2017-06-12 null
2 2017-06-02 2017-06-20 null
2 2017-06-14 2017-07-31 null
2 2017-06-14 2017-07-31 null
2 2017-06-19 2017-07-31 null
2 2017-06-19 2017-07-31 null
Now I have to look for next minimum startdate which is greater than previous look up date(2017-01-31).so my min start date would be 2017-04-01. Now I add 30 days to it and my new look up date is 2017-05-01. For all the records less than new look up start date 2017-05-01 and greater than previous look up date(2017-01-31) the flag will be flag_2 and this goes on for same ID till it reaches the last record for same ID.
My final table should look like:
ID StartDate EndDate flag
1 2017-01-01 2017-02-01 flag_1
1 2017-01-09 2017-01-28 flag_1
1 2017-04-01 2017-04-30 flag_2
1 2017-04-05 2017-05-20 flag_2
1 2017-04-20 2017-06-12 flag_2
2 2017-06-02 2017-06-20 flag_1
2 2017-06-14 2017-07-31 flag_1
2 2017-06-14 2017-07-31 flag_1
2 2017-06-19 2017-07-31 flag_1
2 2017-06-19 2017-07-31 flag_1
i can get the first set of records by below code but unable to think of having this in a loop and having flag updated dynamically .
select a.*,'flag_1' as flag from table a
join
(select cast(min(startdate) as date) as minstartdate,cast(dateadd(day,30,min(startdate)) as date) as maxstartdate,ID from table
group by ID) adate
on cast(adate.maxstartdate as date)> cast(a.startdate as date)
and adate.id=a.id
where a.id=1
order by startdate
Can anyone help me with this logic?
回答1:
Here is how I would approach it - there may be a recursive approach, but I don't use recursion unless it is absolutely necessary.
A couple of notes: I did the work in a temp table to make it easier to reset during development and testing, and I used an integer field for the flag to simplify the increment logic.
-- the setup
Create Table SO_Check_Min_Date
(
ID Int
, StartDate Date
, EndDate Date
, Flag Int
)
Insert Into dbo.SO_Check_Min_Date
(
ID
, StartDate
, EndDate
)
Values
(1, '2017-01-01', '2017-02-01'),
(1, '2017-01-09', '2017-01-28'),
(1, '2017-04-01', '2017-04-30'),
(1, '2017-04-05', '2017-05-20'),
(1, '2017-04-20', '2017-06-12'),
(2, '2017-06-02', '2017-06-20'),
(2, '2017-06-14', '2017-07-31'),
(2, '2017-06-14', '2017-07-31'),
(2, '2017-06-19', '2017-07-31'),
(2, '2017-06-19', '2017-07-31')
-- the logic
Declare
@Flag As Int = 0
, @StartDate As Date
, @LookupDate As Date
, @ID As Int
, @PrevID As Int = 0;
Select
*
Into
#Temp_SO_Check
From SO_Check_Min_Date;
Select * From #Temp_SO_Check As tsc -- to see that the flag field is blank
While Exists
(
Select Top 1
1
From #Temp_SO_Check
Where Flag Is Null
)
Begin
Select Top 1
@ID = ID
, @StartDate = StartDate
From #Temp_SO_Check
Where Flag Is Null
Order By
ID
, StartDate;
If @PrevID <> @ID
Begin
Set @Flag = 1;
Set @PrevID = @ID;
End;
Else
Set @Flag = @Flag + 1;
Set @LookupDate = DateAdd(Day, 30, @StartDate);
Update
#Temp_SO_Check
Set
Flag = @Flag
Where
ID = @ID
And StartDate Between @StartDate
And @LookupDate;
End;
Select * From #Temp_SO_Check As tsc -- to see that it has been populated as desired
回答2:
Since this is the original question--I'm posting the answer here. I'd suggest ditching your loops all together. Here's a way with 2 CTE's and ZERO loops.
declare @table table (ID int, StartDate date, EndDate date)
Insert Into @table
(
ID
, StartDate
, EndDate
)
Values
(1, '2017-01-01', '2017-02-01'),
(1, '2017-01-09', '2017-01-28'),
(1, '2017-04-01', '2017-04-30'),
(1, '2017-04-05', '2017-05-20'),
(1, '2017-04-20', '2017-06-12'),
(2, '2017-06-02', '2017-06-20'),
(2, '2017-06-14', '2017-07-31'),
(2, '2017-06-14', '2017-07-31'),
(2, '2017-06-19', '2017-07-31'),
(2, '2017-06-19', '2017-07-31')
;with cte as(
select
t1.ID
,t1.StartDate
,t1.EndDate
,DT = (select min(StartDate) from @table t2 where t2.StartDate > DATEADD(day,30,t1.StartDate))
from
@table t1),
cte2 as(
select
ID
,StartDate
,EndDate
,dense_rank() over (order by isnull(DT,(select max(StartDate) from cte))) as Flag
from
cte)
select
ID
,StartDate
,EndDate
,case when Flag % 2 = 0 then 2 else Flag % 2 end as Flag
from cte2
来源:https://stackoverflow.com/questions/45698391/how-to-check-minimum-date-in-each-observation-and-update-flag-dynamically