oracle duplicate rows based on a single column - sql

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)

Related

Update using joins by finding the previous value in sql

I have two tables and I have to update the address field in the emp table by looking up in the emphistory table with previous value i.e USA for employee John
Table emp
EId ename sal Address AccountId
-------------------------------
101 John 100 U X12
102 Peter 500 Null X13
Table emphistory
emphisid EId AccountId Address Date (use row_number to find the second record for that eid and accountid)
-----------------------------------------------------
1 101 X12 U 11-01-2020 09:45:00
2 102 X13 Null 11-01-2020 09:46:00
3. 101 X12 USA 11-01-2020 09:30:00
I have to join the tables with account id and eid.
This works in Postgresql & Sql Server
UPDATE emp
SET Address = hist.Address
FROM (
SELECT h.EId, h.AccountId, h.Address
, ROW_NUMBER() OVER (PARTITION BY h.EId, h.AccountId
ORDER BY h.Date DESC) AS Rn
FROM emphistory h
JOIN emp e
ON e.EId = h.EId
AND e.AccountId = h.AccountId
WHERE h.Address IS NOT NULL
) hist
WHERE emp.EId = hist.EId
AND hist.Rn = 2

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 )

Retrieving MAX(date) value from joined table

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

selecting a row using MIN or ROWNUM

I have a oracle table which is similar to the one below which stores people's lastname firstname and age. If last name is same people belong to same family.
LastName FirstName Age
===========================
1 miller charls 20
2 miller john 30
3 anderson peter 45
4 Bates andy 50
5 anderson gary 60
6 williams mark 15
I need to write a oracle sql query to
select youngest person from each family. output shd select rows 1,3,4 and 6
How do I do this ?
Another way, a bit shorter:
select lastname
, max(firstname) keep(dense_rank first order by age) as first_name
, max(age) keep(dense_rank first order by age) as age
from you_table_name
group by lastname
order by lastname
Result:
LASTNAME FIRST_NAME AGE
-------- ---------- ----------
Bates andy 50
anderson peter 45
miller charls 20
williams mark 15
And SQLFiddle Demo
DENSE_RANK() is a ranking function which generates sequential number and for ties the number generated is the same. I prefer to use DENSE_RANK() here considering that a family can have twins, etc.
SELECT Lastname, FirstName, Age
FROM
(
SELECT Lastname, FirstName, Age,
DENSE_RANK() OVER (PARTITION BY LastName ORDER BY Age) rn
FROM tableName
) a
WHERE a.rn = 1
SQLFiddle Demo
With Standard SQL I would do as this...
select *
from family f1
where (
select count(*)
from family f2
where
f2.lastname = f1.lastname
and
f2.age <= f1.age) <= 1
order by lastname;
This SQL gives you possibilities to pick x youngest/oldest in a family. Just modify the f2.age <= f1.age to e.g. f2.age >= f1.age, and the <= 1 to e.g. <=10 (to get top 10 youngest/oldest in a family).
SQLfiddle

SQL: Select rows with a column value that occurs at least N times?

Suppose I have a SQL table "Celebrities" with two columns: "fname" and "lname":
fname | lname
---------+-------
Bill | Clinton
Bill | Gates
George | Bush
George | Clinton
Barack | Obama
I would like to write a query that returns the first and last name of each person in the table whose last name appears at least twice in the column "lname". How do I write this SQL query?
SELECT fname, lname FROM Celebrities
WHERE lname IN
(SELECT lname FROM Celebrities
GROUP BY lname HAVING COUNT (lname) >1)
Using a JOIN:
SELECT a.*
FROM CELEBRITIES a
JOIN (SELECT c.lname
FROM CELEBRITIES c
GROUP BY c.lname
HAVING COUNT(*) >= 2) b ON b.lname = a.lname
Using EXISTS:
SELECT a.*
FROM CELEBRITIES a
WHERE EXISTS (SELECT NULL
FROM CELEBRITIES c
WHERE c.lname = a.lname
GROUP BY c.lname
HAVING COUNT(*) >= 2)
select fname, lname
from
(
select fname, lname, count(*) over(partition by lname) as lcount
from Celebrities
) as S
where lcount > 1
Tested in SQL Server 2008. Might work in other DBMS that support count(*) over(...)