2009-07-23 6 views
2

Il y a plusieurs années, lors d'un entretien téléphonique, on m'a demandé de supprimer des lignes dupliquées dans une base de données. Après avoir donné plusieurs solutions qui fonctionnent, j'ai finalement dit aux restrictions sont les suivantes:Suppression de lignes dupliquées dans une base de données sans utiliser rowid ou création d'une table temporaire

  • Supposons que la table a une colonne VARCHAR
  • Impossible d'utiliser rowid
  • Impossible d'utiliser des tables temporaires

L'enquêteur a refusé pour me donner la réponse. Je suis resté perplexe depuis.

Après avoir demandé à plusieurs collègues au cours des années, je suis convaincu qu'il n'y a pas de solution. Ai-je tort?!

+4

Pouce. Croyez-moi. Vous ne voulez pas ce travail de toute façon. Une question où ils vous font attacher les mains derrière le dos comme cela est généralement plus destiné à montrer comment intelligent l'intervieweur n'est pas tester le candidat. – JohnFx

+0

Merci, JohnFx, pour le soutien ... me rend plus heureux que je n'ai pas poursuivi ce travail. –

+1

La dernière chose que vous voulez est un patron qui ne veut pas une solution, il/elle veut leur solution. C'est une erreur de gestionnaire recrue et très narcissique pour essayer d'embaucher des clones de vous-même. – JohnFx

Répondre

0

Je voudrais mettre un nombre unique de taille fixe dans la colonne VARCHAR pour les lignes dupliquées, puis analyser le nombre et supprimer tous sauf la ligne minimale. Peut-être que c'est ce que sa contrainte VARCHAR est pour. Mais cela pue parce qu'il suppose que votre numéro unique s'adaptera. Lame question. Vous ne vouliez pas travailler là de toute façon. ;-)

+0

$ chars = array ('L', 'O'); while (1 = 1) {echo $ chars [0]; echo $ chars [1];} echo $ chars [0]; –

1

Ceci est un complètement pillée façon de le faire, mais compte tenu des exigences de assanine, voici une solution viable en supposant SQL 2005 ou plus tard:

DELETE from MyTable 
    WHERE ROW_NUMBER() over(PARTITION BY [MyField] order by MyField)>1 
+0

Intéressant - ressemble à row_number() est juste très similaire à rowid –

+0

@vh row_number() est plus similaire à ROWNUM d'Oracle que ROWID d'Oracle, mais beaucoup plus flexible. Oracle a aussi ROW_NUMBER(). Cela fait partie des fonctions analytiques. Oh, et cela ne fonctionnera pas dans SQL Server 2005/2008, car ROW_NUMBER() n'est pas autorisé dans la clause where. –

+0

Ack! Tu as raison. Je jure que ça a marché hier quand je l'ai testé, mais hélas ça ne marche pas ce matin. Désolé pour le faux espoir. En outre, je viens de remarquer la contrainte noRowID, donc cela viole probablement l'esprit des termes. Je dis que vous devriez simplement contre-interroger l'intervieweur sur la façon dont il écrirait une requête pour le faire sans clavier ni souris. C'est à peu près aussi absurde. – JohnFx

2

Et si vous avez eu une réponse, une nouvelle restriction se présenterait-elle soudainement? Puisque vous mentionnez ROWID, je suppose que vous utilisiez Oracle. Les solutions sont pour SQL Server.

Inspiré par SQLServerCentral.com http://www.sqlservercentral.com/scripts/T-SQL/62866/

while(1=1) begin 
    delete top (1) 
    from MyTable 
    where VarcharColumn in 
    (select VarcharColumn 
    from MyTable 
    group by VarcharColumn 
    having count(*) > 1) 

    if @@rowcount = 0 
     exit 
end 

Supprime une ligne à la fois. Lorsque l'avant-dernière rangée d'un ensemble de doublons disparaît, la ligne restante ne sera pas dans la sous-sélection lors du prochain passage dans la boucle. (BIG Yuck!)

