Finding the highest average salary of a department using joins in SQL? - sql

I have two tables with the below schema:
Table 1
-------
empID
empName
Table 2
-------
empID
department
salary
Assuming the tables are:
Table1:
empID|empName
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
10 J
Table 2:
empID|department|salary
1 X 10
2 X 10
3 X 10
4 Y 5
5 Y 5
6 Y 5
7 Y 5
8 Y 5
9 Z 3
10 Z 3
I need to find the department name with the highest average salary and display them along with the employee names.
The output I am expecting is:
empName|department|salary
A 10
B X 10
C 10
This was an interview question, and I am recreating this from memory so it might not be perfect. I am also picking up SQL after a gap of more than 2 years. Please suggest if I am missing something.
The query that I have formed is:
SELECT
table1.empName,
TOP(1) AVG(table2.salary),
table2.department
FROM
table1
INNER JOIN
table2
ON table1.empID = table2.empID
GROUP BY
table2.department

Your syntax looks like SQL Server (the "TOP 1"). In that database, I would do something like this:
SELECT TOP (1) WITH TIES t1.empName, t2.salary, t2.department
FROM table1 t1 INNER JOIN
table2 t2
ON t1.empID = t2.empID
ORDER BY AVG(t2.salary) OVER (PARTITION BY t2.department) DESC;
A more generic solution:
SELECT empName, salary, department
FROM (SELECT t.*,
DENSE_RANK() OVER (ORDER BY avg_salary) as seqnum
FROM (SELECT t1.empName, t2.salary, t2.department,
AVG(t2.salary) OVER (PARTITION BY t2.department) as avg_salary
FROM table1 t1 INNER JOIN
table2 t2
ON t1.empID = t2.empID
) t
) t
WHERE seqnum = 1;

Based on what I draw from your question, I would approach it this way.
WITH department_rank AS
(
SELECT
department,
RANK() OVER(ORDER BY avg_salary DESC) AS avg_salary_rank
FROM
(
SELECT
department,
AVG(salary) AS avg_salary
FROM
table2
GROUP BY
department
) tbl
)
SELECT
dept.department,
emp.empID,
emp.empName,
dept.salary
FROM
table2 dept
JOIN
table1 emp
ON (emp.empID = dept.empID)
JOIN
department_rank drnk
ON (drnk.department = dept.department)
AND (drnk.avg_salary_rank = 1) --Top ranked department based on average salary
OUTPUT:

Related

Calculation of points if there are 2 student with same rank in sql server

Rank_Table
ID Rank
1 1
2 1
3 3
4 3
5 5
Price
No Points
1 10
2 9
3 8
4 7
5 6
Expected Output
ID Rank Points
1 1 9.5
2 1 9.5
3 3 7.5
4 3 7.5
5 5 6
2nd rank is not present so 1st and 2nd points are sum together and distributed among the number of the student
for eg : (10+9) / 2 = 9.5
When I join the 2 table like
select *
from Rank_table a join
Price b
on a.ID = b.No
I am getting the output as
ID Rank No Points
1 1 1 10
2 1 2 9
3 3 3 8
4 3 4 7
5 5 5 6
This seems to be quite a simple requirement, simply using AVG and an OVER clause.
CREATE TABLE [Rank] (ID int, [Rank] int)
CREATE TABLE Price ([No] int, Points int);
GO
INSERT INTO [Rank]
VALUES
(1,1),
(2,1),
(3,3),
(4,3),
(5,5);
INSERT INTO Price
VALUES
(1,10),
(2,9),
(3,8),
(4,7),
(5,6);
GO
SELECT R.ID,
R.[Rank],
AVG(CONVERT(decimal(2,0),P.Points)) OVER (PARTITION BY R.[Rank]) AS Points
FROM [Rank] R
JOIN Price P ON R.ID = P.[No];
GO
DROP TABLE [Rank];
DROP TABLE Price;
You need simple JOIN :
select rn.*,
avg(convert(decimal(10,0), p.Points)) over (partition by rn.rnk) as points
from Rank_Table rn inner join
Price p
on p.id = rn.No;
SELECT *,
AA.pts AS POINTS
FROM rank_table R
INNER JOIN (SELECT rank,
Sum(points) / Count(*) AS PTS
FROM rank_table a
JOIN price b
ON a.id = b.no
GROUP BY rank)AA
ON ( R.rank = AA.rank )
You can calculate AVG at rank level and then join back to Rank_Table like in this working demo
select R.*,T.points from Rank_table R
JOIN
(
select rank, points=avg(cast(points as decimal(10,2)))
from Rank_table a join
Price b
on a.ID = b.No
group by rank
)T
on T.rank=R.rank
Hmmm. You seem to want a non-equijoin based on the "next" rank as well as the rank in each row. Unfortunately, SQL Server 2008 doesn't support lead(), but you can use apply:
select rt.id, rt.rank, avg(p.price * 1.0) as points
from rank_table rt outer apply
(select min(rt2.rank) as next_rank
from rank_table rt2
where rt2.rank > rt.rank
) rt2 left join
price p
on p.no >= rt.rank >= p.no and
(p.no < rt2.next_rank or rt2.next_rank is null)
group by rt.id, rt.rank;

