Inner join with one row of another table - sql

**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.

Related

Join Tables and Return data in single row different columns with count of join

We have 3 tables in Oracle 11g, need to left join them and return the data in single row different columns with count of the join, Is there any way We can acheive the same.
Example:
Table1: (Employee_Data)
Table2: (Employee_Address)
Table3: (Employee_Role)
Expected Result:
Mack has 2 addresses and 2 roles so Emp_Addr_Count is 2, Emp_Role_Count is 2 and the related data is in same row different column.
Kindly note that EMP_ID is unique in Employee_Data table and Employee_Address and Employee_Role could be multiple or zero for a Employee.
Thanks in Advance.
Try this:
SELECT E.Emp_Id
,E.Emp_Name
,E.Emp_Age
,NVL(MAX(EA.RN),0)Addr_Count
,NVL(MAX(CASE WHEN EA.RN = 1 THEN EA.Emp_Address END),' ')Emp_Address_1
,NVL(MAX(CASE WHEN EA.RN = 1 THEN EA.Emp_City END),' ')Emp_City_1
,NVL(MAX(CASE WHEN EA.RN = 2 THEN EA.Emp_Address END),' ')Emp_Address_2
,NVL(MAX(CASE WHEN EA.RN = 2 THEN EA.Emp_City END),' ')Emp_City_2
,NVL(MAX(ER.RN1),0)Role_Count
,NVL(MAX(CASE WHEN ER.RN1 = 1 THEN ER.Emp_task END),' ')Emp_task_1
,NVL(MAX(CASE WHEN ER.RN1 = 2 THEN ER.Emp_task END),' ')Emp_task_2 FROM Employee_Data E JOIN(
SELECT Emp_Id
,ROW_NUMBER() OVER(PARTITION BY Emp_Id ORDER BY Emp_City desc) RN
,Emp_City
,Emp_Address
FROM Employee_Address
)EA ON EA.Emp_Id = E.Emp_Id left JOIN(
SELECT Emp_Id
,ROW_NUMBER() OVER(PARTITION BY Emp_Id ORDER BY Emp_Task) RN1
,Emp_task
FROM Employee_Role
)ER ON ER.Emp_Id = E.Emp_Id GROUP BY E.Emp_Id,E.Emp_Name,E.Emp_Age
Output:
EMP_ID EMP_NAME EMP_AGE ADDR_COUNT EMP_ADDRESS_1 EMP_CITY_1 EMP_ADDRESS_2 EMP_CITY_2 ROLE_COUNT EMP_TASK_1 EMP_TASK_2
1 MACK 45 2 HOME PARADISE MUM TINDER ONCLAVE DEL 2 Manage Task Resource Manage
2 JACK 30 1 BLUE PLAZA MUM 1 Code
3 ANGEL 27 1 HOME PARADISE MUM 0
You can join them as in the following statement :
WITH t AS
(
SELECT d.*, a.emp_address, a.emp_city, r.emp_task
FROM employee_data d
JOIN employee_address a on ( d.emp_id = a.emp_id )
FULL OUTER JOIN employee_role r on ( d.emp_id = r.emp_id )
)
SELECT emp_id, emp_name, emp_age, count(distinct emp_address) emp_addr_count,
min(emp_address) emp_address_1, max(emp_city) emp_city_1,
decode(min(emp_address),max(emp_address),null,max(emp_address)) emp_address_2,
decode(min(emp_city),max(emp_city),null,min(emp_city)) emp_city_2,
count(distinct emp_task) emp_role_count, min(emp_task) emp_task_1,
decode(min(emp_task),max(emp_task),null,max(emp_task)) emp_task_2
FROM t
GROUP BY emp_id, emp_name, emp_age
ORDER BY emp_id;
SQL Fiddle Demo

Finding the highest average salary of a department using joins in 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:

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
)

SQL - GROUP BY and COUNT

I have a table with Column - D and E.
I want to get D, Distinct E in each D, Count of total number of entry for each D. How to write SQL for this ?
Data:
D | E
-----
1 | K
1 | K
1 | A
2 | S
2 | S
2 | S
2 | S
Desired o/p:
D | E | Total_E_in_D
----------------------
1 | K | 3
1 | A | 3
2 | S | 4
SELECT D,E,Count(E in each D)
FROM table
GROUP BY D,E.
Last column should give me the total number of entries for each D.
The specific answer to the question is:
select dept, count(*) as numemployees, count(distinct emp) as numDistinctEmployees
from d1
group by dept;
This just seems quite unusual, because the it assumes that employees would be in the same department more than once.
EDIT:
Strange data format, but just use aggregation with analytic functions:
select dept, emp, sum(count(*)) over (partition by dept) as numEmployees
from d1
group by dept, emp;
You can group on the department and the employee, and join in a query where you group on the department to count the employees:
select
e.Dept,
e.Emp
d.EmpCount
from
table e
inner join (
select
Dept,
count(distinct Emp) as EmpCount
from
table
group by
Dept
) d on d.Dept = e.Dept
group by
e.Dept, e.Emp
You could also use a subquery to count the employees:
select
e.Dept,
e.Emp,
(
select
count(distinct Emp)
from
table d
where
d.Dept = e.Dept
) as EmpCount
from
table e
group by
e.Dept, e.Emp
how's this?
select dept, emp, (select count(*) from table t2 where t2.dept = t1.dept) noEmps
from table t1

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