Aussi, voir http://www.sqlservercentral.com/articles/T-SQL/63578/ pour l'inspiration. Là RBarry Young suggère un moyen qui pourrait être modifié pour stocker les données dédupliquées dans la même table, supprimer toutes les lignes originales, puis convertir les données dédupliquées stockées dans le bon format. Il avait trois colonnes, donc pas exactement analogue à ce que vous faites.

Et puis il pourrait être faisable avec un curseur. Pas sûr et n'a pas le temps de le chercher. Mais créez un curseur pour sélectionner tout hors de la table, dans l'ordre, puis une variable pour suivre à quoi ressemblait la dernière rangée. Si la ligne en cours est la même, supprimez, sinon définissez la variable sur la ligne en cours.

+0

Vous avez raison ... Big Yuck à ce tout en boucle. Vous avez également raison qu'une nouvelle restriction s'est présentée lorsque j'ai suggéré d'utiliser une procédure PL/SQL. Il voulait le faire seulement avec une déclaration DELETE. –

+0

Hé, c'est moi! Et pour info, la technique que j'ai utilisée dans mon article * fonctionnera aussi sur une seule colonne VARCHAR aussi, tant qu'elle n'est pas déjà utilisée au maximum. Oh, et c'est sans boucles ou curseurs (ce qui le rend un peu facile) et * aussi * compatible avec SQL 2000, donc pas de fonction Row_Number() non plus. Et oui, c'est * difficile * dur, mais cela peut être fait. – RBarryYoung

0

Supposons que vous implémentez l'instruction DELETE pour un moteur SQL. Comment allez-vous supprimer deux lignes d'une table qui sont exactement identiques? Vous avez besoin de quelque chose pour distinguer l'un de l'autre! Vous pouvez réellement pas supprimer des lignes entièrement double (toutes les colonnes étant égales par ailleurs) sous les contraintes suivantes (comme il est prévu pour vous)

  1. Aucune utilisation de ROWID ou ROWNUM
  2. Aucune table temporaire
  3. Aucun code de procédure

Cela peut toutefois être fait même si l'une des conditions est relâchée.Voici les solutions en utilisant au moins une des trois conditions

assumer table est définie comme ci-dessous

Créer une table t1 (
col1 vacrchar2 (100),
nombre col2 (5),
col3 numéro (2)
);

identification des lignes en double:

Sélectionner col1, col2, col3
du groupe t1
par col1, col2, col3
comptage ayant (*)> 1

lignes en double peuvent aussi être identifié en utilisant ceci: sélectionnez c1, c2, c3, row_number() over (partition par (c1, c2, c3) ordre par c1, c2, c3) rn
de t1

REMARQUE: La fonction analytique row_number() ne peut pas être utilisée dans une instruction DELETE comme suggéré par JohnFx au moins dans Oracle 10g.

  • solution en utilisant ROWID

Supprimer de t1 où row_id>
(select min (t1_inner.row_id) à partir de t1 t1_innner
où t1_inner.c1 = t1.c1 et t1_inner.c2 = t1.c2 et t1_inner.c3 = t1.c3))

  • solution en utilisant la table temporaire

créer t1_dups de table que (
// requête d'écriture ici pour trouver les lignes en double comme ci-dessus // liste
)

supprimer de t1
où t1.c1, t1.c2, t1.c3 dans (select * from t1.dups)
insert dans t1 (
select c1, c2, c3 de t1_dups)

  • solution à l'aide du code de procédure

Cela va utiliser une approche similaire au cas où nous utilisons une table temporaire.

0
create table temp as 
select c1,c2 
from table 
group by c1,c2 
having(count(*)>1 or count(*)=1); 

Maintenant, laissez tomber la table de base. Renommez la table temporaire en table de base.

Questions connexes