Get the group which has maximum number of records in SQL Server

[ID] [Name] [Dept]
--------------------
1 Manu A
2 Anu A
3 Tanu A
4 Danu A
5 Anu B
6 Danu B
7 Danu C
8 Anu C
9 Tanu C
10 John C
11 Anu D
12 Jane D
13 Danu D
I need to get the Dept with maximum number of employees.
Here is what I tried
SELECT
ID, Name, Dept
FROM
(SELECT
*,
rn = ROW_NUMBER() OVER(PARTITION BY Dept)
FROM Emp) t
WHERE
rn = (SELECT MAX(rn) FROM t)
I need help in the WHERE clause.
You need aggregation to count the number of employees. The approach using row_number() is one approach, but with the right query:
SELECT Dept
FROM (SELECT Dept, COUNT(*) as cnt,
ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) as seqnum
FROM Emp
) e
WHERE seqnum = 1;
However, a more common approach would just use ORDER BY and TOP:
SELECT TOP (1) Dept, COUNT(*) as cnt
FROM emp
GROUP BY Dept
ORDER BY COUNT(*) DESC;
If you wanted ties, then you would use WITH TIES in the SELECT.
Selecting all departments having the same max number of employees:
;WITH c -- create a list of depts and number of emp's
AS (SELECT deptid,
Count(*) cnt
FROM emp
GROUP BY deptid)
SELECT d.*
FROM dept d
INNER JOIN c
ON d.deptid = c.deptid
WHERE c.cnt = (SELECT Max(cnt)
FROM c)
No WHERE is needed. Try using a GROUP BY
SELECT COUNT(Name) as NameCount, Dept from Table
GROUP BY Dept
ORDER BY COUNT(Name) DESC
The biggest group(s) will be at the top
Results
NameCount | Dept
4 A
4 C
3 D
2 B
Ok, now you added the table structure:
;WITH c
AS (SELECT Dept,
Count(*) cnt
FROM Emp
GROUP BY Dept)
SELECT c.*
FROM c
WHERE c.cnt = (SELECT Max(cnt)
FROM c)
I can't quite figure your table structure, but this selects the department with the most employees
SELECT * from Dept WHERE deptid = (
SELECT TOP 1 deptid FROM employees
GROUP BY deptid
ORDER BY COUNT(*) DESC
)

Inner join with one row of another table

