Query who looks if employee has more than 14 vacation days - sql

I want a query that checks if a employee has more than 14 vacation days in a year. I want to make a trigger of it. It is important that this is 14 days in total. But the employee could have 4 days in one month and 10 days in another. He don't need to take the days in one go.
I have something like this (query) I'm using SQL Server
select employeeid
from time
where datediff(day, dateStart, dateEnd) >=1
and year(dateEnd) = 2018
and timecat= 'vacationdays'
group by employeeid
having count(*) >=13
I thought I could use a datediff and have this count like 13 times. But it doesn't work.

You need to sum the date differences:
select employeeid, sum(datediff(day, dateStart, dateEnd) + 1) AS total
from time
where year(dateEnd) = 2018
and timecat= 'vacationdays'
group by employeeid
having SUM(datediff(day, dateStart, dateEnd) + 1) > 14
Consider the +1 in SUM because datediff returns 1 for two dates like '2018-11-16' and '2018-11-17', but if these are the dateStart and dateEnd you want the result to be 2.
There is still one problem remaining: what happens if dateStart and dateEnd are not dates of the same year!

Count(*) just counts the number of rows.
This means if have an employee with two vacations one of 10 days and one of 4 days, (i assume that) this are only two rows in rows in your table and count star returns 2.
You can you the function SUM()
select employeeid, sum( datediff(day, dateStart, dateEnd) ) as sum
from time
where datediff(day, dateStart, dateEnd) >=1
and year(dateEnd) = 2018
and timecat= 'vacationdays'
group by employeeid
having sum >=13
I don't know the structure of your table, therefore I'm not quite sure if the query is correct, but here a further helpful links.
https://www.w3schools.com/sql/sql_count_avg_sum.asp
Getting the sum of a datediff result

Related

T-sql count number of times a week on rows with date interval

If you have table like this:
Name
Data type
UserID
INT
StartDate
DATETIME
EndDate
DATETIME
With data like this:
UserID
StartDate
EndDate
21
2021-01-02 00:00:00
2021-01-02 23:59:59
21
2021-01-03 00:00:00
2021-01-04 15:42:00
24
2021-01-02 00:00:00
2021-01-06 23:59:59
And you want to calculate number of users that is represented on each day in a week with a result like this:
Year
Week
NumberOfTimes
2021
1
8
2021
2
10
2021
3
4
Basically I want to to a Select like this:
SELECT YEAR(dateColumn) AS yearname, WEEK(dateColumn)as week name, COUNT(somecolumen)
GROUP BY YEAR(dateColumn) WEEK(dateColumn)
The problem I have is the start and end date if the date goes over several days I want it to counted each day. Preferably I don't want the same user counted twice each day. There are millions of rows that are constantly being deleted and added so speed is key.
The database is MS-SQL 2019
I would suggest a recursive CTE:
with cte as (
select userid, startdate, enddate
from t
union all
select userid, startdate,
enddate
from cte
where startdate < enddate and
week(startdate) <> week(enddate)
)
select year(startdate), week(startdate), count(*)
from cte
group by year(startdate), week(startdate)
option (maxrecursion 0);
The CTE expands the data by adding 7 days to each row. This should be one day per week.
There is a little logic in the second part to handle the situation where the enddate ends in the same week as the last start date. The above solution assumes that the dates are all in the same year -- which seems quite reasonable given the sample data. There are other ways to prevent this problem.
You need to cross-join each row with the relevant dates.
Create a calendar table with columns of years and weeks, include a start and end date of the week. See here for an example of how to create one, and make sure you index those columns.
Then you can cross-join like this
SELECT
YEAR(dateColumn) AS yearname,
WEEK(dateColumn)as weekname,
COUNT(somecolumen)
FROM Table t
JOIN CalendarWeek c ON c.StartDate >= t.StartDate AND c.EndDate <= t.EndDate
GROUP BY YEAR(dateColumn), WEEK(dateColumn)

SQL Server / SSRS: Calculating monthly average based on grouping and historical values

