Handle the case if an employee have more than one allowance - sql

I have to write a simple query that calculates the total salary and I have a problem that if an employee have more than allowance it will show him twice.
This is what I wrote:
SELECT
employee.ename, Dept.Dname
, (Employee.Salary + ISNULL(allownces.amount,0)-ISNULL(deduction.amount,0)) AS total_salary
, allownces.amount AS allow, deduction.amount AS ded
FROM Employee
LEFT JOIN Dept ON Dept.id = Employee.dept_id
LEFT JOIN allownces ON allownces.emp_id = Employee.id
LEFT JOIN deduction ON deduction.emp_id = Employee.id
GROUP BY ename, Dname, Salary, allownces.amount, deduction.amount
and the result it will calculate the total salary of an employee if he have 2 allowance then it will calculate it two times and I want to calculate the allowance if he have more than one and show it as one.

Try with outer apply instead of left join.
SELECT
employee.ename, Dept.Dname, (Employee.Salary + ISNULL(a.amount,0)-ISNULL(d.amount,0)) AS total_salary
, allownces.amount AS allow, deduction.amount AS ded
FROM Employee
LEFT JOIN Dept ON Dept.id = Employee.dept_id
OUTER APPLY (
SELECT sum(amount) amount
FROM allownces
WHERE allownces.emp_id = Employee.id
) a
OUTER APPLY (
SELECT sum(amount) amount
FROM deduction
WHERE deduction.emp_id = Employee.id
) d

Related

Having hard time understanding SQL Query

I ran across this query and having hard time understanding what it does
SELECT DISTINCT
EMPLOYEE.EMPLOYEE_ID,
EMPLOYEE.LAST_NAME,
EMPLOYEE.FIRST_NAME,
COUNT(*)
FROM EMPLOYEE
JOIN ENTRY ON EMPLOYEE.EMPLOYEE_ID = ENTRY.EMPLOYEE_ID
JOIN TICKET ON ENTRY.TICKET_ID = TICKET.TICKET_ID
WHERE ENTRY.ACTIVITY_ID = 'ADVTS' AND EMPLOYEE.DEPARTMENT_ID ='SLS'
GROUP BY EMPLOYEE.EMPLOYEE_ID, EMPLOYEE.LAST_NAME,
EMPLOYEE.FIRST_NAME,ENTRY.ENTRY_ID
HAVING COUNT(ENTRY.ENTRY_ID) >=
(SELECT CAST(1.25 * COUNT(ENTRY.ACTIVITY_ID)/COUNT(DISTINCT EMPLOYEE.EMPLOYEE_ID) AS float)
FROM
EMPLOYEE
JOIN
ENTRY ON EMPLOYEE.EMPLOYEE_ID = ENTRY.EMPLOYEE_ID
WHERE
ENTRY.ACTIVITY_ID = 'ADVTS' AND EMPLOYEE.DEPARTMENT_ID = 'SLS')
As far as I understand it gives a list of EMPLOYEEs who done ADVTS ACTIVITY and from DEPARTMENT SLS which made ENTRYs at least as much as Average entries made in the DEPARTMENT SLS for ADVTS purposes
Thanks to anyone who take their time to help
Edit succesful result after comments:
SELECT
EMPLOYEE.EMPLOYEE_ID,
EMPLOYEE.LAST_NAME,
EMPLOYEE.FIRST_NAME
FROM EMPLOYEE
JOIN
ENTRY ON ENTRY.EMPLOYEE_ID = EMPLOYEE.EMPLOYEE_ID
GROUP
BY EMPLOYEE.EMPLOYEE_ID, EMPLOYEE.LAST_NAME,
EMPLOYEE.FIRST_NAME
HAVING
COUNT(ENTRY.ENTRY_ID) >=
(SELECT
CAST(1.25 *
COUNT(ENTRY.ACTIVITY_ID)/COUNT(DISTINCT EMPLOYEE.EMPLOYEE_ID)AS float)
FROM
EMPLOYEE JOIN ENTRY ON EMPLOYEE.EMPLOYEE_ID = ENTRY.EMPLOYEE_ID
WHERE
ENTRY.ACTIVITY_ID = 'ADVTS' AND EMPLOYEE.DEPARTMENT_ID = 'SLS')
OUTPUT:
EMPLOYEE_ID| LAST_NAME| FIRST_NAME
7 | Salesman | Efficient
Assuming that TICKET_ID is unique in Ticket and is never NULL in ENTRY, then you can get rid of that JOIN.
Then I assume the purpose of the query is to return employees whose count is more than 1.25 the overall average. That requires a few more (reasonable) assumptions, but this is more simply written as:
SELECT e.*
FROM (SELECT EM.EMPLOYEE_ID, EM.LAST_NAME, EM.FIRST_NAME, COUNT(*) AS CNT,
SUM(COUNT(*)) OVER () * 1.0 / COUNT(*) OVER () as AVG_CNT
FROM EMPLOYEE EM JOIN
ENTRY EM
ON EM.EMPLOYEE_ID = EN.EMPLOYEE_ID
WHERE EN.ACTIVITY_ID = 'ADVTS' AND EM.DEPARTMENT_ID = 'SLS'
GROUP BY EM.EMPLOYEE_ID, EM.LAST_NAME, EM.FIRST_NAME
) e
WHERE cnt >= 1.25 * avg_cnt

