60

I have the below table with the below records in it

create table employee
(
 EmpId number,
 EmpName varchar2(10),
 EmpSSN varchar2(11)
);

insert into employee values(1, 'Jack', '555-55-5555');
insert into employee values (2, 'Joe', '555-56-5555');
insert into employee values (3, 'Fred', '555-57-5555');
insert into employee values (4, 'Mike', '555-58-5555');
insert into employee values (5, 'Cathy', '555-59-5555');
insert into employee values (6, 'Lisa', '555-70-5555');
insert into employee values (1, 'Jack', '555-55-5555');
insert into employee values (4, 'Mike', '555-58-5555');
insert into employee values (5, 'Cathy', '555-59-5555');
insert into employee values (6 ,'Lisa', '555-70-5555');
insert into employee values (5, 'Cathy', '555-59-5555');
insert into employee values (6, 'Lisa', '555-70-5555');

I dont have any primary key in this table .But i have the above records in my table already. I want to remove the duplicate records which has the same value in EmpId and EmpSSN fields.

Ex : Emp id 5

How can I frame a query to delete those duplicate records?

starball
  • 20,030
  • 7
  • 43
  • 238
Shyju
  • 214,206
  • 104
  • 411
  • 497

20 Answers20

85

It is very simple. I tried in SQL Server 2008

DELETE SUB FROM
(SELECT ROW_NUMBER() OVER (PARTITION BY EmpId, EmpName, EmpSSN ORDER BY EmpId) cnt
 FROM Employee) SUB
WHERE SUB.cnt > 1
Flea
  • 11,176
  • 6
  • 72
  • 83
Anjib Rajkhowa
  • 851
  • 6
  • 2
  • 2
    This works well when you have a lot of columns to group by, and it neatly deals with the NULL != NULL when comparing two columns. You don't have to list each column twice like some of the other answers ("a.col = b.col" type thing), and even more importantly, you don't have to check "((a.col = b.col) OR (a.col IS NULL AND b.col IS NULL))" on NULL columns. – Bryce Wagner Apr 03 '13 at 19:58
  • 6
    This answer actually resolves the problem, without structural changes. Works perfectly. – StuckOverflow Jan 28 '14 at 17:50
60

Add a Primary Key (code below)

Run the correct delete (code below)

Consider WHY you woudln't want to keep that primary key.


Assuming MSSQL or compatible:

ALTER TABLE Employee ADD EmployeeID int identity(1,1) PRIMARY KEY;

WHILE EXISTS (SELECT COUNT(*) FROM Employee GROUP BY EmpID, EmpSSN HAVING COUNT(*) > 1)
BEGIN
    DELETE FROM Employee WHERE EmployeeID IN 
    (
        SELECT MIN(EmployeeID) as [DeleteID]
        FROM Employee
        GROUP BY EmpID, EmpSSN
        HAVING COUNT(*) > 1
    )
END
abatishchev
  • 98,240
  • 88
  • 296
  • 433
cjk
  • 45,739
  • 9
  • 81
  • 112
  • 9
    +1: to quote some SQL god: "if it doesn't have a primary key, it's not a table" – marc_s Jun 12 '09 at 07:26
  • 3
    +1 A primary key identifies a row. No PK = no sense. @marc_s: a clustered index differentiates a table from a heap. No PK simply means no data integrity – gbn Jun 12 '09 at 07:28
  • @gbn: even a heap is considered a table :-) This quote was more along the lines: unless you specify a primary key, a table really doesn't have much usefulness (except in edge cases like bulk import / temporary tables etc.) – marc_s Jun 12 '09 at 07:57
  • even in those edge cases I almost always add a primary key, just so I can delete dupped recrds if need be. – HLGEM Jun 12 '09 at 17:05
  • 1
    Looks like the duplicate removal is being done so the EmpID can be the primary key. The other data seems dependant on it. – Stu Pegg Oct 06 '09 at 11:59
  • I had the same problem, stupid 2 column table, not paying attention. You saved me! – MAW74656 Jun 09 '11 at 20:31
  • This would delete the duplicate and the non-duplicate row. I believe Nirav Parikh's solution will only delete the duplicate row while preserving the original row. – Registered User Jan 12 '12 at 00:59
24

