Need to calculate overtime and regular hours - sql

I have 1 table as attendance
|AttendanceresultId | AccountID | Intime | Outtime | ShiftID |
| 1 | 1234 | 2016-06-21 06:56:00 | NULL | 1 |
| 2 | 1234 | NULL | 2016-06-21 17:02:00 | 1 |
| 3 | 1234 | 2016-06-22 06:56:00 | NULL | 1 |
| 4 | 1234 | NULL | 2016-06-22 17:02:00 | 1 |
| 5 | 1235 | 2016-06-21 22:55:00 | NULL | 3 |
| 6 | 1235 | NULL | 2016-06-22 06:00:00 | 3 |
| 7 | 1235 | 2016-06-22 22:55:00 | NULL | 3 |
| 8 | 1235 | NULL | 2016-06-23 07:00:00 | 3 |
Another table is shift table
| ShiftId | Starttime | Endtime |
| 1 | 07:00:00.00 | 16:00:00.00 |
| 3 | 23:00:00.00 | 06:00:00.00 |
I want to calculate the total number of working hours and over time hours for an employee lets say,
Expected Output,
| AccountID | NormalHours | OvertimeHours |
| 1234 | 18:08 Hrs | 02:04 Hrs |
| 1235 | 14:10 Hrs | 01:00 Hrs |
I am newbie in sql server can any one suggest anything

I broke this into four Parts to adjust for the lack of the lag function in 2008:
1) Common Table Expression to organize the records (so as not to count on the identity field)
2) Calculates the total minutes worked and worked for overtime without using the lag function
3) I then summarize those minutes - and split them into hours and minutes.
4) I then format them.
That said, there are quite a few possible places where I think real world data might create issues.
Updated version that does not use the lag function
WITH TimeRecords (AccountID, InOutTime, Intime, OutTime, Shift,
RowNumber) AS
(SELECT att.AccountID, COALESCE(att.Intime,att.OutTime),
att.Intime, att.OutTime, att.Shift,
ROW_NUMBER() OVER(ORDER BY att.AccountID,
COALESCE(att.InTime, att.OutTime) )
FROM dbo.attendance att)
,
ShiftTime AS (
SELECT trecout.AccountID ,
trecin.InTime,
trecout.OutTime,
-- sh.EndTime ,
-- Number of Minutes for the entire shift
CASE WHEN trecout.OutTime IS NOT NULL
THEN DATEDIFF(mi,
trecin.InTime,
trecout.OutTime)
ELSE 0
END AS TotalTime ,
-- Number of Minutes for overtime
CASE WHEN trecout.OutTime IS NOT NULL
THEN DATEDIFF(mi, sh.EndTime,
CAST(trecout.OutTime AS TIME))
ELSE 0
END AS OverTime
FROM TimeRecords trecout
JOIN TimeRecords trecin
ON trecout.RowNumber -1 = trecin.RowNumber
JOIN dbo.ShiftID sh
ON trecout.[Shift] = sh.ShiftID
)
,
-- Summarize the data
ShiftTimeSum
AS ( SELECT st.AccountID ,
FLOOR(SUM(st.TotalTime - st.OverTime) / 60) AS RegularHours ,
SUM(st.TotalTime - st.OverTime) % 60 AS RegularMinutes ,
SUM(st.OverTime) / 60 AS OverTimeHours ,
SUM(st.OverTime) % 60 AS OverTimeMinutes
FROM ShiftTime st
GROUP BY st.AccountID
)
-- Now format
SELECT sts.AccountID ,
CAST(sts.RegularHours AS VARCHAR(5)) + ':'
+ RIGHT(CAST(( 100 + sts.RegularMinutes ) AS CHAR(3)), 2) AS RegularTime ,
CAST(sts.OverTimeHours AS VARCHAR(5)) + ':'
+ RIGHT(CAST(( 100 + sts.OverTimeMinutes ) AS CHAR(3)), 2) AS OverTime
FROM ShiftTimeSum sts;

Related

SQL Server - Counting total number of days user had active contracts