I need to calculate an average based on historical data for a graph in SSRS:
Current Month
Previous Month
2 Months ago
6 Months ago
This query returns the average for each month:
SELECT
avg_val1, month, year
FROM
(SELECT
(sum_val1 / count) as avg_val1, month, year
FROM
(SELECT
SUM(val1) AS sum_val1, SUM(count) AS count, month, year
FROM
(SELECT
COUNT(val1) AS count, SUM(val1) AS val1,
MONTH([SnapshotDate]) AS month,
YEAR([SnapshotDate]) AS year
FROM
[DC].[dbo].[KPI_Values]
WHERE
[SnapshotKey] = 'Some text here'
AND No = '001'
AND Channel = '999'
GROUP BY
[SnapshotDate]) AS sub3
GROUP BY
month, year, count) AS sub2
GROUP BY sum_val1, count, month, year) AS sub1
ORDER BY
year, month ASC
When I add the following WHERE clause I get the average for March (2 months ago):
WHERE month = MONTH(GETDATE())-2
AND year = YEAR(GETDATE())
Now the problem is when I want to retrieve data from 6 months ago; MONTH(GETDATE()) - 6 will output -1 instead of 12. I also have an issue with the fact that the year changes to 2016 and I am a bit unsure of how to implement the logic in my query.
I think I might be going about this wrong... Any suggestions?
Subtract the months from the date using the DATEADD function before you do your comparison. Ex:
WHERE SnapshotDate BETWEEN DATEADD(month, -6, GETDATE()) AND GETDATE()
MONTH(GETDATE()) returns an int so you can go to 0 or negative values. you need a user scalar function managing this, adding 12 when <= 0

SQL - returning average number of items by days of the week.

The query I have below works fine and returns the items shipped, total days, and average number of items per day.
SELECT A.ItemsShipped, A.TotalDays, cast(A.ItemsShipped as float)/cast(A.TotalDays as float) AS
'Averange items shipped per day'
FROM(SELECT
(SELECT Count(*)
FROM PARTNER_WORKORDER
WHERE statusNo = 110)
AS ItemsShipped,
(SELECT DATEDIFF(day, MIN(orderDt),'2013-11-20 00:00:00.000')
From PARTNER_WORKORDER)
AS TotalDays
)A
The question I have now is, how can I get this return the average number of items shipped per each day of the week (average on Monday, average on Tuesday, etc...) so that it ends up returning seven results. I know there is the way of doing a query for each day, but what would be the efficient way of doing it all in one query?
I am using MS SQLServer for this project.
Thank for any help anyone can give.
This should solve your problem. I don't have SQL Server instance at hand (so can't validate it), but I guess you get the idea.
WITH OrdersPerDay AS (
SELECT
CAST(orderDt as INTEGER) as OrderDay,
COUNT(1) as OrderCount
FROM PARTNER_WORKORDER
GROUP BY CAST(orderDt as INTEGER)
WHERE statusNo = 110
)
SELECT
DATEPART(dw, CAST(OrderDay as DATETIME)) as WeekDay,
AVG(OrderCount)
FROM OrdersPerDay
GROUP BY DATEPART(dw, CAST(OrderDay as DATETIME))

Get name of person having activity in every month - Oracle SQL

I have log table where there is are records with user id and the date for a specific activity done. I want to get names of users having activity every month. I am using the following query
select distinct(employeeid) from transactions
where eventdate between '01-OCT-13' AND '23-OCT-13'
and eventdate between '01-SEP-13' AND '01-OCT-13'
and eventdate between '01-AUG-13' AND '01-SEP-13'
and eventdate between '01-JUL-13' AND '01-AUG-13';
But this is doesn't work. Can someone please suggest any improvement?
Edit:
Since my questions seems to be a little confusing, here is an example
EmployeeID | Timestamp
a | 01-Jul-13
b | 01-Jul-13
a | 01-Aug-13
c | 01-Aug-13
a | 01-Sep-13
d | 01-Sep-13
a | 01-Oct-13
a | 01-Oct-13
In the above table, we can see that employee "a" has activity in all the months from July till October. So I want to find a list of all such employees.
You can use COUNT as analytical function and get the number of months for each employee and total number of months. Then select only those employees where both counts match.
select distinct employeeid
from (
select employeeid,
count(distinct trunc(eventdate,'month')) --count of months for each employee
over (partition by employeeid) as emp_months,
count(distinct trunc(eventdate,'month')) --count of all months
over () as all_months,
from transactions
)
where emp_months = all_months;
Wish I could give you the code, but i'm in a bit of a hurry, so this is more of a suggestion.
Have you tried extracting the distinct months (from eventdate), for every user, and if that has 10 rows (assuming it is October, you could dynamically change this), then the employee must of had an event every month.
By very inefficient, I think you mean it doesn't work. The same value can't be both in september, in october, etc.
Anyway, using #LaggKing suggestion, you could try this query:
SELECT employeeid
FROM (
SELECT DISTINCT employeeid, MONTH(eventdate)
FROM transactions
)
HAVING COUNT(*) = MONTH(NOW())
EDIT: You need to take year into account.
SELECT employeeid
FROM (
SELECT DISTINCT employeeid, MONTH(eventdate)
FROM transactions
WHERE YEAR(eventdate) = YEAR(NOW())
)
HAVING COUNT(*) = MONTH(NOW())