SQL MAX() grouping

Tables:
student(sid, sname, sex, age, year, gpa)
major(dname, sid)
Question:
For each department with more than 15 students majoring in the department, we want to print information about the student(s) with the highest GPA within the department. In particular, for each such student, we want to print the student id, student name and GPA, and the department name the student is major in.
So far I have:
SELECT student.sid, student.sname, student.gpa, major.dname
FROM student
RIGHT JOIN major ON student.sid = major.sid
WHERE student.gpa IN (
SELECT MAX(gpa)
FROM student JOIN major ON student.sid = major.sid
GROUP BY dname
HAVING COUNT(dname) > 15
)
But it doesn't give me the accurate query. The clause inside the IN works but when put together in this way it doesn't actually match student.gpa to dname max GPA. What am I doing wrong here?
This Query gives:
enter image description here
I need:
enter image description here
Your inner query gives you the maximum GPA for each department. Then the outer query returns all students who have a GPA equal to any of the maximum GPA's, regardless of the department. The quickest fix of your code is to use a correlated subquery, that will find the maximum GPA for the student's specific department.
SELECT s.sid, s.sname, s.gpa, sm.dname
FROM student s
RIGHT JOIN major sm ON s.sid = sm.sid
WHERE student.gpa IN (
SELECT MAX(ds.gpa)
FROM student ds JOIN major dm ON ds.sid=dm.sid
WHERE dm.dname = sm.dname
GROUP BY dm.dname
HAVING COUNT(dm.dname)>15
)
This query:
select dname, max(s.gpa) maxgpa
from major m inner join student s
on s.sid = m.sid
group by dname
having count(s.sid) > 15
returns all the departments with more than 15 students and the highest gpa in that department.
Join it to the 2 tables like this:
select s.sid, s.sname, s.gpa, t.dname
from (
select dname, max(s.gpa) maxgpa
from major m inner join student s
on s.sid = m.sid
group by dname
having count(s.sid) > 15
) t
inner join major m on m.dname = t.dname
inner join student s on s.sid = m.sid and s.gpa = t.maxgpa
Or with window functions:
select t.sid, t.sname, t.gpa, t.dname
from (
select m.dname, s.*,
rank() over (partition by m.dname order by s.gpa desc) rn,
count(s.sid) over (partition by m.dname) counter
from major m inner join student s
on s.sid = m.sid
) t
where t.counter > 15 and t.rn = 1
If you change your WHERE ... IN statement to be an INNER JOIN, you can connect more fields.
SELECT student.sid, student.sname, student.gpa, major.dname
FROM student
RIGHT JOIN major
ON student.sid = major.sid
INNER JOIN (
SELECT MAX(gpa) as max_gpa, dname
FROM student JOIN major ON student.sid=major.sid
GROUP BY dname
HAVING COUNT(dname)>15
) as dept_gpa
ON student.gpa = dept_gpa.max_gpa
AND major.dname = dept_gpa.dname

