The need for date validation overlaps or not in sql server
Below is the input code
ID EMP_ID PROJECT_NAME START_DATE END_DATE
1 10016351 ABC 22-12-2016 15-05-2017
2 10016351 ABC 01-09-2016 22-11-2016
1 10081503 RTBS AMaaS 21-11-2016 15-02-2017
2 10081503 RTBS AMaaS 18-07-2016 25-11-2016
3 10081503 RTBS AMaaS 21-08-2016 13-10-2016
4 10081503 RTBS AMaaS 03-02-2015 22-05-2015
1 10089293 RTBS PDaaS 17-02-2017 31-12-2017
2 10089293 RTBS PDaaS 13-06-2016 14-02-2017
I need to check an employee if the dates overlap or not. If so, it will return the min. Start date and Max. End date and flag as "O".
For example, for emp_id = 10081503, dates overlap 21-11-2016. Falls from 18 to 201 and 25-11-2016. 21-08-2016 - between 18-07-2016 and 25-11-2016 so the query should return a start date of 07/18/2016 (min) and an end date as 02/15/2017 (max) for dates that overlap. For rest records, it must return the same record with the "N" flag.
EMP_ID | PROJECT_NAME | START_DATE | END_DATE | Flag of 10081503 | RTBS AMaaS | 21-11-2016 | 15-02-2017 | About 10081503 | RTBS AMaaS | 03-02-2015 | 22-05-2015 | N
Like my final conclusion,
ID EMP_ID PROJECT_NAME START_DATE END_DATE FLAG
1 10016351 ABC 22-12-2016 15-05-2017 N
2 10016351 ABC 01-09-2016 22-11-2016 N
1 10081503 RTBS AMaaS 21-11-2016 15-02-2017 O
2 10081503 RTBS AMaaS 03-02-2015 22-05-2015 N
1 10089293 RTBS PDaaS 17-02-2017 31-12-2017 N
2 10089293 RTBS PDaaS 13-06-2016 14-02-2017 N
source to share
Use outer apply()
to return the first one id
to overlap and group by this value. Using row_number()
to renumber id
:
select
id = row_number() over (
partition by t.emp_id
order by min(start_date) desc
)
, t.emp_id
, t.project_name
, start_date = min(start_date)
, end_date = max(end_date)
, flag = max(case when x.id <> t.id then 'O' else 'N' end)
from t
outer apply (
select top 1 i.id
from t i
where i.Emp_Id = t.Emp_id
and i.End_Date > t.Start_Date
and t.End_Date > i.Start_Date
order by i.Start_Date
) x
group by t.emp_id, t.project_name, x.id
Demo version of the rexter: http://rextester.com/VHMZ91714
returns:
+----+----------+--------------+------------+------------+------+
| id | emp_id | project_name | start_date | end_date | flag |
+----+----------+--------------+------------+------------+------+
| 1 | 10016351 | ABC | 2016-12-22 | 2017-05-15 | N |
| 2 | 10016351 | ABC | 2016-09-01 | 2016-11-22 | N |
| 1 | 10081503 | RTBS AMaaS | 2016-07-18 | 2017-02-15 | O |
| 2 | 10081503 | RTBS AMaaS | 2015-02-03 | 2015-05-22 | N |
| 1 | 10089293 | RTBS PDaaS | 2017-02-17 | 2017-12-31 | N |
| 2 | 10089293 | RTBS PDaaS | 2016-06-13 | 2017-02-14 | N |
+----+----------+--------------+------------+------------+------+
The above will cause the overlapping ranges to be flattened, but if you need to collapse multiple ranges that only overlap at the edges into one range, we can use a calendar or dates table like this:
For just 152KB in memory, you can have 30 year dates in your table with this:
/* dates table */
declare @fromdate date = '20000101';
declare @years int = 30;
/* 30 years, 19 used data pages ~152kb in memory, ~264kb on disk */
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
select top (datediff(day, @fromdate,dateadd(year,@years,@fromdate)))
[Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,@fromdate))
into dbo.Dates
from n as deka cross join n as hecto cross join n as kilo
cross join n as tenK cross join n as hundredK
order by [Date];
create unique clustered index ix_dbo_Dates_date on dbo.Dates([Date]);
If you don't want to create a date table, you can use this query to create dates like this:
declare @fromdate date = '20100101';
declare @thrudate date = getdate();
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, dates as (
select top (datediff(day, @fromdate, @thrudate)+1)
[Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,@fromdate))
from n as deka cross join n as hecto cross join n as kilo
cross join n as tenK cross join n as hundredK
order by [Date]
)
, cte as (
select
t.emp_id
, t.project_name
, d.date
, rn = row_number() over (partition by t.emp_id, t.project_name order by d.date)-1
, flag = case when count(*) > 1 then 'O' else 'N' end
from t
inner join dates d
on d.date >= t.start_date
and d.date <= t.end_date
group by t.emp_id, t.project_name, d.date
)
select
id = row_number() over (partition by emp_id order by min(date) desc)
, emp_id
, project_name
, start_date = min(date)
, end_date = max(date)
, flag = max(flag)
from cte
group by emp_id, project_name, dateadd(day,-rn,date)
registry: http://rextester.com/QKEMH32326
returns:
+----+----------+--------------+------------+------------+------+
| id | emp_id | project_name | start_date | end_date | flag |
+----+----------+--------------+------------+------------+------+
| 1 | 10016351 | ABC | 2016-12-22 | 2017-05-15 | N |
| 2 | 10016351 | ABC | 2016-09-01 | 2016-11-22 | N |
| 1 | 10081503 | RTBS AMaaS | 2016-07-18 | 2017-02-15 | O |
| 2 | 10081503 | RTBS AMaaS | 2015-02-03 | 2015-05-22 | N |
| 1 | 10089293 | RTBS PDaaS | 2017-02-17 | 2017-12-31 | N |
| 2 | 10089293 | RTBS PDaaS | 2016-06-13 | 2017-02-14 | N |
+----+----------+--------------+------------+------------+------+
directory of calendar and table numbers:
- Create set or sequence without loops 2- Aaron Bertrand
- Creating a Date / Dimension Table in SQL Server 2008 - David Stein
- Calendar tables - why you need - David Stein
- How to size a date or calendar table in SQL Server - Aaron Bertrand
- TSQL Function for Determining Holidays in SQL Server - Tim Cullen
- F_TABLE_DATE - Michael Valentine Jones
source to share
Thanks everyone for the help.!
Need help in the same context,
In such a scenario, we end up with two records in DISTRIBUTION DETAILS. This is because the resource was assigned to two different projects, but the placement end date of the first distribution project (in the example above) overlaps with the distribution start date of the second distribution project. So to calculate the validity period of allocation 1: Stay period (Alloc 1) = Start date Alloc (Alloc 2) - Start date Alloc (Alloc 1) i.e. (17-11-2016) - (01-09-2016) = 77 The calculation of the validity period for allocation 2 has not changed. Here, the final entry will look like this:
source to share
Unfortunately this first solution does not work as stated, executing the code will only result in three lines (the code groups even non-overlapping dates):
id emp_id start_date end_date flag
1 10016351 2016-09-01 2017-05-15 O
1 10081503 2015-02-03 2017-02-15 O
1 10089293 2016-06-13 2017-12-31 O
Can anyone provide a working solution? Below is some sample code that will work without creating any tables, just for testing:
declare @t table (id int, emp_id int, project_name char(10), start_date date, end_date date, flag char)
insert into @t
values
(1 , 10016351 , 'ABC ', '2016-12-22' , '2017-05-15','X'),
(2 , 10016351 , 'ABC ', '2016-09-01' , '2016-11-22','X'),
(1 , 10081503 , 'RTBS AMaaS', '2016-11-21' , '2017-02-15','X'),
(2 , 10081503 , 'RTBS AMaaS', '2016-07-18' , '2016-11-25','X'),
(3 , 10081503 , 'RTBS AMaaS', '2016-08-21' , '2016-10-13','X'),
(4 , 10081503 , 'RTBS AMaaS', '2015-02-03' , '2015-05-22','X'),
(1 , 10089293 , 'RTBS PDaaS', '2017-02-17' , '2017-12-31','X'),
(2 , 10089293 , 'RTBS PDaaS', '2016-06-13' , '2017-02-14','X')
select
id = row_number() over (
partition by emp_id
order by min(start_date) desc
)
, emp_id
, start_date = min(start_date)
, end_date = max(end_date)
, flag = max(case when x.id <> t.id then 'O' else 'N' end)
from @t t
outer apply (
select top 1 i.id
from @t i
where i.Emp_Id = Emp_id
and i.End_Date > Start_Date
and End_Date > i.Start_Date
order by i.Start_Date
) x
group by emp_id, x.id
source to share