2009-07-24 10 views
6

Est-ce que quelqu'un a une instruction sql élégante pour supprimer les enregistrements en double d'une table, mais seulement s'il y a plus de x nombre de doublons? Donc, il permet jusqu'à 2 ou 3 doublons, mais c'est tout?Requête SQL - Supprimer les doublons si plus de 3 tours?

Actuellement, j'ai une instruction select qui effectue les opérations suivantes:

delete table 
from table t 
left outer join (
select max(id) as rowid, dupcol1, dupcol2 
from table 
group by dupcol1, dupcol2 
) as keeprows on t.id=keeprows.rowid 
where keeprows.rowid is null 

Cela fonctionne très bien. Mais maintenant, ce que je voudrais faire est seulement de supprimer ces lignes si elles ont plus de 2 doublons.

Merci

+0

quand il y a 5 doublons, voulez-vous seul à gauche après la suppression, ou trois? – Stobor

Répondre

7
with cte as (
    select row_number() over (partition by dupcol1, dupcol2 order by ID) as rn 
    from table) 
delete from cte 
    where rn > 2; -- or >3 etc 

La requête est la fabrication d'un 'numéro de ligne' pour chaque enregistrement, regroupés par le (dupcol1, dupcol2) et commandé par ID . En effet ce numéro de ligne compte les 'doublons' qui ont les mêmes dupcol1 et dupcol2 et affecte ensuite le numéro 1, 2, 3 .. N, ordre par ID. Si vous voulez garder seulement 2 doublon, vous devez supprimer ceux qui ont été affectés les numéros 3,4,.. N et qui est la partie prise en charge par le DELLETE.. WHERE rn > 2;

En utilisant cette méthode, vous pouvez changer le ORDER BY en fonction de votre préféré ordre (par exemple ORDER BY ID DESC), de sorte que le LATEST a rn=1, puis le prochain est rn = 2 et ainsi de suite. Le reste reste le même, le DELETE ne supprimera que les plus anciens car ils ont les numéros de ligne les plus élevés.

Contrairement à this closely related question, à mesure que la condition devient plus complexe, l'utilisation de CTE et row_number() devient plus simple. Les performances peuvent être problématiques si aucun index d'accès approprié n'existe.

+0

Merci Remus, mais comme je ne suis pas un expert sql et pas aussi familier avec les mots-clés spécifiques de 2005, pourriez-vous m'expliquer ce que fait la requête? Je suppose que la partition est un bon raccourci pour joindre à gauche à une table groupée, semblable à mon premier exemple ?? Donc, votre deuxième ligne retourne un nouvel identifiant de tous les enregistrements en double en fonction des colonnes fournies? Est-ce que rn est le nombre de fois que la ligne a été dupliquée en fonction des colonnes de la deuxième ligne? Merci. – Scott

+1

La requête est la fabrication d'un «numéro de ligne» pour chaque enregistrement, regroupés par (dupcol1, dupcol2) et classés par ID. En effet ce numéro de ligne compte les 'doublons' qui ont les mêmes dupcol1 et dupcol2 et affecte ensuite le numéro 1, 2, 3 .. N, ordre par ID. Si vous voulez garder seulement 2 'doublons', alors vous devez supprimer ceux qui ont été assignés les nombres 3,4, .. N et c'est la partie prise en charge par le DELLETE .. WHERE rn> 2; HTH, laissez-moi savoir si est encore incertain. –

+0

Non, je l'ai eu, merci beaucoup. Une dernière chose cependant, je veux m'assurer que je garde toujours le record le plus récent. Donc, si je garde des enregistrements avec say <2 duplicates, puis que j'envoie tous les autres, comment puis-je modifier la requête pour m'assurer que les deux ou trois enregistrements les plus récents (max (id)) de la table sont conservés. À titre d'exemple: disons qu'un enregistrement est listé dans notre système 10 fois. Cela viole la règle des doublons "2". Nous aimerions supprimer 7 des doublons, ne laissant qu'une seule fiche et deux copies. Par enregistrement, nous entendons l'enregistrement le plus récent (le plus à jour) qui est entré dans le système. – Scott

3

HAVING est votre ami

select id, count(*) cnt from table group by id having cnt>2

0

assez tard, mais la solution la plus simple pourrait être comme suit supposons que nous avons la table emp_dept (empid, deptID) qui a des lignes en double, Ici, je l'ai utilisé comme @Count varibale .. par exemple 2 dupliqués permis alors @count = 2 Sur la base de données Oracle

delete from emp_dept where @Count <= (select count(1) from emp_dept i where i.empid = emp_dept.empid and i.deptid = emp_dept.deptid and i.rowid < emp_dept.rowid) 

Sur le serveur SQL ou anydatabase qui ne supporte pas id ligne disposent d'un peu, nous avons besoin d'ajouter colonne d'identité juste pour identifier chaque ligne. disent que nous avons ajouté nid que l'identité de la table

alter table emp_dept add nid int identity(1,1) -- to add identity column 

maintenant requête pour supprimer les doublons pourrait être écrite comme

delete from emp_dept where @@Count <= (select count(1) from emp_dept i where i.empid = emp_dept.empid and i.deptid = emp_dept.deptid and i.nid< emp_dept.nid) 

Ici, le concept est de supprimer toutes les lignes pour lesquelles il existe d'autres lignes qui ont la même valeurs de base mais n ou plus grand nombre de plus petit rowid ou identité. Par conséquent, s'il existe des lignes dupliquées, une ligne ayant une identification ou une identité de ligne supérieure sera supprimée. et pour la ligne il n'y a pas de doublon, il échouera dans la recherche de l'identifiant de la rangée inférieure, donc ne sera pas supprimé.

0

Pour Oracle:

delete from test where rowid = ANY (select min(test.rowid) from test left 
    outer join 
    (select min(rowid) row_id from test group by id,name)t on 
    test.rowid=t.row_id where t.row_id is null group by test.id,test.name); 
Questions connexes