Multiple case with different table - sql

I have a query like this:
select U.Name,U.Adluserid as 'Empid',
min(case when IOType=0 then Edatetime end) as 'IN',
max(case when IOType=1 then Edatetime end) as 'out'
from Mx_ACSEventTrn
inner join Mx_UserMst U on Mx_ACSEventTrn.UsrRefcode=U.UserID
left join Tbl_Zeo_Empstatus E on Mx_ACSEventTrn.UsrRefcode=E.Emp_Id
where cast(Edatetime as date) between '2019-10-30' and '2019-10-30'
group by U.Name,U.Adluserid
output
Name Empid IN OUT status
JAS 505 2019-10-30 08:06:37.000 2019-10-30 14:13:29.000 Present
SAAJ 516 2019-10-30 08:05:11.000 2019-10-30 14:17:58.000 Absent
ram 516 2019-10-30 08:20:11.000 2019-10-30 14:17:58.000 Late
I have a another table like this: Tbl_Zeo_Empstatus
Emp_Id Status
123 2
504 2
505 3
I want to show the status column depend on this condition. If IN time
is not null then check the time more than 8.15 AM. If it is more than
8.15 AM then show late other wise show present.
IF in time is null then check the status of employee from this
table "Tbl_Zeo_Empstatus", if status 2 then show "Absent" if status 3
then show "Vacation" like this.
How I can achieve this?

What you need is an additional query level (sub-select) to be able to operate on in and out time which are aggregates (min and max). To be able to join your data with Tbl_Zeo_Empstatus we need to provide outer query with u.UserID and then perform a left join based on that column.
Final query just needs a CASE expression to evaluate your conditions and set expected status based on in time column.
select
t.Name,
t.Empid,
t.t_in,
t.t_out,
case
when t.t_in is null and E.status = 2 then 'Absent'
when t.t_in is null and E.status = 3 then 'Vacation'
when cast(t.t_in as time) > '08:15' then 'Late' else 'Present'
end as status
from (
select
u.UserID,
u.Name,
u.Adluserid as empid,
min(case when IOType=0 then Edatetime end) as t_in,
max(case when IOType=1 then Edatetime end) as t_out,
from
Mx_ACSEventTrn t
inner join Mx_UserMst u on t.UsrRefcode = u.UserID
where
cast(Edatetime as date) between '2019-10-30' and '2019-10-30'
group by
u.UserId, u.Name, u.Adluserid
) t
left join Tbl_Zeo_Empstatus e on t.UserID = e.Emp_Id

try to avoid inner select like this
select
u.UserID,
u.Name,
u.Adluserid as empid,
min(case when IOType=0 then Edatetime end) as t_in,
max(case when IOType=1 then Edatetime end) as t_out
into #tmp
from
Mx_ACSEventTrn t
inner join Mx_UserMst u on t.UsrRefcode = u.UserID
where
cast(Edatetime as date) between '2019-10-30' and '2019-10-30'
group by
u.UserId, u.Name, u.Adluserid
select
t.Name,
t.Empid,
t.t_in,
t.t_out,
case
when t.t_in is null and E.status = 2 then 'Absent'
when t.t_in is null and E.status = 3 then 'Vacation'
when cast(t.t_in as time) > '08:15' then 'Late' else 'Present'
end as status
from
#tmp t
left join Tbl_Zeo_Empstatus e on t.UserID = e.Emp_Id

Related

SQL Counting Using Group By With Multiple Tables and Joins

