Retrieving MAX(date) value from joined table - sql

I need help on a very basic SQL join concept, that I just can't seem to get right.
I have an employee table and a position table. The employee table is like so:
EmpID Name
1 Jane Jones
2 Bob Smith
3 Jim Adams
The position table is like this:
PosID EmpID Position DateFilled
1 1 Sales 1/2/2012
2 2 HR 4/5/2013
3 2 Mgmnt 6/1/2014
4 2 Sr. Mgmnt 7/5/2015
5 3 IT Support 4/6/2014
6 3 IT Devel. 5/11/2015
How can I get the following output:
EmpID Name Position DateFilled
1 Jane Jones Sales 1/2/2012
2 Bob Smith Sr. Mgmnt 7/5/2015
3 Jim Adams IT Devel. 5/11/2015
So, in other words, how do I join to get only the record with the max DateFilled column from the position table to join with the corresponding record in the employee table?
Any assistance would be greatly appreciated.

You can use ROW_NUMBER:
SELECT e.EmpID, e.Name, p.Position, p.DateFilled
FROM employee e
LEFT JOIN (
SELECT EmpID, Position, DateFilled,
ROW_NUMBER() OVER (PARTITION BY EmpID
ORDER BY DateFilled DESC) AS rn
FROM position
) p ON e.EmpID = p.EmpID AND p.rn = 1

You can do it using MAX() KEEP ( DENSE_RANK [FIRST|LAST] ... ) like this:
SELECT e.EmpId,
e.Name,
p.position,
p.datefilled
FROM employee e
INNER JOIN (
SELECT EmpID,
MAX( Position ) KEEP ( DENSE_RANK LAST ORDER BY DateFilled ) AS Position,
MAX( DateFilled ) AS DateFilled
FROM position
GROUP BY EmpID
) p
ON ( e.EmpId = p.EmpID );

try this
select temp.EmpID,(select position from Position where PosID =temp.PosID) position,DateFilled,Name from
(select EmpID,max(PosID) PosID,max(DateFilled) DateFilled
from position group by EmpID ) temp
inner join employee emp on emp.EmpID =temp.EmpID

Related

How to join result table with existing table, on repeating values?

I want to Join result table if the emp_id is repeated. and display employees first name.
result table code:
SELECT emp_id, COUNT(*)
FROM Employment
GROUP BY emp_id
HAVING COUNT(*) > 1
Employment table:
emp_id
task_complete
year
1
task1
2020
1
task2
2020
3
task3
1999
4
task4
2001
Employee table:
emp_id
first_name
last_name
1
Jon
Doe
2
Don
Juan
3
Steve
Works
4
Loki
Odinsen
So that employee 1 has done 2 tasks, Result should look like:
emp_id
first_name
tasks_done
1
Jon
2
This looks like group by and having:
select e.emp_id, e.first_name, count(*)
from employee e join
tasks t
on e.emp_id = t.emp_id
group by e.emp_id, e.first_name
having count(*) > 1;

Select max date from joined tables with columns that has unique values

I want to get the most recent date from joined tables with unique values in 2 columns. How do I do this? I have also tried the ranking (but John has the same rank) and tried rownum = 1, but I still get the same results below for some reason
Name ID Email DeptNo DeptScore OnDate
John A46 john#doe.com 100 50 5/11/2011
John A46 johndoe#aol.com 200 75 7/21/2015
Alice B33 alice#hotmail.com 100 50 4/15/2014
I want to get the following:
Name ID Email DeptNo DeptScore OnDate
John A46 johndoe#aol.com 200 75 7/21/2015
Alice B33 alice#hotmail.com 100 50 4/15/2014
My query
select distinct e.name, e.id, e.email, d.deptno, d.deptscore, d.ondate
from
emp e
inner join dept d on
d.deptno = e.dnum
and d.ondate = e.livedate
and d.ondate = (select max(m.ondate)
from dept m
where d.ondate = m.ondate)
--where e.id in ('A46','B33')
Try to use the below Query, it will solve your problem.
select name,
id,
email,
deptno,
deptscore,
ondate
from (select e.name,
e.id,
e.email,
d.deptno,
d.deptscore,
d.ondate,
rank() over(partition by e.id,e.name order by d.ondate desc) rn
from emp e join
dept d
on d.deptno = e.dnum and d.ondate = e.livedate
) s
where rn = 1;
Output:
NAME ID EMAIL DEPTNO DEPTSCORE ONDATE
John A46 johndoe#aol.com 200 75 21-JUL-15
Alice B33 alice#hotmail.com 100 50 15-APR-14

T-SQL - Concatenate Multiple Rows

