How can I get the absence days of all employee? SQL Server - sql

How can I get the absence days and the present days of all employees , I would like an output like this:
employeeId DATE Status
1 2021/03/04 Absent
1 2021/03/05 Present
2 2021/03/04 Present
2 2021/03/05 Present
What I can get now only the present days :
select distinct DATEPART(dw,er.AddAt) as dayId,
DATENAME(dw,er.AddAt) as dayname,
DATEPART(DAY,er.AddAt) as monthday,
er.employeeId,firstName as Name
from records er,employee
where er.employeeId=employee.employeeId
Update:
After Testing the solution of #Gordon Linoff , that's what I got:
and I have two problems :
I have duplicate data ( 3 times duplicate )
When I select a other month than February , I got no data.
select Day, WeekdayName, e.employeeid,
(case when r.employeeid is not null then 'present' else 'absent' end)
from Get_Calendar_Date(DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-1, 0) ,DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-1, -1)) c cross join
employee e left join
records r
on c.Day = DATEPART(DAY,r.AddAt) and e.employeeid = r.employeeid
where Month=1

Let me assume that you have a calendar table and a table of all employees. The idea is then to do a cross join to generate the rows and a left join to bring in the existing data:
select c.dayid, c.dayname, e.employeeid,
(case when r.employeeid is not null then 'present' else 'absent' end)
from calendar c cross join
employees e left join
records r
on c.dayid = r.dayid and e.employeeid = r.employeeid;
I assume you have a calendar table because you have a column called dayid. If you don't have one, you can construct one using a numbers table, a recursive CTE, a subquery on records or some other method.
You can probably just use existing dates for the calendar:
from (select distinct dayid from records) d

Related

How to join two tables with date and week number?

How do i connect both these tables to have data connected based on department names and week number. So that if the weeknumber don't match up, it shows nulls for others.
http://sqlfiddle.com/#!6/3bbd3/1
A full outer join should do the trick:
SELECT d.id,
d.name,
f.id,
DATEPART(WEEK, setupdate) as WeekNumber
FROM departments d
FULL OUTER JOIN forecast f ON d.name = f.name AND
DATEPART(WEEK, setupdate) = DATEPART(WEEK, forecast)

SQL to show date for Null value based on query

I have two tables, employee and employee time entry. I have run a query that is showing me all employees with the sum of time entered or 0 as null value. In the next column I have week number. If employee has not entered time during the week than it is giving me 0 but it is also giving me null value at the week number. how can I force query to show me week number, assuming no entry was made by employee.
Select
Concat(Empfname,Emplname) as EmployeeName,
department,
iif (sum(whours) is null, 0, sum(whours)) CurrentHours,
Datepart (ww,wdate) WeekNum
From
employee as e
left outer join
TimeEntry as w on e.id = w.eId
and wdate between '01/01/2017' and '01/31/2017'
group by
Concat(Empfname,Emplname), department, Datepart(ww, wdate)
Output
EmployeeName Department CurrentHours WeekNum
------------------------------------------------
John Smith Sales 8 1
Smith John Operations 0 Null
How can I tell it is also from WeekNum 1?
Thanks
The idea is to generate all rows using cross join and then use left join to bring in the rows you want something like this:
Select Concat(e.Empfname, e.Emplname) as EmployeeName, e.department,
coalesce(sum(whours), 0) as CurrentHours
datepart(week, wd.wdate) as WeekNum
from employee e cross join
(select distinct wdate from TimeEntry) wd left outer join
TimeEntry tw
on e.id = w.eId and tw.wdate = wd.wdate
where wd.wdate between '2017-01-01' and '2017-01-31'
group by Concat(e.Empfname, e.Emplname), e.department, Datepart(week, wd.wdate);
Try this:
Select Concat(Empfname,Emplname) as EmployeeName, department
iif (sum(whours) is null, 0, sum(whours)) CurrentHours
ISNULL(Datepart (ww,wdate),1) WeekNum
From employee as e left outer join TimeEntry as w on e.id=w.eId
and wdate between '01/01/2017' and '01/31/2017'
group by Concat(Empfname,Emplname), department, ISNULL(Datepart (ww,wdate),1)
which will force any NULL value to show 1 instead of NULL itself

Group by Month, return 0 if no record found

I want to fetch records from database table for last 12 months. Here is what I tried so far.
SELECT COUNT(s.id), date_part('month', s.viewed_at) month_number
FROM statistics_maps_view as s
INNER JOIN maps as m
ON s.maps_id=m.id Where m.users_id = $users_id group by month_number ORDER BY month_number DESC LIMIT 12
I know It'll group the records month wise. but is there a way to add Count = 0 if there is no record for a particular month?
The group by clause will not create entries where there's no data, as you've seen. What you could do is left join this entire result with another result set that has all the entries you want - e.g., one you dynamically generate with generate_series:
SELECT generate_series AS month_number, cnt
FROM GENERATE_SERIES(1,12) g
LEFT JOIN (SELECT COUNT(s.id) AS cnt,
DATE_PART('month', s.viewed_at) AS month_number
FROM statistics_maps_view s
INNER JOIN maps m ON s.maps_id = m.id
WHERE m.users_id = $users_id
GROUP BY month_number) s ON g.generate_series = s.month_number
ORDER BY 1 ASC