Use the row number to differentiate between duplicate records. Keep the first row number for an EmpID/EmpSSN and delete the rest:

    DELETE FROM Employee a
     WHERE ROW_NUMBER() <> ( SELECT MIN( ROW_NUMBER() )
                               FROM Employee b
                              WHERE a.EmpID  = b.EmpID
                                AND a.EmpSSN = b.EmpSSN )
Paul Morgan
  • 31,226
  • 3
  • 24
  • 27
12
With duplicates

As
(Select *, ROW_NUMBER() Over (PARTITION by EmpID,EmpSSN Order by EmpID,EmpSSN) as Duplicate From Employee)

delete From duplicates

Where Duplicate > 1 ;

This will update Table and remove all duplicates from the Table!

John Conde
  • 217,595
  • 99
  • 455
  • 496
Nirav Parikh
  • 121
  • 1
  • 2
8
select distinct * into newtablename from oldtablename

Now, the newtablename will have no duplicate records.

Simply change the table name(newtablename) by pressing F2 in object explorer in sql server.

askmish
  • 6,464
  • 23
  • 42
8

Code

DELETE DUP 
FROM 
( 
    SELECT ROW_NUMBER() OVER (PARTITION BY Clientid ORDER BY Clientid ) AS Val 
    FROM ClientMaster 
) DUP 
WHERE DUP.Val > 1

Explanation

Use an inner query to construct a view over the table which includes a field based on Row_Number(), partitioned by those columns you wish to be unique.

Delete from the results of this inner query, selecting anything which does not have a row number of 1; i.e. the duplicates; not the original.

The order by clause of the row_number window function is needed for a valid syntax; you can put any column name here. If you wish to change which of the results is treated as a duplicate (e.g. keep the earliest or most recent, etc), then the column(s) used here do matter; i.e. you want to specify the order such that the record you wish to keep will come first in the result.