**Table Employee**
Id Name
1 EmpName1
2 EmpName2
3 EmpName3
**Table EmpDeptHistory**
Id EmpId Dept Date
1 1 Housing 2015-03-02
2 2 Finance 2015-01-03
3 1 WareHouse 2015-05-02
4 2 Housing 2015-02-06
5 3 WareHouse 2015-02-02
6 1 Housing 2015-05-01
7 2 Finance 2015-01-02
8 2 Housing 2015-05-04
9 2 Finance 2015-05-02
10 1 WareHouse 2015-03-08
11 1 Housing 2015-02-20
I need find the recent dept with which every employee worked. Also I need to find for individual employee by passing EmpId
The following query returns only one employee and not all :(
SELECT e.id, edh.dept,edh.date
FROM Employee e
inner join (select top 1 eh.empid, eh.dept, eh.date
from EmpDeptHistory eh
order by eh.date desc) as edh
on e.id=edh.empid
yes, I understand the top 1 will give the emp id based on date, hence only one employee details is show. I am not sure how to get all the employee recent department.
select e.id,edh.dept,edh.date
from employee e
inner join EmpDeptHistory edh
on e.id = (Select eh.empid, eh.dept, eh.date
from EmpDeptHistory eh
where e.id=eh.empid
order by eh.date desc)
The above throws
The ORDER BY clause is invalid in views, inline functions, derived
tables, subqueries, and common table expressions, unless TOP,
OFFSET or FOR XML is also specified.
You can use CROSS APPLY to run the right-hand subquery once for each left-hand row:
SELECT e.id, edh.dept,edh.date
FROM Employee e cross apply ( select top 1 eh.empid, eh.dept, eh.date from
EmpDeptHistory eh where eh.empid = e.id order by eh.date desc) as edh
You can use a CTE and a ranking function like ROW_NUMBER:
WITH CTE AS
(
SELECT e.id, edh.dept, edh.date,
rn = ROW_NUMBER() OVER (PARTITION BY edh.EmpId ORDER BY edh.date DESC)
FROM Employee e inner join EmpDeptHistory edh
on e.id = edh.empid
)
SELECT id, dept, date
FROM CTE
WHERE rn = 1
DEMO
For the latest department for each employee, you can do it like so:
SELECT t1.*
FROM EmpDeptHistory t1 INNER JOIN
(
SELECT EmpId, MAX(Date) [Date]
FROM EmpDeptHistory
GROUP BY EmpId
) AS t2
ON t1.EmpId = t2.EmpId AND t1.Date = t2.Date
EmpId can be put into a where clause if needed.

left join without duplicate values using MIN()

I have a table_1:
id custno
1 1
2 2
3 3
and a table_2:
id custno qty descr
1 1 10 a
2 1 7 b
3 2 4 c
4 3 7 d
5 1 5 e
6 1 5 f
When I run this query to show the minimum order quantities from every customer:
SELECT DISTINCT table_1.custno,table_2.qty,table_2.descr
FROM table_1
LEFT OUTER JOIN table_2
ON table_1.custno = table_2.custno AND qty = (SELECT MIN(qty) FROM table_2
WHERE table_2.custno = table_1.custno )
Then I get this result:
custno qty descr
1 5 e
1 5 f
2 4 c
3 7 d
Customer 1 appears twice each time with the same minimum qty (& a different description) but I only want to see customer 1 appear once. I don't care if that is the record with 'e' as a description or 'f' as a description.
First of all... I'm not sure why you need to include table_1 in the queries to begin with:
select custno, min(qty) as min_qty
from table_2
group by custno;
But just in case there is other information that you need that wasn't included in the question:
select table_1.custno, ifnull(min(qty),0) as min_qty
from table_1
left outer join table_2
on table_1.custno = table_2.custno
group by table_1.custno;
"Generic" SQL way:
SELECT table_1.custno,table_2.qty,table_2.descr
FROM table_1, table_2
WHERE table_2.id = (SELECT TOP 1 id
FROM table_2
WHERE custno = table_1.custno
ORDER BY qty )
SQL 2008 way (probably faster):
SELECT custno, qty, descr
FROM
(SELECT
custno,
qty,
descr,
ROW_NUMBER() OVER (PARTITION BY custno ORDER BY qty) RowNum
FROM table_2
) A
WHERE RowNum = 1
If you use SQL-Server you could use ROW_NUMBER and a CTE:
WITH CTE AS
(
SELECT table_1.custno,table_2.qty,table_2.descr,
RN = ROW_NUMBER() OVER ( PARTITION BY table_1.custno
Order By table_2.qty ASC)
FROM table_1
LEFT OUTER JOIN table_2
ON table_1.custno = table_2.custno
)
SELECT custno, qty,descr
FROM CTE
WHERE RN = 1
Demolink

How do I get a single row value in a group statement?

I have two tables, JobTable and EmployeeTable with the following data:
EmployeeTable:
EmpId Salary
1 10
2 20
3 30
4 40
5 50
6 60
JobTable:
JobId EmpId
A 1
A 2
B 3
B 4
C 5
C 6
I need an SQL statement that will return the EmpId of the Employee with the minimum salary for each Job.
You could use the RANK() function like this:
WITH ranked AS (
SELECT
j.JobId,
e.EmpId,
e.Salary,
RANK() OVER (PARTITION BY j.JobId ORDER BY e.Salary) AS rnk
FROM JobTable j
INNER JOIN EmployeeTable e ON j.EmpId = e.EmpId
)
SELECT
JobId,
EmpId,
Salary,
FROM ranked
WHERE rnk = 1
Hmm, give this one a shot:
SELECT st.EmpID, min(st.salary)
FROM SalaryTable st INNER JOIN JobTable jt ON st.EmpID=jt.EmpID
WHERE jt.JobID = 'A'
GROUP BY st.EmpID