SQL statement to select from two tables

I have two tables employees, salary_advance.
employees table has columns empid, name, salary and salary_advance has id, empid, amount, date
I want to show name, salary, remaining for all employees ..
remaining = ( salary - amount )
when I do inner join I get only employees who take advance ..
I want to show who take advance + other employees in employees ..
This is my SQL statement
select
employees.name , employees.salary ,
(employees.salary - salary_advance.amount )
from
employees
inner join
salary_advance on employees.empid = salary_advance.empid
You'll need to use a LEFT OUTER JOIN instead of your INNER JOIN, and you'll also want to use ISNULL to get a 0 instead of NULL from the salary_advance table:
SELECT
employees.name,
employees.salary,
Remaining = (employees.salary - ISNULL(salary_advance.amount, 0) )
FROM
employees
LEFT OUTER JOIN
salary_advance ON employees.empid = salary_advance.empid
If an employee can have more than one advance, you'll want to use a LEFT JOIN with SUM and GROUP BY to get the correct result. If you need to count only advances since a certain date, add that to the ON clause of the LEFT JOIN;
SELECT employees.name , employees.salary ,
(employees.salary - COALESCE(SUM(salary_advance.amount),0)) remaining
FROM employees
LEFT JOIN salary_advance
ON employees.empid = salary_advance.empid
AND salary_advance.date >= '2012-01-01'
GROUP BY employees.name, employees.salary
An SQLfiddle to test with.
Also you can use:
SELECT
employees.name,
employees.salary,
( CASE salary_advance.amount
WHEN NULL THEN employees.salary
ELSE employees.salary - salary_advance.amount
END
) Remaining
FROM
employees
LEFT OUTER JOIN
salary_advance ON employees.empid = salary_advance.empid
Use a left join, and take care of the null values:
select
e.name , e.salary,
employees.salary - isnull(a.amount, 0)
from
employees e
left outer join
salary_advance a on e.empid = a.empid
The isnull function might be named ifnull, depending on what database you are using.
Print this:
select
employees.name,
employees.salary,
Remaining = (employees.salary - ISNULL(salary_advance.amount, 0) )
from
employees
left outer join
salary_advance on employees.empid = salary_advance.empid
instead of this:
select
employees.name , employees.salary ,
(employees.salary - salary_advance.amount )
from
employees
inner join
salary_advance on employees.empid = salary_advance.empid
Summary: You need to use a left outer join instead of inner join
Try this:
select
employees.name , employees.salary ,
Remaining = (employees.salary - ISNULL(salary_advance.amount, 0))
from
employees
left join
salary_advance on employees.empid = salary_advance.empid
The LEFT JOIN keyword returns all rows from the left table, with the matching rows in the right table.

How to modify this query to show the total number of employees and percent completion in each division?