I want to count the number of days while user had active contract based on table with start and end dates for each service contract. I want to count the time of any activity, no matter if the customer had 1 or 5 contracts active at same time.
+---------+-------------+------------+------------+
| USER_ID | CONTRACT_ID | START_DATE | END_DATE |
+---------+-------------+------------+------------+
| 1 | 14 | 18.02.2021 | 18.04.2022 |
| 1 | 13 | 02.01.2019 | 02.01.2020 |
| 1 | 12 | 01.01.2018 | 01.01.2019 |
| 1 | 11 | 13.02.2017 | 13.02.2019 |
| 2 | 23 | 19.06.2021 | 18.04.2022 |
| 2 | 22 | 01.07.2019 | 01.07.2020 |
| 2 | 21 | 19.01.2019 | 19.01.2020 |
+---------+-------------+------------+------------+
In result I want a table:
+---------+--------------------+
| USER_ID | DAYS_BEEING_ACTIVE |
+---------+--------------------+
| 1 | 1477 |
| 2 | 832 |
+---------+--------------------+
Where
1477 stands by 1053 (days from 13.02.2017 to 02.01.2020 - user had active contracts during this time) + 424 (days from 18.02.2021 to 18.04.2022)
832 stands by 529 (days from 19.01.2019 to 01.07.2020) + 303 (days from 19.06.2021 to 18.04.2022).
I tried some queries with joins, datediff's, case when conditions but nothing worked. I'll be grateful for any help.
If you don't have a Tally/Numbers table (highly recommended), you can use an ad-hoc tally/numbers table
Example or dbFiddle
Select User_ID
,Days = count(DISTINCT dateadd(DAY,N,Start_Date))
from YourTable A
Join ( Select Top 10000 N=Row_Number() Over (Order By (Select NULL))
From master..spt_values n1, master..spt_values n2
) B
On N<=DateDiff(DAY,Start_Date,End_Date)
Group By User_ID
Results
User_ID Days
1 1477
2 832

T-SQL return individual values instead of cumulative value