I am struggling with a query to return a list of managers with their respective employees
I have three tables as follows:
Managers
ManagerID ManagerName
1 Bob
2 Sally
3 Peter
4 George
EmployeeManager
EmployeeID ManagerID
1 1
1 1
2 2
2 2
3 3
3 3
4 4
4 4
Employees
EmployeeID EmployeeName
1 David
1 Joseph
2 Adam
2 Pete
3 Mark
3 Mavis
4 Susan
4 Jennifer
Desired Result Set
ManagerName CountEmployee Employees
Bob 2 David, Joseph
Sally 2 Anish, Pete
Peter 2 Mark, Mavis
George 2 Susan, Jennifer
The query I am currently using is as follows:
Select m.ManagerName
,Count(e.EmployeeName) Over(Partition By m.ManagerID) as CountEmployee
,Rank() Over(Partition By m.ManagerID Order By em.EmployeeID) [RankEmployee]
,e.EmployeeName
From dbo.Employees e
Left Join dbo.EmployeeManager em on em.ManagerID=e.ManagerID
Left Join dbo.Managers m on m.ManagerID=em.ManagerID;
This returns a list of managers and employees on individual rows but I'm struggling to concatenate the employee names as per the above table.
Any ideas or solutions?
Manpaal Singh
You can stuff the result to comma seperated result.
Select m.ManagerName
,Count(e.EmployeeName) Over(Partition By m.ManagerID) as CountEmployee
,Rank() Over(Partition By m.ManagerID Order By em.EmployeeID) [RankEmployee]
,STUFF((SELECT ',' + e.EmployeeName
FOR XML PATH('')), 1, 1, '') AS EmployeeName
From dbo.Employees e
Left Join dbo.EmployeeManager em on em.ManagerID=e.ManagerID
Left Join dbo.Managers m on m.ManagerID=em.ManagerID
SELECT M.ManagerName, E.EmployeeName
FROM Managers AS M
INNER JOIN EmployeeManager AS EM ON M.ManagerID = EM.ManagerID
INNER JOIN Employees AS E ON EM.EmployeeID = E.EmployeeID
ORDER BY M.ManagerName
This will give a list of managers and their employees.
when you fix the ID's of the employees table.
1 David
1 Joseph
2 Adam
2 Pete
3 Mark
3 Mavis
4 Susan
4 Jennifer
should be:
1 David
2 Joseph
3 Adam
4 Pete
5 Mark
6 Mavis
7 Susan
8 Jennifer
you can use recursive sql to convert rows in a string :
with t1 (mngId, empName) as (
select a.mngId,
b.empname
from manager as a, employee as b
where b.mngId = a.mngId),
t2 (mngID, nbr, empName, all_name) as (
select mngId,
cast(1 as Int),
min(empName),
cast(min(empName) as varchar(1000)
from t1
group by mngId
union all
select b.mngId,
b.nbr+1,
a.empName,
trim(b.all_name) concat ', ' concat a.empName
from t0 as a, t1 as b
where b.mngId = a.mngId
and a.empName > b.empName
and a.empName = (
select min( c.empName)
from t0 as c
where c.mngId = b.mngId
and c.empName > b.empName )
)
select *
from t1 as e
where nbr = (
select max(nbr)
from t1 as d
where d.mngId = e.mngId )

oracle duplicate rows based on a single column

How can I find out duplicate rows based on a single column. I have a table in oracle which has data as given below and it has duplicates. I'm trying to select and view all rows with duplicate employee ids as explained below
EMP table:
EmpId Fname Lname Mname Jobcode Status exp_date
1 Mike Jordan A IT W 12/2014
1 Mike Jordan A IT A 12/2014
2 Angela ruth C sales P 12/2015
2 Angela ruth C IT W 12/2015
3 Kelly Mike B sales W 12/2015
From the above table i want to select all rows which duplicate empids such as below
EmpId Fname Lname Mname Jobcode Status exp_date
1 Mike Jordan A IT W 12/2014
1 Mike Jordan A IT A 12/2014
2 Angela ruth C sales P 12/2015
2 Angela ruth C IT W 12/2015
How can I do this? thank you!
SELECT a.*
FROM TableName a
INNER JOIN
(
SELECT EmpID
FROM TableName
GROUP BY EmpID
HAVING COUNT(*) > 1
) b ON a.EmpID = b.EmpID
SQLFiddle Demo
Another way, although I prefer above, is to use IN
SELECT a.*
FROM TableName a
WHERE EmpId IN
(
SELECT EmpId
FROM TableName
GROUP BY EmpId
HAVING COUNT(*) > 1
)
SQLFiddle Demo
Here's another option using a subquery and COUNT OVER PARTITION BY since you're using Oracle 11:
SELECT *
FROM (
SELECT EmpId, Fname, Lname, Mname, Jobcode, Status, exp_date,
COUNT(EmpId) OVER (PARTITION BY EmpId) EmpCount
FROM TableName
) T
WHERE EmpCount > 1
SQL Fiddle Demo (Borrowed from JW)

SQL INNER JOIN return parent row values only on first match

Folks,
All am looking for is to extend the INNER JOIN. Let me just get to the point. I have two tables Dept & Emp. One Dept can have multiple Emp's & not the other way around.
Table Dept
Dept_id Dept_Name
1 IT
2 HR
3 Other
Table Emp
Emp_id Dept_id Emp_Name
11 1 John
12 1 Jill
13 2 Jack
14 3 Jared
15 1 Jim
16 1 Jarret
17 2 Jacob
I need to JOIN it on Dept_id
Expected Results
Dept_id Dept_name Emp_id Emp_Name
1 IT 11 John
NULL NULL 12 Jill
NULL NULL 15 Jim
NULL NULL 16 Jarret
2 HR 13 Jack
NULL NULL 17 Jacob
3 Other 14 Jared
Hope I conveyed what i want precisely. Its just a regular Inner Join on a Foreign Key Constraint. But, I want the values from the First Table (Dept) to be NULL-ed except for the First Match. That being said, I don't care what's the first match. See it below - Just the result for the Dept_id 1.
Expected Results (Only for Dept_id = 1)
It could be
Dept_id Dept_name Emp_id Emp_Name
1 IT 11 John
NULL NULL 12 Jill
NULL NULL 15 Jim
NULL NULL 16 Jarret
OR
Dept_id Dept_name Emp_id Emp_Name
1 IT 15 Jim
NULL NULL 12 Jill
NULL NULL 11 John
NULL NULL 16 Jarret
OR
Two other possibilities.
Thanks in advance. Sorry for the long explanation even though its a simple case.
I agree with Ic. that this is a crazy query that seems to be doing something that your application should be doing instead of your sql query, that being said, it's fun to write those queries :)
SELECT CASE WHEN RowNumber = 1 THEN Dept_id ELSE NULL END AS Dept_id,
CASE WHEN RowNumber = 1 THEN Dept_name ELSE NULL END AS Dept_name,
Emp_id, Emp_Name
FROM Dept d
INNER JOIN
(SELECT ROW_NUMBER() OVER (PARTITION BY Dept_id ORDER BY Emp_Name) AS RowNumber,
Dept_id, Dept_name, Emp_id, Emp_Name
FROM Emp ) t on t.Dept_id = d. Dept_id
select case
when lag(d.dept_id) over (partition by d.dept_id order by e.emp_id) = d.dept_id then null
else d.dept_id
end as dept_id,
case
when lag(d.dept_name) over (partition by d.dept_id order by e.emp_id) = d.dept_name then null
else d.dept_name
end as dept_name,
e.emp_id,
e.emp_name
from dept d
join emp e on e.dept_id = d.dept_id
order by d.dept_id, d.dept_name, e.emp_id
Honestly, I'm not really sure why you want this result. Formatting is usually best left for the UI layer and NOT your database server. Doing it this way makes the data look like the non-first-employees for each department actually do not have a department, and it fundamentally breaks any sorting or editing functions you may have in your client.
However, you could try:
SELECT FormattedDept.Dept_id, FormattedDept.Dept_Name, Emp.Emp_id, Emp.Emp_Name
FROM Emp
LEFT OUTER JOIN
(
SELECT Dept.Dept_id, Dept.Dept_Name, MIN(Emp_id) AS Emp_id
FROM Dept
INNER JOIN Emp ON Dept.Dept_id = Emp.Dept_id
GROUP BY Dept.Dept_id, Dept.Dept_Name
) FormattedDept ON Emp.Dept_id = FormattedDept.Dept_id
AND Emp.Emp_id = FormattedDept.Emp_id
ORDER BY Emp.Dept_id, Emp.Emp_id
SQL Fiddle Demo
As I mention in my comment above, I'm not sure that you would really want the results to return in the way you are asking. Consider this. If the result set was exported to Excel, and a user changed the sort, then you would lose what dept employee 1-x was in and employee 0 would still show their department. I would suggest you group by Dept_id, and not null out the dept_id and dept_name. Handle the display of that data in your display code.
WITH newRecord
AS
(
SELECT "Emp_id", "Dept_id", "Emp_Name",
ROW_NUMBER() OVER (PARTITION BY "Dept_id" ORDER BY "Emp_id" ASC) RN
FROM Emp
)
SELECT CASE WHEN RN = 1 THEN b."Dept_id" ELSE NULL END AS "Dept_ID",
CASE WHEN RN = 1 THEN b."Dept_Name" ELSE NULL END AS "Dept_Name",
"Emp_id", "Emp_Name"
FROM newRecord a
INNER JOIN Dept b ON a."Dept_id" = b."Dept_id"
SQLFiddle Demo
because i cant think of a reason you would want that other then reporting purposes then you can also do this by using SQL*Plus formmating.
just run the following command before the query in SQL*Plus
break on dept_id skip 1
Enjoy.