Author |
Topic |
hanifbarik
Starting Member
9 Posts |
Posted - 2014-09-08 : 09:20:35
|
I've been struggling with a query on and off for a few weeks now. In it's simplest form, I am looking to divide time if there is an overlap between groups. If there are 100 hours each month and there is an overlap between groups, it should divide that time by 2 for the overlapping period only regardless of how many groups overlap, otherwise, return 100 for periods where there is no overlap.Here is an illustration of what I am after. The hours in the coloured cells is what I am expecting back and includes example for employee Adam Smith only.[url]http://imgur.com/cU4ivW5[/url]SQL Fiddle example[url]http://sqlfiddle.com/#!3/e973c/1[/url]This is the results I would expect back.EMPLOYEENAME GROUPNAME STARTDATE ENDDATE WORKHOURSAdam Smith Primary January, 01 2014 January, 31 2014 50Adam Smith Tertiary January, 01 2014 January, 31 2014 50Adam Smith Primary February, 01 2014 February, 28 2014 50Adam Smith Secondary February, 01 2014 February, 28 2014 50Adam Smith Primary March, 01 2014 March, 31 2014 100Adam Smith Primary May, 01 2014 May, 31 2014 100Adam Smith Primary June, 01 2014 June, 30 2014 50Adam Smith Tertiary June, 01 2014 June, 30 2014 50Adam Smith Primary July, 01 2014 July, 31 2014 100Adam Smith Primary August, 01 2014 August, 31 2014 50Adam Smith Secondary August, 01 2014 August, 31 2014 50Adam Smith Secondary September, 01 2014 September, 30 2014 50Adam Smith Tertiary September, 01 2014 September, 30 2014 50Adam Smith Primary November, 01 2014 November, 30 2014 100Adam Smith Primary January, 01 2015 February, 28 2015 100Adam Smith Secondary January, 01 2015 February, 28 2015 100Julie Tyler Primary January, 01 2014 January, 31 2014 100Julie Tyler Primary February, 01 2014 March, 31 2014 50Julie Tyler Secondary February, 01 2014 March, 31 2014 50Julie Tyler Secondary April, 01 2014 July, 31 2014 100Julie Tyler Secondary November, 01 2014 November, 14 2014 100Julie Tyler Secondary November, 15 2014 November, 30 2014 50Julie Tyler Tertiary November, 15 2014 November, 30 2014 50Julie Tyler Tertiary December, 01 2014 January, 31 2015 100 Can the above be achieved using a SQL view or I would need to use stored procedures/functions? |
|
gbritton
Master Smack Fu Yak Hacker
2780 Posts |
Posted - 2014-09-08 : 13:01:36
|
CAn you please post your table definition and some sample data? (as insert into) |
|
|
hanifbarik
Starting Member
9 Posts |
Posted - 2014-09-08 : 13:37:38
|
Here is the table definition and sample data.create table EMPLOYEE ( EMPLOYEEID int, EMPLOYEENAME varchar(200), WORKHOURS int);insert into EMPLOYEE (EMPLOYEEID, EMPLOYEENAME, WORKHOURS)values (1, 'Adam Smith', 100);insert into EMPLOYEE (EMPLOYEEID, EMPLOYEENAME, WORKHOURS)values (2, 'Julie Tyler', 100);create table GROUPS ( GROUPID int, GROUPNAME varchar(200));insert into GROUPS (GROUPID, GROUPNAME)values (1, 'Primary')insert into GROUPS (GROUPID, GROUPNAME)values (2, 'Secondary')insert into GROUPS (GROUPID, GROUPNAME)values (3, 'Tertiary')create table EMPLOYEEGROUPS ( EMPLOYEEID int, GROUPID int, STARTDATE datetime, ENDDATE datetime);insert into EMPLOYEEGROUPS (EMPLOYEEID, GROUPID, STARTDATE, ENDDATE)values (1, 1, '2014-01-01', '2014-03-31');insert into EMPLOYEEGROUPS (EMPLOYEEID, GROUPID, STARTDATE, ENDDATE)values (1, 1, '2014-05-01', '2014-08-31');insert into EMPLOYEEGROUPS (EMPLOYEEID, GROUPID, STARTDATE, ENDDATE)values (1, 1, '2014-11-01', '2014-11-30');insert into EMPLOYEEGROUPS (EMPLOYEEID, GROUPID, STARTDATE, ENDDATE)values (1, 1, '2015-01-01', '2015-02-28');insert into EMPLOYEEGROUPS (EMPLOYEEID, GROUPID, STARTDATE, ENDDATE)values (1, 2, '2014-02-01', '2014-02-28');insert into EMPLOYEEGROUPS (EMPLOYEEID, GROUPID, STARTDATE, ENDDATE)values (1, 2, '2014-08-01', '2014-09-30');insert into EMPLOYEEGROUPS (EMPLOYEEID, GROUPID, STARTDATE, ENDDATE)values (1, 2, '2015-01-01', '2015-02-28');insert into EMPLOYEEGROUPS (EMPLOYEEID, GROUPID, STARTDATE, ENDDATE)values (1, 3, '2014-01-01', '2014-02-28');insert into EMPLOYEEGROUPS (EMPLOYEEID, GROUPID, STARTDATE, ENDDATE)values (1, 3, '2014-06-01', '2014-06-30');insert into EMPLOYEEGROUPS (EMPLOYEEID, GROUPID, STARTDATE, ENDDATE)values (1, 3, '2014-09-01', '2014-09-30');insert into EMPLOYEEGROUPS (EMPLOYEEID, GROUPID, STARTDATE, ENDDATE)values (2, 1, '2014-01-01', '2014-03-31');insert into EMPLOYEEGROUPS (EMPLOYEEID, GROUPID, STARTDATE, ENDDATE)values (2, 2, '2014-02-01', '2014-07-31');insert into EMPLOYEEGROUPS (EMPLOYEEID, GROUPID, STARTDATE, ENDDATE)values (2, 2, '2014-11-01', '2014-11-30');insert into EMPLOYEEGROUPS (EMPLOYEEID, GROUPID, STARTDATE, ENDDATE)values (2, 3, '2014-11-15', '2015-01-31'); |
|
|
hanifbarik
Starting Member
9 Posts |
Posted - 2014-09-08 : 14:26:57
|
I have also added a column to EMPLOYEEGROUPS table to mimic a primary key.-- Add an integer columnalter table EMPLOYEEGROUPSadd EMPLOYEEGROUPSID int-- Generate a sequential list of numbers;with CTE_PKGENERATOR as (select *,ROW_NUMBER() OVER(ORDER BY EMPLOYEEID) AS rnfrom EMPLOYEEGROUPS)-- Update new column with number sequenceupdate eset e.EMPLOYEEGROUPSID = c.rnfrom EMPLOYEEGROUPS einner join CTE_PKGENERATOR c on c.EMPLOYEEID = e.EMPLOYEEID and c.GROUPID = e.GROUPID and c.STARTDATE = e.STARTDATE and c.ENDDATE = e.ENDDATE |
|
|
hanifbarik
Starting Member
9 Posts |
Posted - 2014-09-08 : 14:39:07
|
I've had an attempt at trying to find the answer but I suspect I've taken a sledge hammer to crack a nut.Essentially, I am taking the date range for each group (primary, secondary and tertiary) and splitting to days. This as you can image creates a massive result set - a row (for each day) between each date range for each group for each employee.I then flag a 2 to indicate any days for primaries which overlap with secondary or tertiary. I do the same for secondary, flag a 2 if days overlap with primary and tertiary and again if tertiary overlaps with the other 2 groups.I suspect this approach may be completely over-the-top for a large amount of data.The result set I get back isn't quite correct as when I group the data, it's grouping non-sequential days, for example, May 2014 with July 2015 even though these should be separate as there is an overlap in June. See image above.Anyway, for what it's worth, here is the query.Thanks.;with CTE_PRIMARYas (select eg.EMPLOYEEGROUPSID, e.EMPLOYEEID, e.EMPLOYEENAME, g.GROUPID, g.GROUPNAME, eg.STARTDATE, eg.ENDDATE, e.WORKHOURS from EMPLOYEEGROUPS eginner join EMPLOYEE e on e.EMPLOYEEID = eg.EMPLOYEEIDinner join GROUPS g on g.GROUPID = eg.GROUPIDwhere g.GROUPNAME = 'Primary' UNION allselect EMPLOYEEGROUPSID, EMPLOYEEID, EMPLOYEENAME, GROUPID, GROUPNAME, DATEADD(d,1,STARTDATE), ENDDATE, WORKHOURSfrom CTE_PRIMARYwhere STARTDATE < ENDDATE),CTE_SECONDARYas (select eg.EMPLOYEEGROUPSID, e.EMPLOYEEID, e.EMPLOYEENAME, g.GROUPID, g.GROUPNAME, eg.STARTDATE, eg.ENDDATE, e.WORKHOURS from EMPLOYEEGROUPS eginner join EMPLOYEE e on e.EMPLOYEEID = eg.EMPLOYEEIDinner join GROUPS g on g.GROUPID = eg.GROUPIDwhere g.GROUPNAME = 'Secondary' UNION allselect EMPLOYEEGROUPSID, EMPLOYEEID, EMPLOYEENAME, GROUPID, GROUPNAME, DATEADD(d,1,STARTDATE), ENDDATE, WORKHOURSfrom CTE_SECONDARYwhere STARTDATE < ENDDATE),CTE_TERTIARYas (select eg.EMPLOYEEGROUPSID, e.EMPLOYEEID, e.EMPLOYEENAME, g.GROUPID, g.GROUPNAME, eg.STARTDATE, eg.ENDDATE, e.WORKHOURS from EMPLOYEEGROUPS eginner join EMPLOYEE e on e.EMPLOYEEID = eg.EMPLOYEEIDinner join GROUPS g on g.GROUPID = eg.GROUPIDwhere g.GROUPNAME = 'Tertiary' UNION allselect EMPLOYEEGROUPSID, EMPLOYEEID, EMPLOYEENAME, GROUPID, GROUPNAME, DATEADD(d,1,STARTDATE), ENDDATE, WORKHOURSfrom CTE_TERTIARYwhere STARTDATE < ENDDATE),-- select * from CTE_PRIMARY option (maxrecursion 0) -- 392-- select * from CTE_SECONDARY option (maxrecursion 0) -- 359-- select * from CTE_TERTIARY option (maxrecursion 0) -- 197CTE_OVERLAP as (select *, case when exists (select 1 from CTE_SECONDARY s where s.EMPLOYEEID = p.EMPLOYEEID and s.STARTDATE = p.STARTDATE) or exists (select 1 from CTE_TERTIARY t where t.EMPLOYEEID = p.EMPLOYEEID and t.STARTDATE = p.STARTDATE) then 2 else 1 end as OverlapDatefrom CTE_PRIMARY punion allselect *, case when exists (select 1 from CTE_PRIMARY p where p.EMPLOYEEID = s.EMPLOYEEID and p.STARTDATE = s.STARTDATE) or exists (select 1 from CTE_TERTIARY t where t.EMPLOYEEID = s.EMPLOYEEID and t.STARTDATE = s.STARTDATE) then 2 else 1 end as OverlapDatefrom CTE_SECONDARY sunion allselect *, case when exists (select 1 from CTE_PRIMARY p where p.EMPLOYEEID = t.EMPLOYEEID and p.STARTDATE = t.STARTDATE) or exists (select 1 from CTE_TERTIARY t where t.EMPLOYEEID = t.EMPLOYEEID and t.STARTDATE = t.STARTDATE) then 2 else 1 end as OverlapDatefrom CTE_TERTIARY t)--select *,--STARTDATE - ROW_NUMBER() OVER(PARTITION BY EMPLOYEEGROUPSID, GROUPNAME ORDER BY STARTDATE) AS rn-- from CTE_OVERLAP--order by EMPLOYEENAME, GROUPNAME, STARTDATE--option (maxrecursion 0) -- Total 948select EMPLOYEEGROUPSID, EMPLOYEENAME, GROUPNAME, OverlapDate, min(STARTDATE) as STARTDT, max(STARTDATE) as ENDDT, min(WORKHOURS) as WRKHRSfrom CTE_OVERLAPgroup by EMPLOYEEGROUPSID, EMPLOYEENAME, GROUPNAME, OverlapDateorder by EMPLOYEENAME, STARTDToption (maxrecursion 0) |
|
|
|
|
|