I have 3 tables (Schools, Students, Activities) such as simply like these:
School
Student
Activity
I want to get the list for each school the ALL activities and number of participants according to the gender in the 2020 ONLY like this table:
I did a query like this but it didn't worked as I wanted:
SELECT Sc.SchoolName, A.ActivityName, Sc.Year COUNT(Gender)
FROM School Sc
JOIN Student S ON Sc.SchoolID=S.SchoolID
JOIN Activity A ON S.ActivityID=A.ActivityID
GROUP BY Gender
How can I fix this? Can you give me solution?
You want conditional aggregation:
SELECT
Sc.SchoolName,
A.ActivityName,
Sc.Year,
SUM(CASE WHEN S.Gender = 'F' THEN 1 ELSE 0 ENd) F,
SUM(CASE WHEN S.Gender = 'M' THEN 1 ELSE 0 ENd) M
FROM School Sc
JOIN Student S ON Sc.SchoolID = S.SchoolID
JOIN Activity A ON S.ActivityID = A.ActivityID
GROUP BY
Sc.SchoolName,
A.ActivityName,
Sc.Year
SELECT
Sc.SchoolName,
A.ActivityName,
Sc.Year,
COUNT(CASE WHEN S.Gender = 'F' THEN StudentID END) as F,
COUNT(CASE WHEN S.Gender = 'M' THEN StudentID END) as M
FROM School Sc
INNER JOIN Student S ON Sc.SchoolID = S.SchoolID
INNER JOIN Activity A ON S.ActivityID = A.ActivityID
WHERE YEAR LIKE '2020-%'
GROUP BY
Sc.SchoolName,
A.ActivityName,
Sc.Year
Please use below query,
SELECT Sc.SchoolName, Sc.Year, A.ActivityName,
case when Sc.Gender = 'M' then count(1) end as M,
case when Sc.Gender = 'F' then count(1) end as F
FROM School Sc
JOIN Student S ON Sc.SchoolID=S.SchoolID
JOIN Activity A ON S.ActivityID=A.ActivityID
GROUP BY Sc.SchoolName, Sc.Year, A.ActivityName;

sql join not taking all records from another table

