Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
| Author |
Topic |
|
rypi
Yak Posting Veteran
55 Posts |
Posted - 2011-03-30 : 16:37:38
|
| I am trying to create an employee turnover report but have no idea on how to create the query. For example purposes, there is an EmployeeDateDetails table like the following.ID EmpID HiredDate TerminationDate(NULL)1 10 2011-01-01 NULL2 11 2011-02-01 2011-03-253 12 2011-03-01 NULL4 13 2011-04-01 NULLI want to have a query that will return results something like this:Date OpenBal #Joined #Left ClosingBalanceJan 2011 0 1 0 1 Feb 2011 1 1 0 2 Mar 2011 2 1 1 2 Apr 2011 2 1 0 3 May 2011 3My sql skills are fairly limited and don't even know where to start with something like this. Any help would be greatly appreciated.Thanks. |
|
|
Bustaz Kool
Master Smack Fu Yak Hacker
1834 Posts |
Posted - 2011-03-30 : 19:51:38
|
| [CODE]declare @StartDate Datedeclare @table table ( ID int identity(1, 1), EmpID varchar(20), HiredDate date, TerminationDate date null )insert into @table ( EmpID, HiredDate, TerminationDate )values ('10', '2011-01-01', NULL), ('11', '2011-02-01', '2011-03-25'), ('12', '2011-03-01', NULL), ('13', '2011-04-01', NULL)select @StartDate = min(DateAdd(month, DateDiff(month, 0, HiredDate), 0))from @table;with MonthDates -- Convert dates to first of monthas (select cast(DateAdd(month, DateDiff(month, 0, HiredDate), 0) as date) HiredDate, cast(DateAdd(month, DateDiff(month, 0, TerminationDate), 0) as date) TerminationDatefrom @table),MonthDatesAggregated -- Aggregate counts (hired/fired) per monthas (select HiredDate RptYearMonth, count(HiredDate) HiredCount, count(TerminationDate) TerminatedCountfrom MonthDatesgroup by HiredDate),Turnoveras (select @StartDate RptYearMonth, 0 OpeningHeadCount, HiredCount, TerminatedCount, HiredCount - TerminatedCount ClosingHeadCountfrom MonthDatesAggregatedwhere RptYearMonth = @StartDateunion allselect cast(DateAdd(month, 1, t.RptYearMonth) as date), ClosingHeadCount, m.HiredCount, m.TerminatedCount, ClosingHeadCount + m.HiredCount - m.TerminatedCountfrom Turnover tinner join MonthDatesAggregated m on t.RptYearMonth = m.RptYearMonth)select *from Turnoverorder by RptYearMonth[/CODE]=======================================Elitism is the slur directed at merit by mediocrity. -Sydney J. Harris, journalist (1917-1986) |
 |
|
|
rypi
Yak Posting Veteran
55 Posts |
Posted - 2011-03-31 : 12:06:13
|
| Thank you so much Bustaz!This is amazing, I have been struggling with this for a couple days and have gotten nowhere. Thanks for your help!I have been messing around with the query, but am still having a couple issues.If there is a gap between hired dates, like this for example:insert into @table ( EmpID, HiredDate, TerminationDate )values ('10', '2011-01-01', NULL), ('11', '2011-04-01', '2011-05-25'), ('12', '2011-05-01', NULL), ('13', '2011-06-01', NULL)The results are only returned for the first date, and the next month includes a HiredCount that doesn't exist, like this:2011-01-01 0 1 0 12011-02-01 1 1 0 2Would it be possible to fill the gaps so it returned like this?:2011-01-01 0 1 0 12011-02-01 1 0 0 12011-03-01 1 0 0 12011-04-01 1 1 0 22011-05-01 2 1 1 12011-06-01 1 1 0 22011-07-01 2 1 0 32011-08-01 3 0 0 3And if the start and end dates were provided as parameters could the query filter between those dates but include an OpeningHeadCount for employees hired before the startdate. For example, if startdate = '2011-05-01' and enddate = '2011-09-01' the results would be:2011-05-01 2 1 1 22011-06-01 2 1 0 32011-07-01 3 0 0 32011-08-01 3 0 0 32011-09-01 3 0 0 3Thanks again for your help. |
 |
|
|
Bustaz Kool
Master Smack Fu Yak Hacker
1834 Posts |
Posted - 2011-03-31 : 16:41:35
|
| I thought about gaps when putting this together but didn't want to make things too complicated. The solution I had in mind was to use a Calendar table. The Calendar would insure that every month was represented in the date range of interest. In short, the approach would join the Calendar to your data table. Use an outer join so that if there is no personnel activity, you'll still get the Calendar data. This could be done in the "with MonthDates" section.You'll need to:1) Create a Calendar table of months (or a real Calendar and only use the first of the month)2) OUTER JOIN the data to the Calendar in the MonthDates section3) GROUP BY Calendar.YearMonth instead of HiredDate in the MonthDatesAggregated sectionHTH=======================================Elitism is the slur directed at merit by mediocrity. -Sydney J. Harris, journalist (1917-1986) |
 |