How to handle NULL in a SQL subquery?

I'm pulling my hair out over such a simple thing...
I'm recording the number of days a member attends a gym club. By default, I assume the member attends every day. When they are sick, I record the dates and total number of days absent in a table (ie DateFrom, DateEnd, TotalDays). The total days absent is the difference between DateFrom and DateEnd.
Now sometimes I don't know when the member is coming back to gym. Just that they have stopped attending on a certain day. Hence the DateEnd and TotalDays are unknown. So the total number days are calculated by taking the difference between DateFrom and today's date.
Table: InactiveOnProgram
Columns: PersonId, DateFrom, DateEnd, TotalDays
Data:
1,01/01/2012,05/01/2012,5
1,05/01/2012,08/01/2012,3
2,01/02/2012,05/02/2012,5
2,05/02/2012,08/02/2012,3
2,20/02/2012,null,null
My below query works fine for personId=2. The total days absent is 8+2=10 days (2 days being 20/02/2012 till 22/02/2012 = today ). But for personId=1, it returns null , instead of 8 days!
sql:
(SELECT
case ( isnull(sum(TotalDays), 0) )
when 0 then 0
else CAST(SUM(TotalDays) as DECIMAL(20,2))
end
FROM InactiveOnProgram
)
+
(SELECT
case ( isnull( DateFrom, 0) )
when null then 0
when 0 then 0
else CAST(datediff(day,DateFrom, getdate()) as DECIMAL(20,2))
end
FROM InactiveOnProgram
WHERE (TotalDays is null or TotalDays =0)
AND DateTo is null
)
Any idea what I'm missing here?! As far as I can guess the second part of sql returns null and because of this it ignores the first part!
Any help is much appreciated.
Thanks
You can write it as a single query:
declare #InactiveOnProgram table
(PersonId int, DateFrom datetime, DateEnd datetime, TotalDays int)
insert into #InactiveOnProgram (PersonId , DateFrom , DateEnd , TotalDays)
select 1,'20120101','20120105',5 union all
select 1,'20120105','20120108',3 union all
select 2,'20120201','20120205',5 union all
select 2,'20120205','20120208',3 union all
select 2,'20120220',null,null
select PersonId,SUM(COALESCE(TotalDays,DATEDIFF(day,DateFrom,CURRENT_TIMESTAMP)))
from #InactiveOnProgram group by PersonId
I'm not really happy with the storing of TotalDays, but given your data set, it seems necessary, since apparently, from 1st - 5th = 5 days, but from 5th - 8th = 3 days.
Do you only guess that second part returns null or do you know that? Because as far as I can see, first part is returning something undefined.
You need to use SUM() and ISNULL() in different order, like:
select cast(sum(isnull(TotalDays, 0)) as decimal(20,2)) as totdays
And in second case you can use next:
datediff(day, isnull(DateFrom, getdate()), getdate())
This way you can eliminate null values before calculation/conversion.
Maybe this is your solution:
select personid, sum( closed + unclosed)
from (
SELECT personid
, CAST(SUM(isnull(nullif(TotalDays,0),0)) as DECIMAL(20,2)) as closed
, case when min(isnull(nullif(DateFrom,0),0))=0 OR (SUM(isnull(nullif(TotalDays,0),0)) >0 AND min(isnull(nullif(dateend,0),0)) >0) then 0 else min(CAST(datediff(day,DateFrom, getdate()) as DECIMAL(20,2))) end as unclosed
FROM test
group by personid
--WITH ROLLUP
) as test
group by personid
WITH ROLLUP
The problem is basically that in SQL a null term in a calculation results in a null.
Make sure null is not possible in your results.
BTW, your logic is waaaaay too complicated - simplify it