I have a 1 table in a db that stored Incoming, Outgoing and Net values for various Account Codes over time. Although there is a date field the sequence of events per Account Code is based on the "Version" number where 0 = original record for each Account Code and it increments by 1 after each change to that Account Code.
The Outgoing and Incoming values are stored in the db as cumulative values rather than the individual transaction value but I am looking for a way to Select * From this table and return the individual amounts as opposed to the cumulative.
Below are test scripts of table and data, and also 2 examples.
If i Select where code = '123' in the test table I currently get this (values are cumulative);
+------+------------+---------+---------+---------+-----+
| Code | Date | Version | Incoming| Outgoing| Net |
+------+------------+---------+---------+---------+-----+
| 123 | 01/01/2018 | 0 | 100 | 0 | 100 |
| 123 | 07/01/2018 | 1 | 150 | 0 | 150 |
| 123 | 09/01/2018 | 2 | 150 | 100 | 50 |
| 123 | 14/01/2018 | 3 | 200 | 100 | 100 |
| 123 | 18/01/2018 | 4 | 200 | 175 | 25 |
| 123 | 23/01/2018 | 5 | 225 | 175 | 50 |
| 123 | 30/01/2018 | 6 | 225 | 225 | 0 |
+------+------------+---------+---------+---------+-----+
This is what I would like to see (each individual transaction);
+------+------------+---------+----------+----------+------+
| Code | Date | Version | Incoming | Outgoing | Net |
+------+------------+---------+----------+----------+------+
| 123 | 01/01/2018 | 0 | 100 | 0 | 100 |
| 123 | 07/01/2018 | 1 | 50 | 0 | 50 |
| 123 | 09/01/2018 | 2 | 0 | 100 | -100 |
| 123 | 14/01/2018 | 3 | 50 | 0 | 50 |
| 123 | 18/01/2018 | 4 | 0 | 75 | -75 |
| 123 | 23/01/2018 | 5 | 25 | 0 | 25 |
| 123 | 30/01/2018 | 6 | 0 | 50 | -50 |
+------+------------+---------+----------+----------+------+
If I had the individual transaction values and wanted to report on the cumulative, I would use an OVER PARTITION BY, but is there an opposite to that?
I am not looking to redesign the create table or the process in which it is stored, I am just looking for a way to report on this from our MI environment.
Note: I've added other random Account Codes into this to emphasis how the data is not ordered by Code or Version, but by Date.
thanks in advance for any help.
USE [tempdb];
IF EXISTS ( SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'Table1'
AND TABLE_SCHEMA = 'dbo')
DROP TABLE [dbo].[Table1];
GO
CREATE TABLE [dbo].[Table1]
(
[Code] CHAR(3)
,[Date] DATE
,[Version] CHAR(3)
,[Incoming] DECIMAL(20,2)
,[Outgoing] DECIMAL(20,2)
,[Net] DECIMAL(20,2)
);
GO
INSERT INTO [dbo].[Table1] VALUES
('123','2018-01-01','0','100','0','100'),
('456','2018-01-02','0','50','0','50'),
('789','2018-01-03','0','0','0','0'),
('456','2018-01-04','1','100','0','100'),
('456','2018-01-05','2','150','0','150'),
('789','2018-01-06','1','50','50','0'),
('123','2018-01-07','1','150','0','150'),
('456','2018-01-08','3','200','0','200'),
('123','2018-01-09','2','150','100','50'),
('789','2018-01-10','2','0','0','0'),
('456','2018-01-11','4','225','0','225'),
('789','2018-01-12','3','75','25','50'),
('987','2018-01-13','0','0','50','-50'),
('123','2018-01-14','3','200','100','100'),
('654','2018-01-15','0','100','0','100'),
('456','2018-01-16','5','250','0','250'),
('987','2018-01-17','1','50','50','0'),
('123','2018-01-18','4','200','175','25'),
('789','2018-01-19','4','100','25','75'),
('987','2018-01-20','2','150','125','25'),
('321','2018-01-21','0','100','0','100'),
('654','2018-01-22','1','0','0','0'),
('123','2018-01-23','5','225','175','50'),
('321','2018-01-24','1','100','50','50'),
('789','2018-01-25','5','100','50','50'),
('987','2018-01-26','3','150','150','0'),
('456','2018-01-27','6','250','250','0'),
('456','2018-01-28','7','270','250','20'),
('321','2018-01-29','2','100','100','0'),
('123','2018-01-30','6','225','225','0'),
('987','2018-01-31','4','175','150','25')
;
GO
SELECT *
FROM [dbo].[Table1]
WHERE [Code] = '123'
GO;
USE [tempdb];
IF EXISTS ( SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'Table1'
AND TABLE_SCHEMA = 'dbo')
DROP TABLE [dbo].[Table1];
GO;
}
Just use lag():
select Evt, Date, Version,
(Loss - lag(Loss, 1, 0) over (partition by evt order by date)) as incoming,
(Rec - lag(Rec, 1, 0) over (partition by evt order by date)) as outgoing,
(Net - lag(Net, 1, 0) over (partition by evt order by date)) as net
from [dbo].[Table1];

Repeating ID based on

