Calculating day difference between 2 date columns where dates are "messy" - sql

Query:
SELECT EmployeeId,
HireDate,
TerminationDate
FROM dbo.Employment
WHERE EmployeeId = 318312
ORDER BY HireDate,
TerminationDate;
Result:
I need to get the number of days this person worked. The problem is that the termination date is "messy" ... meaning, I might not get a termination date for every hire date.
So basically I need to put the dates in "order" ... and then figure out how many days the person had of employment.
In this scenario, it goes as follows:
Person is hired on 2012-12-19, has no termination date and then was re-hired on 2012-12-27.
Person terminates on 2014-03-01 and then is re-hired on 2014-06-05.
Person has no termination date after 2014-06-05 so it is assumed he was re-hired on 2014-06-06 rather than 2014-06-05.
How do I go about creating a query that captures the number of EMPLOYMENT days (excluding gaps), in this scenario?
I would be grouping this by EmployeeID as I'm running this for multiple employees.
This problem is really kicking my butt and I need some help.

This is rather complex but uses LAG to get the previous row, put that in a CTE and then pick out the data with a CASE:
;WITH dataCTE AS
(SELECT EmployeeID,
LAG(HireDate, 1) OVER (ORDER BY HireDate) PreviousHireDate,
LAG(TerminationDate, 1) OVER (ORDER BY HireDate) PreviousTerminationDate,
HireDate, TerminationDate
FROM Employment)
SELECT EmployeeID,
CASE WHEN PreviousTerminationDate IS NULL THEN PreviousHireDate ELSE HireDate END AS HireDate,
TerminationDate,
DATEDIFF(DAY, CASE WHEN PreviousTerminationDate IS NULL THEN PreviousHireDate ELSE HireDate END, TerminationDate) AS NumberOfDays
FROM dataCTE
WHERE TerminationDate IS NOT NULL
Example fiddle here: http://sqlfiddle.com/#!6/1f839e/22

Related

MoM changes in sql

Is it possible to do month over month salary changes based on last 2 months and grouping this by unique id?How do I go about this issue? Thank you.
I tried this
SELECT
employee_id,
fiscal_year,
salary,
LAG(salary) OVER (
PARTITION BY employee_id
ORDER BY fiscal_year) previous_salary
FROM
basic_pays
where
date between Dateadd(month,-2,_date) And Getdate()
but it's bringing back 0 for all and runs a very long time.

Trying to determine length of time from a set date to current date from a table in SQL

I have an employees table:
I am trying to determine the length of time certain employees - e.g. Luke and Helen - have been working since the start date up to now, along with the Team they belong to.
So far I can show the table based on their name and team eg:
SELECT EmployeeName, Team
FROM employees
WHERE EmployeeName in ('Luke', 'Helen');
But I am having issues trying to show the table with the length of time each employee has been in the company:
SELECT EmployeeName, Team, count (*) AS Tenure
FROM
(
SELECT EmployeeName, Team
FROM employees
WHERE dateddiff(mm, StartDate, getdate()) AND EmployeeName in ('Luke', 'Helen')
)a
GROUP BY EmployeeName, department;
Any help would be greatly appreciated
You need to select the columns that you want. These appear to be:
SELECT EmployeeName, Team, DATEDIFF(month, StartDate, getdate())
FROM employees
WHERE EmployeeName IN ('Luke', 'Helen');
The DATEDIFF() goes in the SELECT, not the WHERE.

How to calculate the total numbers of employees available as at a specific day given their date of entry?

I am using SQL Server 2012 and I have a table in my database called Employee.
Assuming it has only 3 columns, namely EmpID, Dept, DateOfEntry, Date Left, what would be the SQL query syntax that will give the count of employees as at, say, 2017-05-31
Each row in the table represents a single employee.
Here is how the Employee Table stands:
EmpId Dept DateOfEntry DateLeft
100 F&B 2015-06-05 2016-01-02
125 Kitchen 2016-02-12 2016-03-10
151 Finance 2018-05-03 NULL
...
UPDATE: Apologies for missing the DateLeft column in the above table. I have updated the question accordingly.
It seems that the OP isn't going to post their attempt.
This is a very simple query, and with a bit of a Google/Bing/Yahoo you probably would have found the syntax on how to use a WHERE clause with Greater Than (>) and Less Than (<) expressions. Anyway, the answer you want is:
DECLARE #Date date;
SET #Date = '20170531';
SELECT COUNT(*) AS Employees
FROM YourTable
WHERE DateOfEntry <= #Date
AND (DateLeft > #Date --I assume that they are no longer emplyed on the day they leave, other use >=
OR DateLeft IS NULL);
select count(*),dept from employee where '31-may-2017'>DateOfEntry
and '31_-may-2017'<DateOfLeft group by dept;
If you want at in single date than try this:
select count(EmpID) from employee
where DateOfEntry>='31-may-2017' and (dateleft is null or dateleft< getdate())
You would count the employees who are at work as of that date, right? Meaning they should have entered on or BEFORE that day:
select count(*)
from employees
where DateOfEntry <= '20170531' and
(dateLeft is null OR dateLeft >= '20170531');
Note: If you wouldn't accept the person as at work on her\his dateLeft then change >= to >.

SQL Server one-to-many relational IF query