I have a query like this
WITH CTE AS
(
SELECT
U.Name, U.Adluserid AS 'Empid',
MIN(CASE WHEN IOType = 0 THEN Edatetime END) AS 'IN',
MAX(CASE WHEN IOType = 1 THEN Edatetime END) AS 'out',
(CASE
WHEN MAX(E.Status) = 1 THEN 'AL'
WHEN MAX(E.Status) = 2 THEN 'SL'
ELSE 'L'
END) AS leave_status
FROM
Mx_ACSEventTrn
RIGHT JOIN
Mx_UserMst U ON Mx_ACSEventTrn.UsrRefcode = U.UserID
LEFT JOIN
Tbl_Zeo_Empstatus E ON Mx_ACSEventTrn.UsrRefcode = E.Emp_Id
WHERE
CAST(Edatetime AS DATE) BETWEEN '2019-11-03' AND '2019-11-03'
GROUP BY
U.Name, U.Adluserid
)
SELECT
[Name], [Empid], [IN], [OUT],
(CASE
WHEN CAST([IN] AS TIME) IS NULL THEN CAST(leave_status AS NVARCHAR(50))
WHEN CAST([IN] AS TIME) < CAST('08:15' AS TIME) THEN 'P'
ELSE 'L'
END) AS status
FROM
CTE
In my employee master table Mx_UserMst I have 67 employees. But here it is showing only a few employees those who are punched. I want to show all employees from employee master
I believe that the problem is his WHERE clause:
where cast(Edatetime as date) between '2019-11-03' and '2019-11-03'
Why not cast(Edatetime as date) = '2019-11-03'?
I'm not sure in which table the column Edatetime belongs (you should qualify all the columns with the correct table name/alias).
You must move the condition to an ON clause:
WITH CTE AS
(
select U.Name,U.Adluserid as 'Empid',
min(case when IOType=0 then Edatetime end) as 'IN',
max(case when IOType=1 then Edatetime end) as 'out',
case max(E.Status) when 1 then 'AL' when 2 then 'SL' else 'L' end as leave_status
from Mx_UserMst U
left join Mx_ACSEventTrn on Mx_ACSEventTrn.UsrRefcode=U.UserID and (cast(Edatetime as date) between '2019-11-03' and '2019-11-03')
left join Tbl_Zeo_Empstatus E on Mx_ACSEventTrn.UsrRefcode=E.Emp_Id
group by U.Name,U.Adluserid
)
SELECT [Name], [Empid],[IN],[OUT],
case
when cast([IN] as time) is null then cast(leave_status as nvarchar(50))
when cast([IN] as time) < cast('08:15' as time) then 'P'
else 'L'
end as status
FROM CTE
If Edatetime belongs to Tbl_Zeo_Empstatus move the condition to the next join's ON clause.
I also changed the RIGHT to a LEFT join so to make the statement more readable.
If you want to keep everything in a particular table, then that should be the first table in the FROM clause. Subsequent joins should be LEFT JOINs and conditions on subsequent tables should be in the ON clause rather than the WHERE clause.
I would also advise you to use table aliases and to only use single quotes for string and date constants -- NOT column aliases.
The following assumes that IOType and Edatetime are in the table Mx_ACSEventTrn. I should not have to guess. You should qualify all column names in the query.
WITH CTE AS (
SELECT U.Name, U.Adluserid AS Empid,
MIN(CASE WHEN AE.IOType = 0 THEN AE.Edatetime END) AS in_dt,
MAX(CASE WHEN AE.IOType = 1 THEN AE.Edatetime END) AS out_dt,
(CASE WHEN MAX(ES.Status) = 1 THEN 'AL'
WHEN MAX(ES.Status) = 2 THEN 'SL'
ELSE 'L'
END) AS leave_status
FROM Mx_UserMst U LEFT JOIN
Mx_ACSEventTrn AE
ON AE.UsrRefcode = U.UserID AND
CAST(AE.Edatetime AS DATE) BETWEEN '2019-11-03' AND '2019-11-03' LEFT JOIN
Tbl_Zeo_Empstatus ES
ON AE.UsrRefcode = ES.Emp_Id AND
GROUP BY U.Name, U.Adluserid
)
SELECT Name, Empid, IN_DT, OUT_DT,
(CASE WHEN IN_DT IS NULL THEN leave_status
WHEN CAST(IN_DT AS TIME) < CAST('08:15' AS TIME) THEN 'P'
ELSE 'L'
END) AS status
FROM CTE;
Some more points:
Don't name aliases things like IN that are already key words. That is why I gave it the name IN_DT.
There is no reason to cast to a TIME to compare to NULL.
I don't see a reason to cast to NVARCHAR(50) in the outer CASE expression.

Aggregate case when inside non aggregate query

I have a pretty massive query that in its simplest form looks like this:
select r.rep_id, u.user_id, u.signup_date, pi.application_date, pi.management_date, aum
from table1 r
left join table2 u on r.user_id=u.user_id
left join table3 pi on u.user_id=pi.user_id
I need to add one more condition that gives me count of users with non null application date per rep (like: rep 1 has 3 users with filled application dates), and assign it into categories (since 3 users, rep is a certain status category). This looks something like this:
case when sum(case when application_date is not null then 1 else 0 end) >=10 then 'status1'
when sum(case when application_date is not null then 1 else 0 end) >=5 then 'status2'
when sum(case when application_date is not null then 1 else 0 end) >=1 then 'status3'
else 'no_status' end as category
However, if I was to simply add it to the select statement, all reps will becomes of status1 because the sum() is done over all advisors with application dates filled:
select r.rep_id, u.user_id, u.signup_date, pi.application_date, pi.management_date, aum,
(
select case when sum(case when application_date is not null then 1 else 0 end) >=10 then 'status1'
when sum(case when application_date is not null then 1 else 0 end) >=5 then 'status2'
when sum(case when application_date is not null then 1 else 0 end) >=1 then 'status3'
else 'no_status' end as category
from table3
) as category
from table1 r
left join table2 u on r.user_id=u.user_id
left join table3 pi on u.user_id=pi.user_id
Can you assist with having the addition to my query to be across reps and not overall? Much appreciated!
Based on your description, I think you need a window function:
select r.rep_id, u.user_id, u.signup_date, pi.application_date, pi.management_date, aum,
count(pi.application_date) over (partition by r.rep_id) as newcol
from table1 r left join
table2 u
on r.user_id = u.user_id left join
table3 pi
on u.user_id = pi.user_id;
You can use the count() in a case to get ranges, if that is what you prefer.