I have a very simple requirement but I'm struggling to find a way around this.
I have a very simple query:
SELECT
ServiceCode,
StartDate,
Available,
Nights,
BookingID
FROM #tmpAvailability
LEFT JOIN vwRSBooking B
ON B.Depart = A.StartDate
AND B.ServiceCode = A.SupplierCode
AND B.StatusID IN (2640, 2621)
ORDER BY StartDate;
Made up of 2 tables
#tmpAvailability which consists of the following fields:
SupplierCode
StartDate
Available
vwRSBooking which consists of the following fields
BookingID
DepartDate
Code
Nights
StatusID
Departure and startdate can be joined to link the first day, and the servicecode and suppliercode can be joined to make sure that the availability is linked to the same supplier.
Which produces an output like this:
Code | Dates | Available | Nights | BookingID
TEST | 2018-01-04 | 1 | NULL | NULL
TEST | 2018-01-05 | 1 | NULL | NULL
TEST | 2018-01-06 | 0 | 4 | 123456
TEST | 2018-01-07 | 0 | NULL | NULL
TEST | 2018-01-08 | 0 | NULL | NULL
TEST | 2018-01-09 | 0 | NULL | NULL
TEST | 2018-01-10 | 1 | NULL | NULL
TEST | 2018-01-11 | 1 | NULL | NULL
TEST | 2018-01-12 | 1 | NULL | NULL
TEST | 2018-01-13 | 0 | NULL | 234567
TEST | 2018-01-14 | 0 | NULL | NULL
TEST | 2018-01-15 | 0 | NULL | NULL
What I need is when the BookingID in for 4 days that the bookingID and the nights are spread across those days, for example:
Code | Dates | Available | Nights | BookingID
TEST | 2018-01-04 | 1 | NULL | NULL
TEST | 2018-01-05 | 1 | NULL | NULL
TEST | 2018-01-06 | 0 | 4 | 123456
TEST | 2018-01-07 | 0 | 4 | 123456
TEST | 2018-01-08 | 0 | 4 | 123456
TEST | 2018-01-09 | 0 | 4 | 123456
TEST | 2018-01-10 | 1 | NULL | NULL
TEST | 2018-01-11 | 1 | NULL | NULL
TEST | 2018-01-12 | 1 | NULL | NULL
TEST | 2018-01-13 | 0 | 3 | 234567
TEST | 2018-01-14 | 0 | 3 | 234567
TEST | 2018-01-15 | 0 | 3 | 234567
TEST | 2018-01-16 | 1 | NULL | NULL
If anyone has any ideas on how to solve it would be most appreciated.
Andrew
You could replace your vwRSBooking with another view which uses a CTE to obtain all the dates the booking covers. Then use the view's coverdate for joining to the #tmpAvailability table:
CREATE VIEW vwRSBookingFull
AS
WITH cte ( bookingid, nights, depart, code, coverdate)
AS (SELECT bookingid,
nights,
depart,
code,
depart
FROM vwRSBooking
UNION ALL
SELECT c.bookingid,
c.nights,
c.depart,
c.code,
DATEADD(d, 1, c.coverdate)
FROM cte c
WHERE DATEDIFF(d, c.depart, c.coverdate) < (c.nights - 1))
SELECT c.bookingid,
c.nights,
c.depart,
c.code,
c.coverdate
FROM cte c
GO
You will need a calendar table with all the dates in the date range your dates may fall into. For this example, I build one for January 2018. We can then join onto this table to create the additional rows.
Here is the sample code I used. You can see it at SQL Fiddle.
CREATE TABLE code (
code varchar(max),
dates date,
available int,
nights int,
bookingid int
)
INSERT INTO code VALUES
('TEST','2018-01-04','1',NULL,NULL),
('TEST','2018-01-05','1',NULL,NULL),
('TEST','2018-01-06','0',4,123456),
('TEST','2018-01-07','0',NULL,NULL),
('TEST','2018-01-08','0',NULL,NULL),
('TEST','2018-01-09','0',NULL,NULL),
('TEST','2018-01-10','1',NULL,NULL),
('TEST','2018-01-11','1',NULL,NULL),
('TEST','2018-01-12','1',NULL,NULL),
('TEST','2018-01-13','0',3,234567),
('TEST','2018-01-14','0',NULL,NULL),
('TEST','2018-01-15','0',NULL,NULL)
CREATE TABLE dates (
dates date
)
INSERT INTO dates VALUES
('2018-01-01'),('2018-01-02'),('2018-01-03'),('2018-01-04'),('2018-01-05'),('2018-01-06'),('2018-01-07'),('2018-01-08'),('2018-01-09'),('2018-01-10'),('2018-01-11'),('2018-01-12'),('2018-01-13'),('2018-01-14'),('2018-01-15'),('2018-01-16'),('2018-01-17'),('2018-01-18'),('2018-01-19'),('2018-01-20'),('2018-01-21'),('2018-01-22'),('2018-01-23'),('2018-01-24'),('2018-01-25'),('2018-01-26'),('2018-01-27'),('2018-01-28'),('2018-01-29'),('2018-01-30'),('2018-01-31')
Here is the query based on this dataset:
SELECT
code.code,
dates.dates,
code.available,
code.nights,
code.bookingid
FROM code
LEFT JOIN dates ON
dates.dates >= code.dates
AND dates.dates < DATEADD(DAY,nights,code.dates)
Edit: Here is an example using your initial query as a subquery to join your result set onto the dates table if you want a copy & paste. Still requires creating the dates table.
SELECT
ServiceCode,
StartDate,
Available,
Nights,
BookingID
FROM (
SELECT
ServiceCode,
StartDate,
Available,
Nights,
BookingID
FROM #tmpAvailability
LEFT JOIN vwRSBooking B
ON B.Depart = A.StartDate
AND B.ServiceCode = A.SupplierCode
AND B.StatusID IN (2640, 2621)
) code
LEFT JOIN dates ON
dates.dates >= code.dates
AND dates.dates < DATEADD(DAY,nights,code.dates)
ORDER BY StartDate;