Query based on Count, Time frame, and location

Ok so I need to write a query that I am probably making much more complicated than it needs to be but I could use some help.
I need to select records of clients that have not been seen for a year or longer, have seen us more than once but can be only once if it is not at certain locations.
So what I have so far is:
WITH CTE AS
(
SELECT
client_id,
location_id,
employee_id,
create_timestamp,
ROW_NUMBER() OVER(PARTITION BY person_id ORDER BY create_timestamp DESC) AS ROW
FROM
client_Appointment
)
SELECT
c.client_id,
COUNT(*)
FROM
CTE AS ce
INNER JOIN person AS c
ON p.person_id= ce.client_id
INNER JOIN employee_mstr AS em
ON em.employee_id = ce.empoyee_id
INNER JOIN location_mstr AS lm
ON lm.location_id = ce.location_id
WHERE
ce.create_timestamp <= CONVERT(VARCHAR(10), DATEADD(Year,-1,GETDATE()), 120)
GROUP BY
p.person_id
HAVING
COUNT(*) > 1
I'm unsure where to go from here. Also this does not get me all the info I need and if I add that information to the select clause I have to use it in group by which means I don't get all the needed records.
Thanks
So you want only clients who have not been seen in a year or more,
then clients that have either one visit NOT at certain locations OR more than one visit. Did I get that right?
Note: Just replace (VALUES(1),(2),(3)) with your table name
WITH CTE_visits
AS
(
SELECT
c.client_id,
COUNT(*) AS total_visits,
SUM(
CASE
WHEN ce.location_id IN (SELECT ID FROM (VALUES(1),(2),(3)) AS A(ID)) THEN 0 --so when it is a certain location then do NOT count it
ELSE 1 --if it is not at the certain locations, then count it
END
) AS visits_not_at_certain_locations
FROM
client_Appointment AS ce
INNER JOIN person AS c
ON p.person_id= ce.client_id
INNER JOIN employee_mstr AS em
ON em.employee_id = ce.empoyee_id
INNER JOIN location_mstr AS lm
ON lm.location_id = ce.location_id
CROSS APPLY(SELECT client_id, MAX(create_timestamp) last_visit FROM client_Appointment WHERE client_id = ce.client_id GROUP BY client_id) CA --find most recent visit for each client_id
WHERE
ce.create_timestamp <= CONVERT(VARCHAR(10), DATEADD(Year,-1,GETDATE()), 120) --remember this only counts visits over a year ago
AND last_visit <= CONVERT(VARCHAR(10), DATEADD(Year,-1,GETDATE()), 120) --says only return client_id's who's last visit is more than a year ago
GROUP BY
p.person_id
)
SELECT *
FROM CTE_visits
WHERE visits_not_at_certain_locations = 1 --seen once NOT at certain locations
OR total_visits > 1 --seen more than once at any location

Write a SQL Query to replace values and include all the Dates

Well I have this -
Table DimDate- Date
Table Employee- Id,Name,Points,Date
Now the Employee table has points for everyday unless they did not come...so the Date does not have all the Dates entries... I mean for e.g in a week he did not come for 2 days the Employee table has only 5 rows...so I have this dimdate table which has all the dates till 2050 which I want to join with and add Zeros for the dates he does not have points. So I have written this query but does not work -
Select E.EmployeeId,D.Date,isNull(E.Points,0) from DimDate D left join Employee E on D.Date between '01-01-2009'and '06-01-2009' where E.EmployeeId=1
The above query give multiple dates and I tried group by on Date but does not work.
You probably dont want to join the two tables on a date range but a date. Then filter the record set by the date range. example
Select
E.EmployeeId,
D.Date,
isNull(E.Points,0)
from DimDate D
left join Employee E on D.Date = E.Date
where E.EmployeeId=1
AND D.Date Between '01-01-2009'and '06-01-2009'
Edited:
Select
E.EmployeeId,
D.Date,
isNull(E.Points,0)
from DimDate D
left join Employee E on D.Date = E.Date And E.EmployeeId=1
where D.Date Between '01-01-2009'and '06-01-2009'
OR
Select
E.EmployeeId,
D.Date,
isNull(E.Points,0)
from DimDate D
left join Employee E on D.Date = E.Date
where (E.EmployeeId = 1 OR E.EmployeeId is NULL)
AND D.Date Between '01-01-2009'and '06-01-2009'
I think you need a cross join between the dimdates table and the table where your employees are defined. This will give you a list of records with all employee/date combinations. Then the result of that needs to be left outer joined to the table that has the employee points records.
Something like:
Select CJ.EmployeeId,CJ.Date,isNull(E.Points,0)
from (SELECT EmployeeID, D.Date
from DimDate D CROSS JOIN [EmployeeDefinitionTable] as edt) as CJ
left outer join Employee E on CJ.Date =E.Date AND CJ.EmployeeId = E.EmployeeId
where CJ.Date between '01-01-2009'and '06-01-2009'
and E.EmployeeId = 1
Where EmployeeDefinitionTable is a table that uniquely lists all employees (or at least their id's for this problem statement).
This also captures employees with no points entries.
The between statement and/or EmployeeId filtering could be moved up into the cross join if it fits your requirements. It would make the cross join more efficient.