JohnLBevan
  • 22,735
  • 13
  • 96
  • 178
  • Welcome to Stack Overflow! Code only answers are not very useful on their own. It would help if you could add some detail explaining how/why it answers the question. – SiHa Sep 27 '16 at 15:24
  • 1
    I was surprised to learn you *can* delete rows from an alias (or a view), and when you do this, the corresponding row(s) will be deleted from the underlying table! I read more about ["updatable views" here](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-view-transact-sql?redirectedfrom=MSDN&view=sql-server-ver15#updatable-views) - "You can modify the data of an underlying base table through a view, as long as the following conditions are true..." – Nate Anderson Aug 10 '21 at 20:38
7

You could create a temporary table #tempemployee containing a select distinct of your employee table. Then delete from employee. Then insert into employee select from #tempemployee.

Like Josh said - even if you know the duplicates, deleting them will be impossile since you cannot actually refer to a specific record if it is an exact duplicate of another record.

Daren Thomas
  • 67,947
  • 40
  • 154
  • 200
  • 2
    Only trick there is if the names are different but the ID/SSN match. You'd have to somehow pick one because distinct wouldn't help there. – Josh Jun 12 '09 at 07:19
  • 1
    +1 this is the most straightforward and portable solution. OP does not state what brand of database he uses. – Bill Karwin Jun 12 '09 at 07:22
  • @Josh: from the OP's sample, it looks like that's not an issue. The duplicate rows are identical in all columns. – Bill Karwin Jun 12 '09 at 07:23
2

If you don't want to create a new primary key you can use the TOP command in SQL Server:

declare @ID int
while EXISTS(select count(*) from Employee group by EmpId having count(*)> 1)
begin
    select top 1 @ID = EmpId
    from Employee 
    group by EmpId
    having count(*) > 1

    DELETE TOP(1) FROM Employee WHERE EmpId = @ID
end
Joe
  • 334
  • 3
  • 3
2

ITS easy use below query

WITH Dups AS
(
  SELECT col1,col2,col3,
ROW_NUMBER() OVER(PARTITION BY col1,col2,col3 ORDER BY (SELECT 0)) AS rn
 FROM mytable
)
DELETE FROM Dups WHERE rn > 1
Abhishek Jaiswal
  • 1,161
  • 12
  • 6
1

delete sub from (select ROW_NUMBER() OVer(Partition by empid order by empid)cnt from employee)sub where sub.cnt>1

Sudhar P
  • 11
  • 1
  • Welcome to stackoverflow. This is an old question with a well established answer. IF you believe your answer adds something significant and new, please expand it with more explanation. – Simon.S.A. Nov 28 '18 at 03:45
0

no ID, no rowcount() or no temp table needed....

WHILE 
  (
     SELECT  COUNT(*) 
     FROM TBLEMP  
     WHERE EMPNO 
            IN (SELECT empno  from tblemp group by empno having count(empno)>1)) > 1 


DELETE top(1)  
FROM TBLEMP 
WHERE EMPNO IN (SELECT empno  from tblemp group by empno having count(empno)>1)
Praveen Nambiar
  • 4,852
  • 1
  • 22
  • 31
0

there are two columns in the a table ID and name where names are repeating with different IDs so for that you may use this query: . .

DELETE FROM dbo.tbl1
WHERE id NOT IN (
     Select MIN(Id) AS namecount FROM tbl1
     GROUP BY Name
)
Jens Kloster
  • 11,099
  • 5
  • 40
  • 54
0

Having a database table without Primary Key is really and will say extremely BAD PRACTICE...so after you add one (ALTER TABLE)

Run this until you don't see any more duplicated records (that is the purpose of HAVING COUNT)

DELETE FROM [TABLE_NAME] WHERE [Id] IN 
(
    SELECT MAX([Id])
    FROM [TABLE_NAME]
    GROUP BY [TARGET_COLUMN]
    HAVING COUNT(*) > 1
)


SELECT MAX([Id]),[TABLE_NAME], COUNT(*) AS dupeCount
FROM [TABLE_NAME]
GROUP BY [TABLE_NAME]
HAVING COUNT(*) > 1

MAX([Id]) will cause to delete latest records (ones added after first created) in case you want the opposite meaning that in case of requiring deleting first records and leave the last record inserted please use MIN([Id])

d1jhoni1b
  • 7,497
  • 1
  • 51
  • 37
0
create unique clustered index Employee_idx
on Employee ( EmpId,EmpSSN )
with ignore_dup_key

You can drop the index if you don't need it.

Anil
  • 387
  • 2
  • 24
Sudhir
  • 1
0

Let's think out of the box.

I don't delete from the table, I make a new table first, for safety. I personally prefer do a

INSERT INTO new_table SELECT DISTINCT * FROM orig_table;

Now, new_table now should contains the expected data I want. I can check new_table to ensure that.

Then I have 2 options to replace the orig_table

A. delete orig_table; rename new_table to orig_table

B. truncate orig_table; insert data from new_table to orig_table; delete new_table (Recommended: in case you have some trigger/something else linked to the original orig_table)

  • Good idea, but kind of a duplicate of https://stackoverflow.com/a/11119012/1260022 – D-S Mar 12 '22 at 07:39
0

I'm not an SQL expert so bear with me. I'm sure you'll get a better answer soon enough. Here's how you can find the duplicate records.

select t1.empid, t1.empssn, count(*)
from employee as t1 
inner join employee as t2 on (t1.empid=t2.empid and t1.empssn = t2.empssn)
group by t1.empid, t1.empssn
having count(*) > 1

Deleting them will be more tricky because there is nothing in the data that you could use in a delete statement to differentiate the duplicates. I suspect the answer will involve row_number() or adding an identity column.

Josh
  • 68,005
  • 14
  • 144
  • 156
-1
select t1.* from employee t1, employee t2 where t1.empid=t2.empid and t1.empname = t2.empname and t1.salary = t2.salary
group by t1.empid, t1.empname,t1.salary having count(*) > 1
Freelancer
  • 9,008
  • 7
  • 42
  • 81
-1

delete from employee where rowid in (select rowid from (select rowid, name_count from (select rowid, count(emp_name) as name_count from employee group by emp_id, emp_name) where name_count>1))

-2
DELETE FROM 'test' 
USING 'test' , 'test' as vtable
WHERE test.id>vtable.id and test.common_column=vtable.common_column  

Using this we can remove duplicate records

The_Fox
  • 6,992
  • 2
  • 43
  • 69
-3
ALTER IGNORE TABLE test
           ADD UNIQUE INDEX 'test' ('b'); 

@ here 'b' is column name to uniqueness, @ here 'test' is index name.

Anil
  • 387
  • 2
  • 24