|
|
rypi
Yak Posting Veteran
55 Posts |
Posted - 2011-04-05 : 01:26:15
|
| Almost have this working as it should, but there are a couple small issues still. The first issue is when the first employee hire is counted. In this example it's on 2011-01-01. It's counted correctly in the first row of the returned results, but then the very next month has a hired count of 1 as well, when it should be 0.The second issue is that the hired/terminated values are added to the next month. In this example there are 2 new hires on 2011-03-01but the results show them as hired in 2011-04The results from this example should be:Date OpeningBalance Hired Terminated ClosingBalance2011-01-01 0 1 0 12011-02-01 1 0 0 12011-03-01 1 2 0 32011-04-01 3 1 0 42011-05-01 4 1 0 52011-06-01 5 0 1 42011-07-01 4 0 0 42011-08-01 4 0 0 42011-09-01 4 3 0 72011-10-01 7 0 0 72011-11-01 7 0 0 72011-12-01 7 0 0 72012-01-01 7 0 0 7Declare @pStartDate date = '2011-01-01'Declare @pEndDate date = '2012-01-01'declare @Employee table ( HiredDate date, TerminationDate date null )insert into @Employee ( HiredDate, TerminationDate )values ('2011-01-01', NULL), ('2011-03-01', NULL), ('2011-03-01', NULL), ('2011-04-01', '2011-06-25'), ('2011-05-01', NULL), ('2011-09-01', NULL), ('2011-09-01', NULL), ('2011-09-01', NULL)declare @Calendar table ( YearMonth date )insert into @Calendar ( YearMonth )values ('2011-01-01'), ('2011-02-01'), ('2011-03-01'), ('2011-04-01'), ('2011-05-01'), ('2011-06-01'), ('2011-07-01'), ('2011-08-01'), ('2011-09-01'), ('2011-10-01'), ('2011-11-01'), ('2011-12-01');WITH MonthDates -- Convert dates to first of month AS ( SELECT CAST(DateAdd(month, DateDiff(month, 0, HiredDate), 0) as date) HiredDate, CAST(DateAdd(month, DateDiff(month, 0, TerminationDate), 0) as date) TerminationDate FROM @Employee E ), MonthDatesAggregated -- Aggregate counts (hired/fired) per month AS ( SELECT YearMonth AS "Date", Hired = SUM(CASE WHEN YearMonth = MD1.HiredDate THEN 1 ELSE 0 END), Terminated = SUM(CASE WHEN YearMonth = MD1.TerminationDate THEN 1 ELSE 0 END) FROM @Calendar LEFT JOIN MonthDates AS MD1 ON YearMonth = MD1.HiredDate OR YearMonth = MD1.TerminationDate WHERE YearMonth Between @pStartDate AND @pEndDate GROUP BY YearMonth ), Turnover AS ( SELECT @pStartDate AS "Date", (SELECT COUNT(*) FROM @Employee WHERE TerminationDate IS Null AND HiredDate < @pStartDate) AS OpeningBalance, Hired, Terminated, ((SELECT COUNT(*) FROM @Employee WHERE TerminationDate IS Null AND HiredDate < @pStartDate) + Hired) - Terminated AS ClosingBalance FROM MonthDatesAggregated WHERE "Date" = @pStartDate UNION all SELECT CAST(DateAdd(month, 1, t.Date) as date), ClosingBalance, m.Hired, m.Terminated, ClosingBalance + m.Hired - m.Terminated FROM Turnover t INNER JOIN MonthDatesAggregated m on t.Date = m.Date ) SELECT * FROM Turnover ORDER BY "Date" Thanks! |
 |
