Update table with overlap date range and change status - sql-server

I have a table with following column and I would like to update it as following.
The Logic is the start date take the date will be updated if overlap with following rules: take the earliest start date and enddate of the latest row with overlapping date based on member id. And the status of the remaining overlap column will be updated to 2. Hope someone could help.
ID MemberID StartDate EndDate Status
1 2 2015-01-01 2015-02-28 1
2 2 2015-02-01 2015-02-03 1
3 2 2015-02-01 2015-03-01 1
4 1 2015-02-01 2015-02-28 1
5 3 2015-02-01 2015-02-28 1
6 2 2015-05-01 2015-05-20 1
I would like to update to
ID MemberID StartDate EndDate Status
1 2 2015-01-01 2015-03-01 1
2 2 2015-01-01 2015-03-01 2
3 2 2015-01-01 2015-03-01 2
4 1 2015-02-01 2015-02-28 1
5 3 2015-02-01 2015-02-28 1
6 2 2015-05-01 2015-05-20 1

I think this should do it :
update a set
a.startdate =
(select min(startdate) from #table where memberID = a.memberID),
a.enddate =
(select max(enddate) from #table where memberID = a.memberID),
a.status =
case when a.id =
(select min(id) from #table where memberID = a.memberID)
then status else 2
end
from #table a

Try this,
---- Creating CTE for finding overlapped dates
;WITH CTE AS (
SELECT A.ID,
B.ID AS MAPPED_ID,
A.MEMBERID,
B.STARTDATE,
B.ENDDATE,
B.STATUS
FROM #YOUR_TABLE A
JOIN #YOUR_TABLE B ON B.STARTDATE <= A.ENDDATE-- Condition for finding the overlapped dates
AND B.ENDDATE >= A.STARTDATE
AND A.MEMBERID = B.MEMBERID)-- end here
UPDATE T
SET T.STARTDATE = A.STARTDATE,
T.ENDDATE = A.ENDDATE,
T.STATUS = A.STATUS
FROM #YOUR_TABLE T
JOIN (SELECT ID,
MEMBERID,
STARTDATE,
ENDDATE,
STATUS=CASE
WHEN RN > 1 THEN 2
ELSE 1
END
FROM (SELECT T.ID,
T.MEMBERID,
CS1.STARTDATE,
CS2.ENDDATE,
ROW_NUMBER() -- ROWNUMBER FOR FINDING THE STATUS
OVER(
PARTITION BY T.MEMBERID, CS1.STARTDATE, CS2.ENDDATE
ORDER BY T.ID) AS RN
FROM #YOUR_TABLE T
CROSS APPLY (SELECT CAST(MIN(STARTDATE)AS DATETIME) AS STARTDATE --- FINDING MIN(STARTDATE) FOR THE OVERLAPPED GROUP
FROM CTE A
WHERE A.ID = T.ID) CS1
CROSS APPLY (SELECT ENDDATE -- FINDING LAST ENDDATE FOR THE OVERLAPPED GROUP (IE RN=1)
FROM (SELECT ENDDATE,--- ROW_NUMBER FOR THE OVERLAPPED GROUPS
ROW_NUMBER()
OVER(
ORDER BY B.MAPPED_ID DESC) AS RN
FROM CTE B
WHERE B.ID = T.ID)A
WHERE A.RN = 1)CS2)A)A ON A.ID = T.ID
SELECT *
FROM #YOUR_TABLE

Related

Locate items with no changes over a period of time

Is there a T-SQL query that would allow me to see products that have had no changes in quantity for the past 4 days?
Product
Date
Quantity
Coke
2022-04-06
0
Coke
2022-04-07
0
Coke
2022-04-08
0
Coke
2022-04-09
0
Pepsi
2022-04-06
0
Pepsi
2022-04-07
1
Pepsi
2022-04-08
1
Pepsi
2022-04-09
1
Sprite
2022-04-06
1
Sprite
2022-04-07
0
Sprite
2022-04-08
0
Sprite
2022-04-09
1
Tango
2022-04-05
2
Tango
2022-04-06
1
Tango
2022-04-07
1
Tango
2022-04-08
1
Tango
2022-04-09
1
Result
Product
Quantity
Coke
0
Edit: this is how I started off my code
DECLARE #CurrentDate date = GETDATE();
DECLARE #PreviousDate date = DATEADD (Day, -4, #CurrentDate)
DECLARE #Quantity AS Decimal(8,5)
DECLARE #Count AS int
SELECT
[Date], [Product], [Quantity]
FROM
Table1
WHERE
[Date] = #PreviousDate
AND [Product] IN (SELECT [Product]
FROM Table1
WHERE [DATE] BETWEEN #PreviousDate AND #CurrentDate)
If I understand correctly you could use first_value() analytic function to partition the products accordingly and check the first and last quantities match,
select distinct product, quantity from (
select *,
First_Value(quantity) over(partition by product order by date) f,
First_Value(quantity) over(partition by product order by date desc) l
from t
where date >= DateAdd(day,-4,GetDate())
)t
where f = l;
Demo Fiddle
Edit
Seems I didn't quite understand but do now (I hope) so how about an approach using not exists?
with cv as (
select *,
First_Value(quantity) over(partition by product order by date desc) currentValue,
Count(*) over(partition by product) qty
from t
where date >= DateAdd(day,-4,GetDate())
)
select distinct product, quantity
from cv t
where qty=4 and not exists (
select * from cv t2
where t2.product = t.product
and t2.quantity != currentValue
);
Demo Fiddle (2)
You select all records from #PreviousDate (WHERE [Date] = #PreviousDate) and which have modifications (AND [Product] IN (...))
SELECT
t1.[Date], t1.[Product], t1.[Quantity], t2.Quantity "PreviousQuantity"
FROM
Table1 t1
LEFT JOIN Table1 t2 On t1.Product = t2.Product and t2.[Date] = #PreviousDate
WHERE t1.[Date] = #CurrentDate
and t1.Quantity = t2.Quantity
But this will show nothing because #PreviousDate has the value 2022-04-05 (when #CurrentDate=2022-04-09)
So, you might need to change #PreviousDate to: DECLARE #PreviousDate date = DATEADD (Day, -3, #CurrentDate)
see: DBFIDDLE

Date Comparison of Two Tables in SQL SERVER

I had this Data,
Table One :
EmpID Date Absent
1 01/01/2018 1
1 01/02/2018 1
1 02/05/2018 1
1 03/25/2018 1
1 04/01/2018 0
1 05/02/2018 1
1 06/03/2018 1
Table Two
ID Amount DateEffective
1 5.00 02/06/2018
2 3.00 05/02/2018
3 10.00 06/03/2018
Desired Output
EmpID Month Year Absent Penalty
1 January 2018 2 5.00
1 February 2018 1 5.00
1 March 2018 1 3.00
1 April 2018 0 3.00
1 May 2018 1 13.00
1 June 2018 1 10.00
This is my Code
SELECT { fn MONTHNAME(one.Date) } AS MonthName, YEAR(one.Date) AS Year, SUM(one.Absent) AS Absent,
(
SELECT top 1 two.DailyRate
FROM table_two as two
WHERE EmpID = '1'
AND one.Date <= two.EffectivityDate
)
FROM table_one as one
WHERE EmpID = '1'
GROUP BY { fn MONTHNAME(one.Date) }, MONTH(one.Date), YEAR(one.DTRDate)
ORDER BY Year(one.Date),month(one.Date)
and it shows an error :
Column 'one.Date' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause
please help for this issue...
Thanks
Try this :
SELECT
one.EmpID
,DATENAME(MONTH,one.Date) AS [MonthName]
,YEAR(one.Date) AS [Year]
,SUM(one.Absent) AS [Absent]
,(SELECT top 1 two.Amount
FROM table_two as two
WHERE two.ID = one.EmpID
AND YEAR(two.DateEffective) >= YEAR(one.Date)
AND MONTH(two.DateEffective) >=MONTH(one.Date)
) AS [Penalty]
FROM table_one as one
WHERE
one.EmpID = '1'
GROUP BY one.EmpID,DATENAME(MONTH,one.Date), MONTH(one.Date), YEAR(one.Date)
ORDER BY Year(one.Date),month(one.Date)
From my understanding to do this,
select e.EmpID
,datename(month,e.Date)[month]
,year(e.Date) [year]
,sum(e.Absent) as [Abscount]
,a.Amount
from
empl e left join abs a
on datename(month,e.Date)=DATENAME(month,a.DateEffective)
group by e.EmpID,DATENAME(MONTH,e.Date), MONTH(e.Date), YEAR(e.Date) , a.Amount
order by Abscount desc
Revert me if any clarifications needed...
is this helpful.?
Create Table #TabOne(EmpID int,[Date] Date,[Absent] Bit)
Create Table #TabTwo(ID int,Amount float,DateEffective Date)
Insert into #TabOne
SELECT 1,'01/01/2018',1 Union All
SELECT 1,'01/02/2018',1 Union All
SELECT 1,'02/05/2018',1 Union All
SELECT 1,'03/25/2018',1 Union All
SELECT 1,'04/01/2018',0 Union All
SELECT 1,'05/02/2018',1 Union All
SELECT 1,'06/03/2018',1
Insert into #TabTwo
Select 1,5.00 ,'02/06/2018' Union All
Select 2,3.00 ,'05/02/2018' Union All
Select 3,10.00,'06/03/2018'
;with cte1
As
(
Select One.EmpID,MONTH(one.[Date]) As [mon],YEAR(one.[Date]) As [Year],two.Amount,one.[Absent],
ROW_NUMBER() OVER(partition by One.EmpID,One.[Date] order by DATEDIFF(dd,two.DateEffective,one.[Date]) desc) as rn
from #TabOne one
LEFT JOIN #TabTwo two on one.[Date]<=two.DateEffective
)
Select EmpID,DATENAME(month, DATEADD(month, [mon]-1, CAST('2008-01-01' AS datetime))) As [Month],
[Year],SUM(CASE WHEN [Absent]=0 then 0 ELSE 1 END) As [Absent] ,MAX(Amount) As Penalty
from cte1
where rn=1
Group by EmpID,[Year],[mon]
order by EmpID,[Year],[mon]
Drop Table #TabOne
Drop Table #TabTwo

If Value is present in two consecutive months , display only one month in sql

I would want to check ID in consecutive months, IF Same ID is present in two consecutive months then consider that ID only for 1st month.
If ID's are not in consecutive month then show the distinct ID's grouped by start date month.(We consider only start date)
For example, ID 1 is present in start date months january and Feb , then Distinct count of this ID will be 1 in Jan, how ever ID 2 and 3 are
present in Jan and March and Feb and May Resp, now I would like to see this distinct count of ID in Jan and March.
Current Data
Table1:
ID StartDate EndDate
1 2017-01-12 2017-01-28
1 2017-01-19 2017-01-28
1 2017-01-29 2017-02-11
1 2017-02-01 2017-02-11
1 2017-02-19 2017-02-24
2 2017-01-12 2017-01-28
2 2017-01-19 2017-01-28
2 2017-03-09 2017-03-20
3 2017-02-12 2017-02-28
3 2017-02-19 2017-02-28
3 2017-05-05 2017-05-29
3 2017-05-09 2017-05-29
I tried with below logic bt I know I am missing on something here.
select t.* from Table1 t
join Table1 t t1
on t1.ID=t.ID
and datepart(mm,t.StartDate)<> datepart(mm,t1.StartDate)+1
Expected Result:
DistinctCount StartDateMonth(In Numbers)
1 1(Jan)
2 1(Jan)
2 3(March)
3 2(Feb)
3 5(May)
Any help is appreciated!
Here's my solution. The thinking for this is:
1) Round all the dates to the first of the month, then work with the distinct dataset of (ID, StartDateRounded). From your dataset, the result should look like this:
ID StartDateRounded
1 2017-01-01
1 2017-02-01
2 2017-01-01
2 2017-03-01
3 2017-02-01
3 2017-05-01
2) From this consolidated dataset, find all records by ID that do not have a record for the previous month (which means it's not a consecutive month and thus is a beginning of a new data point). This is your final dataset
with DatesTable AS
(
SELECT DISTINCT ID
,DATEADD(month,DateDiff(month,0,StartDate),0) StartDateRounded
,DATEADD(month,DateDiff(month,0,StartDate)+1,0) StartDateRoundedPlusOne
FROM Table1
)
SELECT t1.ID, DatePart(month,t1.StartDateRounded) AS StartDateMonth
FROM DatesTable t1
LEFT JOIN DatesTable t2
ON t1.ID = t2.ID
AND t1.StartDateRounded = t2.StartDateRoundedPlusOne
WHERE t2.ID IS NULL; --Verify no record exists for prior month
sqlfiddler for reference. Let me know if this helps
Just need to take advantage of the lag on the inner query to compare values between rows, and apply the logic in question on the middle query, and then do a final select.
/*SAMPLE DATA*/
create table #table1
(
ID int not null
, StartDate date not null
, EndDate date null
)
insert into #table1
values (1, '2017-01-12', '2017-01-28')
, (1, '2017-01-19', '2017-01-28')
, (1, '2017-01-29', '2017-02-11')
, (1, '2017-02-01', '2017-02-11')
, (1, '2017-02-19', '2017-02-24')
, (2, '2017-01-12', '2017-01-28')
, (2, '2017-01-19', '2017-01-28')
, (2, '2017-03-09', '2017-03-20')
, (3, '2017-02-12', '2017-02-28')
, (3, '2017-02-19', '2017-02-28')
, (3, '2017-05-05', '2017-05-29')
, (3, '2017-05-09', '2017-05-29')
/*ANSWER*/
--Final Select
select c.ID
, c.StartDateMonth
from (
--Compare record values to rule a record in/out based on OP's logic
select b.ID
, b.StartDateMonth
, case when b.StartDateMonth = b.StartDateMonthPrev then 0 --still the same month?
when b.StartDateMonth = b.StartDateMonthPrev + 1 then 0 --immediately prior month?
when b.StartDateMonth = 1 and b.StartDateMonthPrev = 12 then 0 --Dec/Jan combo
else 1
end as IncludeFlag
from (
--pull StartDateMonth of previous record into current record
select a.ID
, datepart(mm, a.StartDate) as StartDateMonth
, lag(datepart(mm, a.StartDate), 1, NULL) over (partition by a.ID order by a.StartDate asc) as StartDateMonthPrev
from #table1 as a
) as b
) as c
where 1=1
and c.IncludeFlag = 1
Output:
+----+----------------+
| ID | StartDateMonth |
+----+----------------+
| 1 | 1 |
| 2 | 1 |
| 2 | 3 |
| 3 | 2 |
| 3 | 5 |
+----+----------------+
Try the below query,
SELECT ID,MIN(YEARMONTH) AS YEARMONTH
FROM (
SELECT ID
,YEAR([StartDate])*100+MONTH([StartDate]) AS YEARMONTH
,LAG(YEAR([StartDate])*100+MONTH([StartDate]))
OVER(ORDER BY ID) AS PREVYEARMONTH
,ROW_NUMBER() OVER(ORDER BY ID) AS ROW_NO
FROM #Table1
GROUP BY ID,((YEAR([StartDate])*100)+MONTH([StartDate]))
) AS T
GROUP BY ID
,(CASE WHEN YEARMONTH - PREVYEARMONTH > 1 THEN ROW_NO ELSE 0 END)
ORDER BY ID
Output:
ID YEARMONTH
1 201701
2 201701
2 201703
3 201702
3 201705
Thank you all guys. most of the logic seemed to work..but I tried just with below one and I Was good with thiis.
SELECT t1.ID, DatePart(month,t1.Startdate) AS StartDateMonth
FROM DatesTable t1
LEFT JOIN DatesTable t2
ON t1.ID = t2.ID
AND DatePart(month,t1.Startdate) = DatePart(month,t2.Startdate)+1
WHERE t2.ID IS NULL;
Thanks again
Ok, I wrote my first query without checking, believed that will work correctly. This is my updated version, should be faster than other solutions
select
id
, min(st)%12 --this will return start month
, min(st)/12 + 1 --this will return year, just in case if you need it
from (
select
id, st, gr = st - row_number() over (partition by ID order by st)
from (
select
distinct ID, st = (year(StartDate) - 1) * 12 + month(StartDate)
from
#table2
) t
) t
group by id, gr

Query to get last cost

I'm trying to do this query, in sql server, but something is wrong. Need some help...
I have a table with item movements and another one with other movements (buy) where I find the cost of each item in each date when I buy it. So, I just need first table with last cost based on the date of movement finding the cost on second table on the last date.
In other words, only must search the records from the second table with date lower than the first table date for that item and return the cost of the most recent date.
Examples:
First Table
REF DATE
1 2015-10-15
1 2015-08-30
2 2015-09-11
3 2015-05-22
2 2015-03-08
2 2015-07-15
3 2015-11-14
1 2015-11-20
Second Table (Buy)
REF DATE COST
1 2015-08-20 150
1 2015-10-12 120
2 2015-04-04 270
2 2015-06-15 280
3 2015-03-01 75
3 2015-10-17 80
I need this result:
REF DATE Cost
1 2015-10-15 120
1 2015-08-30 150
2 2015-09-11 280
3 2015-05-22 75
2 2015-03-08 -
2 2015-07-15 280
3 2015-11-14 80
1 2015-11-20 120
Any help appreciated.
You can do it using OUTER APPLY:
SELECT [REF], [DATE], [COST]
FROM Table1 AS t1
OUTER APPLY (
SELECT TOP 1 COST
FROM Table2 AS t2
WHERE t1.REF = t2.REF AND t1.DATE >= t2.DATE
ORDER BY t2.DATE DESC) AS t3
Demo here
;WITH cte AS (
SELECT ft.*,
st.[Cost],
ROW_NUMBER() OVER (PARTITION BY ft.[Ref],ft.[Date] ORDER BY st.[Date] DESC) RN
FROM FirstTable ft
LEFT JOIN SecondTable st ON ft.[Ref] = st.[Ref]
AND ft.[Date] >= st.[Date]
)
SELECT Ref,
[Date],
[Cost]
FROM cte
WHERE RN = 1
or if you dont want to use a cte.
SELECT
Ref,
[Date],
[Cost]
FROM
(SELECT
ft.*,
st.[Cost],
ROW_NUMBER() OVER (PARTITION BY ft.[Ref],ft.[Date] ORDER BY st.[Date] DESC) RN
FROM
FirstTable ft
LEFT JOIN SecondTable st ON ft.[Ref] = st.[Ref]
AND ft.[Date] >= st.[Date]
) t
WHERE
t.RN = 1

Collapsing records with adjacent start and end dates

I have the following data in a table in SQL Server 2008 R2:
ID Code StartDate EndDate
10001 3 2014-07-25 2014-07-28
10001 3 2014-07-29 2014-10-06
10001 3 2014-10-07 2014-10-10
10001 1 2014-10-11 2014-10-31
10001 1 2014-11-01 2014-11-15
10001 3 2014-11-16 2014-11-25
10001 3 2014-11-26 NULL
20002 3 2014-07-25 2014-07-28
20002 3 2014-07-29 2014-10-06
20002 3 2014-10-07 NULL
30003 3 2014-07-25 2014-11-13
30003 3 2014-11-14 2014-11-24
30003 2 2014-11-25 NULL
I want to "collapse" any records with the same Code, and adjacent EndDate and StartDate. The results should be:
ID Code StartDate EndDate
10001 3 2014-07-25 2014-10-10
10001 1 2014-10-11 2014-11-15
10001 3 2014-11-16 NULL
20002 3 2014-07-25 NULL
30003 3 2014-07-25 2014-11-24
30003 2 2014-11-25 NULL
I've been trying to use various sub-queries and the ROW_NUMBER() function, but just can't get it to work. I suspect this would be easily done with a CTE, but I haven't been able to wrap my head around how those work in order to try it here. Any ideas?
Since your ranges are continuous, the problem essentially becomes a gaps-and-islands one.
© Andriy M
Thanks to Steve Ford for the table:
declare #EventLog table
(
Id int,
Code tinyint,
StartDate date,
EndDate date null
)
insert into #EventLog
values
(10001, 3, '2014-07-25', '2014-07-28'),
(10001, 3, '2014-07-29', '2014-10-06'),
(10001, 3, '2014-10-07', '2014-10-10'),
(10001, 1, '2014-10-11', '2014-10-31'),
(10001, 1, '2014-11-01', '2014-11-15'),
(10001, 3, '2014-11-16', '2014-11-25'),
(10001, 3, '2014-11-26', null),
(20002, 3, '2014-07-25', '2014-07-28'),
(20002, 3, '2014-07-29', '2014-10-06'),
(20002, 3, '2014-10-07', null),
(30003, 3, '2014-07-25', '2014-11-13'),
(30003, 3, '2014-11-14', '2014-11-24'),
(30003, 2, '2014-11-25', null);
Thanks Andriy M for the solution:
declare #MaxDate date = '9999-12-31';
with cte as
(
select *,
g = row_number() over (partition by Id order by StartDate)
- row_number() over (partition by Id, Code order by StartDate)
from #EventLog
)
select
Id,
Code,
StartDate = min(StartDate),
EndDate = nullif(max(isnull(EndDate, #MaxDate)), #MaxDate)
from cte
group by
Id, Code, g;
Try this,
CREATE TABLE #TEMP
(
ID INT,
CODE INT,
STARTDATE DATE,
ENDDATE DATE
)
INSERT INTO #TEMP VALUES
(10001,3,'2014-07-25','2014-07-28'),
(10001,3,'2014-07-29','2014-10-06'),
(10001,3,'2014-10-07','2014-10-10'),
(10001,1,'2014-10-11','2014-10-31'),
(10001,1,'2014-11-01','2014-11-15'),
(10001,3,'2014-11-16','2014-11-25'),
(10001,3,'2014-11-26',NULL),
(20002,3,'2014-07-25','2014-07-28'),
(20002,3,'2014-07-29','2014-10-06'),
(20002,3,'2014-10-07',NULL),
(30003,3,'2014-07-25','2014-11-13'),
(30003,3,'2014-11-14','2014-11-24'),
(30003,2,'2014-11-25',NULL)
SELECT T1.ID,T1.CODE,T1.STARTDATE,A.ENDDATE FROM (SELECT L.ID,L.CODE,MIN(STARTDATE) AS STARTDATE,DIFF FROM (SELECT ID,
CODE,
STARTDATE,
ENDDATE,
IsNull(Lag(CODE, 2)
OVER (
ORDER BY ID, STARTDATE, ENDDATE), CODE) AS T_LAG,
CODE - IsNull(Lag(CODE, 2)
OVER (
ORDER BY ID, STARTDATE, ENDDATE), CODE) AS DIFF
FROM #TEMP ) L
GROUP BY L.ID,L.CODE,DIFF) T1
CROSS APPLY(
SELECT ID,CODE,ENDDATE,DIFF FROM (SELECT ID,CODE,ENDDATE,DIFF,ROW_NUMBER() OVER (PARTITION BY ID,CODE,DIFF ORDER BY ID,CODE,STARTDATE DESC,ENDDATE DESC) AS T_R FROM (SELECT ID,
CODE,
STARTDATE,
ENDDATE,
IsNull(Lag(CODE, 2)
OVER (
ORDER BY ID, STARTDATE, ENDDATE), CODE) AS T_LAG,
CODE - IsNull(Lag(CODE, 2)
OVER (
ORDER BY ID, STARTDATE, ENDDATE), CODE) AS DIFF
FROM #TEMP ) A) A
WHERE T_R=1 AND ID=T1.ID AND CODE=T1.CODE AND DIFF=T1.DIFF)A
ORDER BY T1.ID,T1.STARTDATE
Try this (I'm replicating SQL 2012 Lead And Lag functionality using Row_Number() in 2008):
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE EventLog
(
ID Int,
Code tinyint,
StartDate Date,
EndDate Date Null
)
INSERT INTO EventLog
Values
(10001, 3, '2014-07-25', '2014-07-28'),
(10001, 3, '2014-07-29', '2014-10-06'),
(10001, 3, '2014-10-07', '2014-10-10'),
(10001, 1, '2014-10-11', '2014-10-31'),
(10001, 1, '2014-11-01', '2014-11-15'),
(10001, 3, '2014-11-16', '2014-11-25'),
(10001, 3, '2014-11-26', NULL),
(20002, 3, '2014-07-25', '2014-07-28'),
(20002, 3, '2014-07-29', '2014-10-06'),
(20002, 3, '2014-10-07', NULL),
(30003, 3, '2014-07-25', '2014-11-13'),
(30003, 3, '2014-11-14', '2014-11-24'),
(30003, 2, '2014-11-25', NULL)
Query 1:
WITH CTE
AS
(
SELECT ID,
Code,
StartDate,
EndDate,
ROW_NUMBER() OVER (PARTITION BY Id, Code ORDER BY Id, Code, StartDate) As RN
FROM EventLog
),
CTE2
AS
(
SELECT CTE.Id, CTE.Code, CTE.StartDate, CTE.EndDate,
CASE WHEN DATEDIFF(d, LAG.EndDate, CTE.StartDate) = 1
THEN Lag.EndDate
ELSE NULL
END AS PrevEndDate,
CASE WHEN DateDiff(d, LEAD.StartDate, CTE.EndDate) = -1
THEN Lead.StartDate
ELSE NULL
END As NextStartDate
FROM CTE
LEFT OUTER JOIN CTE AS Lag
ON CTE.ID = Lag.ID AND CTE.Code = Lag.Code AND Lag.Rn = CTE.RN - 1
LEFT OUTER JOIN CTE AS Lead
ON CTE.ID = Lead.ID AND CTE.Code = Lead.Code AND Lead.Rn = CTE.RN + 1
),
StartAndEnd
As
(
SELECT ID,
Code,
StartDate,
EndDate,
PrevEndDate,
NextStartDate,
ROW_NUMBER() OVER (PARTITION BY Id, Code ORDER BY ID, Code, StartDate) As RN
FROM CTE2
WHERE (PrevEndDate IS NULL Or NextStartDate IS NULL)
)
SELECT S.ID, s.Code, S.StartDate, E.EndDate
FROM StartAndEnd as S
LEFT JOIN StartAndEnd E
ON S.ID = E.ID AND S.Code = E.Code AND E.RN = S.Rn + 1
WHERE S.PrevEndDate Is Null
ORDER By s.Id, S.StartDate
Results:
| ID | CODE | STARTDATE | ENDDATE |
|-------|------|------------|------------|
| 10001 | 3 | 2014-07-25 | 2014-10-10 |
| 10001 | 1 | 2014-10-11 | 2014-11-15 |
| 10001 | 3 | 2014-11-16 | (null) |
| 20002 | 3 | 2014-07-25 | (null) |
| 30003 | 3 | 2014-07-25 | 2014-11-24 |
| 30003 | 2 | 2014-11-25 | (null) |
;WITH StartDates
AS(
Select e1.ID, e1.Code, e1.StartDate, ROW_NUMBER() OVER (Order By e1.ID asc) as RowNumber
From #EventLog e1
LEFT JOIN #EventLog e2 ON e1.Code = e2.Code and e2.EndDate = DATEADD(day,-1,e1.StartDate)
WHERE e2.Id is null
),
EndDates as(
Select e1.ID, e1.Code, e1.EndDate, ROW_NUMBER() OVER (Order by e1.ID asc) as RowNumber
FROM #EventLog e1
LEFT JOIN #EventLog e2 ON e1.Code = e2.Code and e2.StartDate = DATEADD(day,1,e1.EndDate)
WHERE e2.Id is null
)
Select s.ID, s.Code, s.StartDate, e.EndDate
FROM StartDates s
JOIN EndDates e ON s.Code = e.Code and s.RowNumber = e.RowNumber

Resources