The basic operation here is to first select all but one row out of a set of duplicates then use that as a subselect to a delete statement. Then when you're done you put a unique constraint / index on that row so you don't have to do this again.
I'll be using pgsql syntax. You'll have to translate to other dbs.
create table mytable (info text);
insert into mytable values ('abc');
insert into mytable values ('abc');
insert into mytable values ('def');
insert into mytable values ('def');
insert into mytable values ('def');
insert into mytable values ('hij');
select * from mytable;
info
------
abc
abc
def
def
def
hij
(6 rows)
As you can see we have dupes in the info field. Now, we'll modify this table to give it a unique value for each row in a new column. If your table already has a unique field then you can skip this part, obviously
alter table mytable add unid int;
create temp sequence t;
update mytable set unid=nextval('t');
UPDATE 6
select * from mytable ;
info | unid
------+------
abc | 1
abc | 2
def | 3
def | 4
def | 5
hij | 6
select distinct a.unid from mytable a join mytable b on (a.unid > b.unid and a.info=b.info);
unid
------
2
4
5
(3 rows)
Now that we have the "extra" rows listed in that select, We can delete them. I'll do this in a transaction in case things go wrong.
e=# begin;
BEGIN
delete from mytable where unid in (select distinct a.unid from mytable a join mytable b on (a.unid > b.unid and a.info=b.info));
DELETE 3
select * from mytable ; info | unid
------+------
abc | 1
def | 3
hij | 6
(3 rows)
-- Looks good let's commit:
commit;
COMMIT
Easy, eh?