Union in outer query

I'm attempting to combine multiple rows using a UNION but I need to pull in additional data as well. My thought was to use a UNION in the outer query but I can't seem to make it work. Or am I going about this all wrong?
The data I have is like this:
+------+------+-------+---------+---------+
| ID | Time | Total | Weekday | Weekend |
+------+------+-------+---------+---------+
| 1001 | AM | 5 | 5 | 0 |
| 1001 | AM | 2 | 0 | 2 |
| 1001 | AM | 4 | 1 | 3 |
| 1001 | AM | 5 | 3 | 2 |
| 1001 | PM | 5 | 3 | 2 |
| 1001 | PM | 5 | 5 | 0 |
| 1002 | PM | 4 | 2 | 2 |
| 1002 | PM | 3 | 3 | 0 |
| 1002 | PM | 1 | 0 | 1 |
+------+------+-------+---------+---------+
What I want to see is like this:
+------+---------+------+-------+
| ID | DayType | Time | Tasks |
+------+---------+------+-------+
| 1001 | Weekday | AM | 9 |
| 1001 | Weekend | AM | 7 |
| 1001 | Weekday | PM | 8 |
| 1001 | Weekend | PM | 2 |
| 1002 | Weekday | PM | 5 |
| 1002 | Weekend | PM | 3 |
+------+---------+------+-------+
The closest I've come so far is using UNION statement like the following:
SELECT * FROM
(
SELECT Weekday, 'Weekday' as 'DayType' FROM t1
UNION
SELECT Weekend, 'Weekend' as 'DayType' FROM t1
) AS X
Which results in something like the following:
+---------+---------+
| Weekday | DayType |
+---------+---------+
| 2 | Weekend |
| 0 | Weekday |
| 2 | Weekday |
| 0 | Weekend |
| 10 | Weekday |
+---------+---------+
I don't see any rhyme or reason as to what the numbers are under the 'Weekday' column, I suspect they're being grouped somehow. And of course there are several other columns missing, but since I can't put a large scope in the outer query with this as inner one, I can't figure out how to pull those in. Help is greatly appreciated.
It looks like you want to union all a pair of aggregation queries that use sum() and group by id, time, one for Weekday and one for Weekend:
select Id, DayType = 'Weekend', [time], Tasks=sum(Weekend)
from t
group by id, [time]
union all
select Id, DayType = 'Weekday', [time], Tasks=sum(Weekday)
from t
group by id, [time]
Try with this
select ID, 'Weekday' as DayType, Time, sum(Weekday)
from t1
group by ID, Time
union all
select ID, 'Weekend', Time, sum(Weekend)
from t1
group by ID, Time
order by order by 1, 3, 2
Not tested, but it should do the trick. It may require 2 proc sql steps for the calculation, one for summing and one for the case when statements. If you have extra lines, just use a max statement and group by ID, Time, type_day.
Proc sql; create table want as select ID, Time,
sum(weekday) as weekdayTask,
sum(weekend) as weekendTask,
case when calculated weekdaytask>0 then weekdaytask
when calculated weekendtask>0 then weekendtask else .
end as Task,
case when calculated weekdaytask>0 then "Weekday"
when calculated weekendtask>0 then "Weekend"
end as Day_Type
from have
group by ID, Time
;quit;
Proc sql; create table want2 as select ID, Time, Day_Type, Task
from want
;quit;