SQL count number of users hava a value > 1

I need a select which brings two lines, one with the number of people with the " number of hits " > 0 and the other line with the number of people with the " number of hits " = 0
SELECT u.name as 'Usuário',u.full_name as 'Nome do Usuário',count(l.referer) as 'Número de Acessos'
FROM mmp_user u
LEFT JOIN MMP_MMPUBLISH_LOG l
on u.id=l.user_id
AND l.event_date between '2015-08-01' and '2015-08-08'
group by u.name,u.full_name
order by count(l.referer) desc
I have,
151 Users
9 accessed and
142 not accessed.
But i don't return this values in select, help me please.
Table mmp_user fields (ID,CREATED_BY,AVATAR_ID,CREATION_DATE,EMAIL,FULL_NAME,LAST_EDITED_BY,LAST_EDITION_DATE,NAME,OBSERVATION,USER_PASSWORD,PASSWORD_REMINDER,SIGNATURE,STATUS,ADMINISTRATOR,DESIGNER,SECURITY_OFFICE,PUBLISHER,BRANCH_ID,DEPARTMENT_ID,EXTENSION,PHONE,COMPANY_ID,POSITION,ADMISSION_DATE,PASSWORD_LAST_EDITION_DATE,DISMISSED_DATE,NEWSLETTER,EXPIRE_DATE,COMPANY,BRANCH,DEPARTMENT,AREA_ID,SITE,USER_NUMBER,PREFIX_HOME_PHONE,PREFIX_MOBILE_PHONE,ADDRESS,ADDRESS_COMPLEMENT,ADDRESS_TYPE,CITY,NEIGHBORHOOD,STATE,ZIP_CODE,BIRTHDATE,GENDER,HOME_PHONE,MOBILE_PHONE,CPF,MARIAGE_STATUS,NATIONALITY,RG,EDUCATION,URL_SITE,FIRST_NAME,LAST_NAME,ID_SAP,PASSWORD_GAFISA,NICKNAME,CODE_POSITION,CREATION_USER_ORIGIN,LEVEL_POSITION,BIRTH_DATE_VISIBILITY,HOME_PHONE_COUNTRY_PREFIX,HOME_PHONE_VISIBILITY,MOBILE_PHONE_COUNTRY_PREFIX,MOBILE_PHONE_VISIBILITY,AREA_PREFIX,COUNTRY_PREFIX,PHONE_OBSERVATION,RESPONSIBLE,RESOURCE_ID,AVATAR_RF_ID,RESOURCE_AVATAR_ID,AVATAR_URL_LUCENE,avatarurl,PASSWORD_EXCHANGE,USER_NAME_EXCHANGE,DOMAIN_EXCHANGE,I18N,LAST_IMPORT_FILE,HIERARCHY_POSITION,SECRET_NICKNAME,PROFILE_TYPE,NOT_VIEW_USER,CHANGE_POSITION_DATE,DISTINGUISHED_NAME,OU_USER,AUTH_TOKEN,AUTH_TOKEN_EXPIRATION)
TableMMP_MMPUBLISH_LOG fields (ID,MMPUBLISH_LOG_TYPE,EVENT_DATE,USER_ID,TRANSACTION_NAME,USER_IP,USER_LOGIN,USER_NAME,SESSION_ID,REFERER,PUBLISHING_OBJECT_ID,PUBLISHING_OBJECT_NAME,PHASE_ID,PHASE_NAME,PHASE_COMMENT,ACCESS_URL,HOME_PAGE_ID,HOMEPAGE_ID,phaseComment,phaseId,phaseName,PO_VERSION_NUMBER)
Thanks
You could wrap this query with another query and apply a case expression to the count:
SELECT access_code, COUNT(*)
FROM (SELECT u.name,
u.full_name,
CASE WHEN COUNT(l.referer) > 0 THEN 'access'
ELSE 'no access'
END as access_code
FROM mmp_user u
LEFT JOIN mmp_mmpupluish_log l ON
u.id=l.user_id AND
l.event_date BETWEEN '2015-08-01' AND '2015-08-08'
GROUP BY u.name, u.full_name) t
GROUP BY access_code
ORDER BY access_code ASC
SELECT u.name Usuário, u.full_name [Nome do Usuário],
count(l.referer) [Número de Acessos],
Sum(case when NumberOfHits = 0 then 1 else 0 end) ZeroHitsCount,
Sum(case when NumberOfHits > 0 then 1 else 0 end) HasSomeHitsCount
FROM mmp_user u
LEFT JOIN MMP_MMPUBLISH_LOG l
on u.id=l.user_id
AND l.event_date between '2015-08-01' and '2015-08-08'
group by u.name, u.full_name
order by count(l.referer) desc
Use a case statement:
SELECT (case when l.referer is null then 'Not Accessed'
else 'Accessed'
end) as which,
count(*) as 'Número de Acessos'
FROM mmp_user u LEFT JOIN
MMP_MMPUBLISH_LOG l
on u.id = l.user_id AND
l.event_date between '2015-08-01' and '2015-08-08'
group by (case when l.referer is null then 'Not Accessed'
else 'Accessed'
end)
order by count(l.referer) desc;
Actually, the above counts the number of accesses. One way to get the number of users is to use count(distinct u.id). Another way uses a subquery:
select AccessType, count(*)
from (select u.*,
(case when exists (select 1
from MMP_MMPUBLISH_LOG l
where u.id = l.user_id AND
l.event_date between '2015-08-01' and '2015-08-08'
)
then 'Accessed' else 'Not Accessed'
end) as AccessType
from mmp_user u
) u
group by AccessType;