|
|
Bustaz Kool
Master Smack Fu Yak Hacker
1834 Posts |
Posted - 2011-04-05 : 20:33:47
|
| Here's my latest. The Terminated count was correct but was assigned to the wrong month. I finally decided that we needed to group by two different entities. I also corrected the join condition into the recursive cte.[CODE]declare @StartDate Datedeclare @table table ( ID int identity(1, 1), EmpID varchar(20), HiredDate date, TerminationDate date null )insert into @table ( EmpID, HiredDate, TerminationDate )values ('10', '2011-01-01', NULL), ('11', '2011-04-01', '2011-05-25'), ('12', '2011-05-01', NULL), ('13', '2011-06-01', NULL)declare @Calendar table ( YearMonth date )insert into @Calendar ( YearMonth )values ('2011-01-01'), ('2011-02-01'), ('2011-03-01'), ('2011-04-01'), ('2011-05-01'), ('2011-06-01'), ('2011-07-01'), ('2011-08-01'), ('2011-09-01'), ('2011-10-01'), ('2011-11-01'), ('2011-12-01')select @StartDate = min(DateAdd(month, DateDiff(month, 0, HiredDate), 0))from @table;with MonthDates -- Convert dates to first of monthas (select cast(DateAdd(month, DateDiff(month, 0, HiredDate), 0) as date) HiredDate, cast(DateAdd(month, DateDiff(month, 0, TerminationDate), 0) as date) TerminationDatefrom @table),MonthDatesAggregated -- Aggregate counts (hired/fired) per monthas (select a.RptYearMonth, a.HiredCount, b.TerminatedCountfrom ( select c.YearMonth RptYearMonth, count(HiredDate) HiredCount from @Calendar c left outer join MonthDates md on c.YearMonth = md.HiredDate group by c.YearMonth ) ainner join ( select c.YearMonth RptYearMonth, count(TerminationDate) TerminatedCount from @Calendar c left outer join MonthDates md on c.YearMonth = md.TerminationDate group by c.YearMonth ) b on a.RptYearMonth = b.RptYearMonth),Turnoveras (select @StartDate RptYearMonth, 0 OpeningHeadCount, HiredCount, TerminatedCount, HiredCount - TerminatedCount ClosingHeadCountfrom MonthDatesAggregatedwhere RptYearMonth = @StartDateunion allselect cast(DateAdd(month, 1, t.RptYearMonth) as date), ClosingHeadCount, m.HiredCount, m.TerminatedCount, ClosingHeadCount + m.HiredCount - m.TerminatedCountfrom Turnover tinner join MonthDatesAggregated m on cast(DateAdd(month, 1, t.RptYearMonth) as date) = m.RptYearMonth)select *from Turnoverorder by RptYearMonth[/CODE]Over and out...=======================================Elitism is the slur directed at merit by mediocrity. -Sydney J. Harris, journalist (1917-1986) |
 |
|
|
Bustaz Kool
Master Smack Fu Yak Hacker
1834 Posts |
Posted - 2011-04-06 : 11:26:13
|
| Here's my latest. The Terminated count was correct but was assigned to the wrong month. I finally decided that we needed to group by two different entities. I also corrected the join condition in the recursive cte.[CODE]declare @StartDate Datedeclare @table table ( ID int identity(1, 1), EmpID varchar(20), HiredDate date, TerminationDate date null )insert into @table ( EmpID, HiredDate, TerminationDate )values ('10', '2011-01-01', NULL), ('11', '2011-04-01', '2011-05-25'), ('12', '2011-05-01', NULL), ('13', '2011-06-01', NULL)declare @Calendar table ( YearMonth date )insert into @Calendar ( YearMonth )values ('2011-01-01'), ('2011-02-01'), ('2011-03-01'), ('2011-04-01'), ('2011-05-01'), ('2011-06-01'), ('2011-07-01'), ('2011-08-01'), ('2011-09-01'), ('2011-10-01'), ('2011-11-01'), ('2011-12-01')select @StartDate = min(DateAdd(month, DateDiff(month, 0, HiredDate), 0))from @table;with MonthDates -- Convert dates to first of monthas (select cast(DateAdd(month, DateDiff(month, 0, HiredDate), 0) as date) HiredDate, cast(DateAdd(month, DateDiff(month, 0, TerminationDate), 0) as date) TerminationDatefrom @table),MonthDatesAggregated -- Aggregate counts (hired/fired) per monthas (select a.RptYearMonth, a.HiredCount, b.TerminatedCountfrom ( select c.YearMonth RptYearMonth, count(HiredDate) HiredCount from @Calendar c left outer join MonthDates md on c.YearMonth = md.HiredDate group by c.YearMonth ) ainner join ( select c.YearMonth RptYearMonth, count(TerminationDate) TerminatedCount from @Calendar c left outer join MonthDates md on c.YearMonth = md.TerminationDate group by c.YearMonth ) b on a.RptYearMonth = b.RptYearMonth),Turnoveras (select @StartDate RptYearMonth, 0 OpeningHeadCount, HiredCount, TerminatedCount, HiredCount - TerminatedCount ClosingHeadCountfrom MonthDatesAggregatedwhere RptYearMonth = @StartDateunion allselect cast(DateAdd(month, 1, t.RptYearMonth) as date), ClosingHeadCount, m.HiredCount, m.TerminatedCount, ClosingHeadCount + m.HiredCount - m.TerminatedCountfrom Turnover tinner join MonthDatesAggregated m on cast(DateAdd(month, 1, t.RptYearMonth) as date) = m.RptYearMonth)select *from Turnoverorder by RptYearMonth[/CODE]Over and out...=======================================Elitism is the slur directed at merit by mediocrity. -Sydney J. Harris, journalist (1917-1986) |
 |
|
|
|
|
|
|
|