Creating an Evaluation Query for 3 months, 6 months and 12 months

I am in need of getting correct query for gathering records based on their hire_date and their evaluations that occurs with these parameters:
3 months
6 months
12 months
from system_date.
The way the report will work is collecting employees that have evaluations due dates between 30 days past and 90 days after the current system_date.
Here is the tricky part: If the hire_date is greater than 1 year from system_date than only 12 month evaluations will exist. If their current assignment changes, as in their job, they revert back to 3, 6 and 12 months evaluations, but will ultimately end up being back in 12 months evaluations.
Let me know if you need more clarification.
Flow chart:
As I don't know your tables or which database you are using, I've created the following sample solution using MS SQL Server. I've declared variables, a CTE (common table expression) and created a function for readability, but you can easily substitute them.
SQL Fiddle demo
First, I've considered that you have a simple Employees table with the hire_date and last_assignment_change.
I've inserted some sample data for testing:
Employees
| ID | NAME | HIRE_DATE | LAST_ASSIGNMENT_CHANGE |
|----|-------|------------|------------------------|
| 1 | Alice | 2012-07-15 | (null) |
| 2 | Bob | 2013-12-02 | (null) |
| 3 | Carol | 2014-01-13 | (null) |
| 4 | Dave | 2014-01-13 | 2014-08-28 |
| 5 | Eve | 2014-03-15 | (null) |
| 6 | Frank | 2014-06-15 | (null) |
| 7 | Gina | 2014-09-15 | (null) |
So, we have:
Today = 2014-09-25
Filter dates between 2014-08-25 and 2014-12-25
Alice: next evaluation = 2014-07-15 -> do not show
Bob: next evaluation = 2014-12-02 -> show
Carol: next evaluation = 2015-01-13 -> do not show
Dave: next evaluation = 2014-11-28 -> show
Eve: next evaluation = 2014-09-15 -> show
Frank: next evaluation = 2014-09-15 -> show
Gina: next evaluation = 2014-12-15 -> show
Next, I've created a function to simplify the CAST to DATE used in end of the query:
CREATE FUNCTION dbo.fn_CastDate
( #year int, #month int, #day int )
RETURNS date
AS
BEGIN
RETURN
CAST(
CAST(#year AS VARCHAR(4)) +
RIGHT('0' + CAST(#month AS VARCHAR(2)), 2) +
RIGHT('0' + CAST(#day AS VARCHAR(2)), 2)
AS DATE)
END
GO
Finally, the query to filter by the evaluation dates:
BEGIN
DECLARE
#today date,
#last_month date,
#next_three_months date
SET #today = CAST(GETDATE() AS DATE)
SET #last_month = DATEADD(month, -1, #today)
SET #next_three_months = DATEADD(month, 3, #today)
;WITH employees_cte AS
(
SELECT
id,
name,
COALESCE(last_assignment_change, hire_date) AS assignment_date -- if last_assignment_change is null, use hire_date
FROM
employees
)
SELECT
*
FROM
employees_cte
WHERE
DATEADD(month, 3, assignment_date) BETWEEN #last_month AND #next_three_months -- Evaluation = 3 months
OR
DATEADD(month, 6, assignment_date) BETWEEN #last_month AND #next_three_months -- Evaluation = 6 months
OR
DATEADD(month, 12, assignment_date) BETWEEN #last_month AND #next_three_months -- Evaluation = 12 months
OR
(DATEDIFF(day, assignment_date, #next_three_months) < 365 -- Evaluation = annual
AND
(dbo.fn_CastDate(YEAR(#last_month), MONTH(assignment_date), DAY(assignment_date)) -- test with YEAR(#last_month)
BETWEEN #last_month AND #next_three_months
OR
dbo.fn_CastDate(YEAR(#next_three_months), MONTH(assignment_date), DAY(assignment_date)) -- test with YEAR(#next_three_months)
BETWEEN #last_month AND #next_three_months)
)
END
Output:
| ID | NAME | ASSIGNMENT_DATE |
|----|-------|-----------------|
| 2 | Bob | 2013-12-02 |
| 4 | Dave | 2014-08-28 |
| 5 | Eve | 2014-03-15 |
| 6 | Frank | 2014-06-15 |
| 7 | Gina | 2014-09-15 |
Note: the function CastDate will fail if you try to convert a date like 2012-02-29 (Leap Year) to 2014-02-29 (not a Leap Year). To solve this, convert February 29 to February 28 before using the the CAST.
NB: I have assumed Oracle is the dbms given you are referring to sysdate.
It would be simpler for all concerned if there was a provided set of sample data and expected result.
However, using this data (derived from Zanon's hope you don't mind):
| ID | NAME | HIRE_DATE | ISCONTRACTOR | TERM_DATE |
|----|-------|------------|--------------|-----------|
| 1 | Alice | 2012-07-15 | 0 | (null) |
| 2 | Bob | 2013-12-02 | 0 | (null) |
| 3 | Carol | 2014-01-13 | 1 | (null) |
| 4 | Dave | 2014-01-13 | 0 | (null) |
| 5 | Eve | 2014-03-15 | 0 | (null) |
| 6 | Frank | 2014-06-15 | 0 | (null) |
| 7 | Gina | 2014-09-15 | 0 | (null) |
and this query:
select
id
, name
, to_char(hire_date,'yyyy-mm-dd') hire_date
, to_char(anniversary,'yyyy-mm-dd') anniversary
, case when evaldue = 3 then to_char(add_months(hire_date,3) ,'yyyy-mm-dd')
when evaldue = 6 then to_char(add_months(hire_date,6) ,'yyyy-mm-dd')
when evaldue = 9 then to_char(add_months(hire_date,9) ,'yyyy-mm-dd')
when evaldue = 12 then to_char(anniversary,'yyyy-mm-dd')
end evaldue_date
, evaldue
FROM (
select
employees.*
, case
when add_months(hire_date,3) > trunc(sysdate) then 3
when add_months(hire_date,6) > trunc(sysdate) then 6
when add_months(hire_date,9) > trunc(sysdate) then 9
else 12
end evaldue
, to_date(to_char(sysdate,'YYYY') || to_char(hire_date,'MMDD'),'YYYYMMDD') anniversary
from employees
where iscontractor = 0 -- i.e. only for employees
and term_date IS NULL
)
WHERE
case when evaldue = 3 then add_months(hire_date,3)
when evaldue = 6 then add_months(hire_date,6)
when evaldue = 9 then add_months(hire_date,9)
when evaldue = 12 then anniversary
end between trunc(sysdate) - 30 and trunc(sysdate) + 90
ORDER BY
evaldue_date ASC
, evaldue ASC
;
This is the calculated result:
| ID | NAME | HIRE_DATE | ANNIVERSARY | EVALDUE_DATE | EVALDUE |
|----|-------|------------|-------------|--------------|---------|
| 4 | Dave | 2014-01-13 | 2014-01-13 | 2014-10-13 | 9 |
| 2 | Bob | 2013-12-02 | 2014-12-02 | 2014-12-02 | 12 |
| 7 | Gina | 2014-09-15 | 2014-09-15 | 2014-12-15 | 3 |
| 6 | Frank | 2014-06-15 | 2014-06-15 | 2014-12-15 | 6 |
| 5 | Eve | 2014-03-15 | 2014-03-15 | 2014-12-15 | 9 |
based on hire_date an "evaldue" of 3,6 or 9 is calculated (i.e. which
performance review is needed) if the hire_date is beyond 9 months the
evaldue is 12.
The anniversary of hire_date is also calculated for later reference
using those prior calculations an evaldue_date is calculated
then that calculated evaldue_date is compared to sysdate - 30 to sysdate + 90
See this demonstrated at SQLFiddle