I have the following database design:
Employee Table: Username, Name, DivisionCode
Division Table: SapCode, DivisionShortcut
Courses Table: CourseID, CourseName, GroupID
Groups Table: GroupID, GroupName
Employee_Courses Table: EmployeeID, CourseID
I came up with the following query that shows the divisions, courses and the total number of participants in each division:
SELECT dbo.Divisions.DivisionShortcut, dbo.courses.CourseName, COUNT(dbo.employee_courses.courseId) AS [Total Number of Participants]
FROM dbo.courses INNER JOIN
dbo.employee_courses ON dbo.courses.CourseID = dbo.employee_courses.courseId INNER JOIN
dbo.groups ON dbo.courses.GroupID = dbo.groups.ID RIGHT OUTER JOIN
dbo.employee AS employee_1 INNER JOIN
dbo.Divisions ON employee_1.DivisionCode = dbo.Divisions.SapCode ON dbo.employee_courses.employeeId = employee_1.Username
WHERE (dbo.courses.GroupID = 1)
GROUP BY dbo.courses.CourseID, dbo.courses.CourseName, dbo.Divisions.DivisionShortcut
In addition to be shown above, I want to display the total number of employees in each division and the percent completion in each division, too. By the way, the percent completion is equal to (total number of participants in each course / total number of employees in each division)
So how to do that?
You could use a subquery to add the total employee count to the join on Divisions. For example:
SELECT d.DivisionShortcut
, c.CourseName
, d.EmployeeCount as DivisionEmployees
, count(c.courseId) as CompletedCourses
, 100.0 * count(c.courseId) / d.EmployeeCount as PercentageCompleted
FROM dbo.courses c
JOIN dbo.employee_courses ec
ON c.CourseID = ec.courseId
JOIN dbo.employee e
ON ec.employeeId = e.Username
JOIN (
select d2.SapCode
, d2.DivisionShortcut
, count(e2.DivisionCode) as EmployeeCount
from dbo.Divisions d2
JOIN dbo.employee e2
ON d2.SapCode = e2.DivisionCode
group by
d2.SapCode
, d2.DivisionShortcut
) d
ON d.SapCode = e.DivisionCode
WHERE c.GroupID = 1
GROUP BY
d.DivisionShortcut
, d.EmployeeCount
, c.CourseName

How to show the total number of employees and participants, and participation percentage in all departments?

I have the following database design:
Employees Table: EmployeeID, Name, OrgCode
Departments Table: OrgCode, DepartName
CompleteSurvey Table: ID, ParticipantID
And I need to develop one query that will display a table that shows the total number of employees in all departments and the total number of participants who completed the survey in all departments.
I could be able to find the total number of employees in all department by the following query:
SELECT COUNT(DISTINCT dbo.Employees.EmployeeID) AS [Total Number of Employees]
FROM dbo.Departments INNER JOIN
dbo.Employees ON dbo.Departments.OrgCode = dbo.Employees.OrgCode CROSS JOIN
dbo.CompleteSurvey
Then, I could be able to find the total number of participants in all department by the following query:
SELECT COUNT(DISTINCT dbo.CompleteSurvey.ID) AS [Total Number of Participants]
FROM dbo.Departments INNER JOIN
dbo.Employees ON dbo.Departments.OrgCode = dbo.Employees.OrgCode INNER JOIN
dbo.CompleteSurvey ON dbo.Employees.EmployeeID = dbo.CompleteSurvey.RespondantID
But I should have a one query only.
For example, if Department A has 100 employee and the number of participants is 50 out of 100
and Department B has 80 employee and the number of participants is 30
The query should show the following:
the total number of employees in all departments = 180
the total number of participants in all departments = 80
the percent completion in all of them = 80/180 = 44%
So how to do that?
I don't know SQL Server, I hope an Oracle solution could help too:
select e.employees, c.respondents, (c.respondents / e.employees) * 100 percentage
from (select count(*) employees from employees) e,
(select count(*) respondents from complete_survey) c
You don't need to involve the Department table in these calculations:
SELECT
TotalEmployees = COUNT(*),
TotalParticipants = COUNT(s.ParticipantID),
ParticipationPercentage = COUNT(s.ParticipantID) * 100.0 / COUNT(*)
FROM dbo.Employee e
LEFT JOIN (SELECT DISTINCT ParticipantID FROM dbo.CompleteSurvey) s
ON e.EmployeeID = s.ParticipantID