I have an employees table that has many employee_records. The employee_records table has a column named event_type and it is an enum that can be either hire-date, promotion, termination, title-change, or rehire. I am attempting to calculate an employees total time employed and make some calculations based on how long they have been employed.
How can I add a column that gives me the total days that they have been employed?
Essentially, I need to see if they have a record with the event_type = termination and if they do, then I need to see if they have a rehire date, and if they do, then I need to use their rehire date as the first day of their employment and calculate their time of employment that way.
As for a result, I simply need a days_employed column that reflects the actual amount of days they have been employed.
Here is what I have so far.
SELECT
employees.id,
first_name,
last_name,
email,
event_type,
CASE
WHEN DATEDIFF(DAY, employee_records.created_at, SYSDATETIME()) < 365 * 5
THEN 1
WHEN DATEDIFF(DAY, employee_records.created_at, SYSDATETIME()) < 365 * 10
THEN 2
ELSE 3
END AS benfits_type,
DATEDIFF(DAY, employee_records.created_at, SYSDATETIME()) AS days_employed,
employee_records.created_at AS hire_date
FROM
employees
JOIN
employee_records ON employees.id = employee_records.employee_id
ORDER BY
employees.id ASC;
Here is an example of how you could do this. I'll post the query first and then walk through my explanation. If I understood you correctly, you were not looking for total days the employee was hired, but rather the total days of the employee's most recent employment at the company (Max hire date to max termination date or today).
;WITH hired
AS (SELECT employee_records.employee_id id,
Max(employee_records.created_at) created_at
FROM employee_records
WHERE event_type = 'hire-date'
GROUP BY employee_records.employee_id),
latest
AS (SELECT employees.id
id,
Isnull(Cast(Max(employee_records.created_at) AS DATE), Getdate()
)
created_at
FROM employees
LEFT JOIN employee_records
ON employees.id = employee_records.employee_id
AND employee_records.event_type = 'termination'
GROUP BY employees.id)
SELECT *,
Datediff(day, hired, latestday) DaysEmployeed
FROM (SELECT hired.id,
hired.created_at AS Hired,
CASE
WHEN hired.created_at > latest.created_at THEN Cast(
Getdate() AS DATE)
ELSE latest.created_at
END AS LatestDay
FROM hired
INNER JOIN latest
ON hired.id = latest.id) JoinedCTEs
First of all I know you mentioned event_type is an integer, but for easy explanation I used a varchar.
Two CTEs to start.
First there is "hired" which will get you the latest hire date. So if an employee has multiple hire dates, it grabs the latest date.
Second there is "latest" which is the latest date an employee has a termination date, but also uses today's date as a placeholder date if an employee has never been terminated.
The final query joins the two CTEs and does a datediff by day to determine how many days an employee has been at the company. If the termination date is earlier than the hire date (An employee who was hired, terminated, rehired and is still with the company), it will take today's date as the latest date to count.

How to calculate the longest period in days that a company has gone without headcount change?

Given an employees table with the columns EmpID,FirstName,LastName,StartDate, and EndDate.
I want to use a query on Oracle to calculate the longest period in days that a company has gone without headcount change.
Here is my query:
select MAX(endDate-startDate)
from
(select endDate
from employees
where endDate is not null)
union all
(select startDate
from employees)
But I got an error:
ORA-00904:"STARTDATE":invalid identifier
How can I fix this error?
Is my query the correct answer to this question?
Thanks
You aren't returning the startDate in the sub-query. Add startDate to the inner query.
select MAX(endDate-startDate) from
(select startDate, endDate from employees where endDate is not null)
union all
(select startDate from employees)
EDIT:
You can also try this:
select MAX(endDate-startDate) from employees where endDate is not null
However, I don't think your query is what you're looking for as it only lists the longest term employee that no longer works at the company.
In a simplistic view, you would want to put together all the start-dates (when the headcount increases) and all the end-dates (when it decreases), combine them all, arrange them in increasing order, measure the differences between consecutive dates, and take the max.
"Put together" is a UNION ALL, and measure differences between "consecutive" dates can be done with the analytic function lag().
One complication: one employee may start exactly on the same date another is terminated, so the headcount doesn't change. More generally, on any given date there may be starts and ends, and you need to exclude the dates when there are an equal number of starts and ends. So the first part of the solution is more complicated: you need to group by date and compare the start and end counts.
Something like this may work (not tested!):
with d ( dt, flag ) as (
select start_date, 's' from employees union all
select end_date , 'e' from employees
),
prep ( int ) as
select dt - lag(dt) over (order by dt)
from d
group by dt
having count(case flag when 's' then 1 end) !=
count(case flag when 'e' then 1 end)
)
select max(int) as max_interval
from prep
;
Edit - Gordon has a good point in his solution: perhaps the longest period without a change in headcount is the current period (ending "now"). For this reason, one needs to add SYSDATE to the UNION ALL, like he did. It can be added with either flag (for example 's' to be specific).
I think the answer to your question is something like this:
select max(span)
from (select (lead(dte) over (order by dte) - dte) as span
from (select startDate as dte from employees union all
select endDate as dte from employees union all
select trunc(sysdate) from dual
) d
) d;
A head-count change (presumably) occurs when an employee starts or stops. Hence, you want the largest interval between two such adjacent dates.