Joined SQL query MAX aggregate with condition

I am having trouble with a SQL query. Here is a representation of my schema on SQL Fiddle:
http://sqlfiddle.com/#!15/14c8e/1
The issue is that I want to return rows of data from the Invitations table and join them with a sum of both the 'sent' event_type and 'viewed' event_type from the associated events, as well as the latest created_at date.
I can get all the data and counts working, but am having issue with the last_sent_on. Is there a way I can use a condition in a MAX aggregate function?
e.g.
MAX(
SELECT events.created_at
WHERE event_type='sent'
)
If not, how would I write the proper subselect?
I am currently using Postgresql.
Thank you.
You can use a case statement inside of max just as you've done with sum. The query below will select the maximum created_at for event_type='sent'
SELECT
i.id,
i.name,
i.email,
max(case when e.event_type='sent' then e.created_at end) AS last_sent_on,
sum(case when e.event_type='sent' then 1 else 0 end) AS sent_count,
sum(case when e.event_type='viewed' then 1 else 0 end) AS view_count
FROM
invitations i
LEFT OUTER JOIN
events e
ON e.eventable_id = i.id
WHERE e.eventable_type='Invitation'
GROUP BY i.id, i.name, i.email
SQLFiddle
Try using a subquery to build the max value for sent.
SELECT
i.id,
i.name,
i.email,
sent.last_sent,
sum(case when e.event_type='sent' then 1 else 0 end) AS sent_count,
sum(case when e.event_type='viewed' then 1 else 0 end) AS view_count
FROM
invitations i
LEFT OUTER JOIN
events e
ON e.eventable_id = i.id
LEFT JOIN ( SELECT eventable_id uid, MAX(created_at) AS last_sent
FROM events
WHERE event_type = 'sent'
GROUP BY eventable_id ) AS sent
ON sent.uid = i.id
WHERE e.eventable_type='Invitation'
GROUP BY i.id, i.name, i.email, sent.last_sent