2008-10-28 10 views
2

Ceci est similaire à this question, mais il semble que certaines des réponses ne sont pas tout à fait compatibles avec MySQL (ou je ne le fais pas bien), et je vais avoir un diable de temps à déterminer les changements dont j'ai besoin. Apparemment, mon SQL est plus rustre que je ne le pensais. Je cherche aussi à changer une valeur de colonne plutôt que de le supprimer, mais je pense au moins cette partie est simple ...MySQL - marquer tous sauf 1 ligne correspondante

J'ai une table comme:

rowid SERIAL 
fingerprint TEXT 
duplicate BOOLEAN 
contents TEXT 
created_date DATETIME

Je veux Dupliquer = vrai pour tous sauf le premier (par created_date) de chaque groupe par empreinte digitale. Il est facile de marquer tous les des lignes avec des empreintes digitales en double comme des dupes. La partie sur laquelle je suis coincé est de garder le premier. Une des applications qui remplit la table contient des charges de données volumineuses, plusieurs travailleurs chargeant des données provenant de différentes sources, et les données des travailleurs ne sont pas nécessairement partitionnées par date, il est donc difficile d'essayer de les marquer toutes comme ils entrent (le premier inséré n'est pas nécessairement le premier par date). En outre, j'ai déjà un tas de données là-dedans, je vais devoir nettoyer de toute façon. Je préfère donc avoir une requête relativement efficace que je peux exécuter après un chargement en bloc pour la nettoyer plutôt que de la construire dans cette application.

Merci!

+0

est (fingerprint, created_date) unique? –

Répondre

0

Que diriez-vous d'une approche en deux étapes, en supposant que vous pouvez aller en ligne lors d'une charge de données:

  • Mark chaque élément en double.
  • Sélectionnez la première ligne de chaque groupe et effacez le drapeau dupliqué.

Pas élégant, mais fait le travail.

+0

Cela peut facilement être accompli avec une seule requête, plutôt facile. Aucune raison d'aller à ces longueurs pour compliquer les choses. – sliderhouserules

0

Voici une drôle de façon de le faire:

SET @rowid := 0; 

UPDATE mytable 
SET duplicate = (rowid = @rowid), 
    rowid = (@rowid:=rowid) 
ORDER BY rowid, created_date; 
  • d'abord définir une variable utilisateur à zéro, en supposant que cela est inférieur à tout rowid dans votre table.
  • Utilisez ensuite la fonctionnalité MySQL UPDATE...ORDER BY pour vous assurer que les lignes sont mises à jour dans l'ordre par rowid, puis par created_date.
  • Pour chaque ligne, si le rowid actuel n'est pas égal à la variable utilisateur @rowid, définissez duplicate sur 0 (faux). Cela ne sera vrai que sur la première ligne rencontrée avec une valeur donnée pour rowid.
  • Ajoutez ensuite un ensemble fictif de rowid à sa propre valeur, en définissant @rowid à cette valeur comme effet secondaire.
  • Si la ligne suivante est UPDATE, s'il s'agit d'un doublon de la ligne précédente, rowid sera égal à la variable utilisateur @rowid et, par conséquent, duplicate sera défini sur 1 (vrai).

Edit: Maintenant, je l'ai testé, et je corrigé une erreur dans la ligne qui définit duplicate.

0

Je ne sais pas la syntaxe MySQL, mais PLSQL vous venez:

UPDATE t1 
SET duplicate = 1 
FROM MyTable t1 
WHERE rowid != (
    SELECT TOP 1 rowid FROM MyTable t2 
    WHERE t2.fingerprint = t1.fingerprint ORDER BY created_date DESC 
) 

Cela peut avoir des erreurs de syntaxe, comme je suis juste taper le brassard de/pas en mesure de le tester, mais c'est l'essentiel.


version MySQL (non testé):

UPDATE t1 
    SET duplicate = 1 
FROM MyTable t1 
WHERE rowid != (
    SELECT rowid FROM MyTable t2 
    WHERE t2.fingerprint = t1.fingerprint 
    ORDER BY created_date DESC 
    LIMIT 1 
) 
+0

SELECT TOP est une fonctionnalité de Microsoft SQL Server. Il n'est pas pris en charge dans Oracle ou MySQL. –

+0

Je viens de chercher la syntaxe MySQL, c'est LIMIT. – sliderhouserules

0

Voici une autre façon de le faire, en utilisant plusieurs tables UPDATE syntaxe de MySQL:

UPDATE mytable m1 
    JOIN mytable m2 ON (m1.rowid = m2.rowid AND m1.created_date < m2.created_date) 
SET m2.duplicate = 1; 
+0

Ne tient pas compte des dates en double ... – sliderhouserules

+0

Oh, oui, vous avez raison. Il suppose que chaque date est unique. Et bien. –

+0

True, mais vous pouvez faire m1.primary_key

2

MySQL doit être explicitement si les données que vous regroupez sont supérieures à 1024 octets (voir this link pour plus de détails). Donc, si vos données dans la colonne d'empreintes digitales sont plus grandes que 1024 octets, vous devez utiliser la variable max_sort_length (voir this link pour plus de détails sur les valeurs autorisées, et this link sur la façon de le définir) pour un plus grand nombre afin que le groupe ne soit pas utilisé en mode silencieux une partie de vos données pour le regroupement. Une fois que vous êtes sûr que MySQL regroupera vos données correctement, la requête suivante définira l'indicateur dupliqué de sorte que le premier enregistrement d'empreinte digitale ait un doublon défini sur FAUX/0 et que tous les enregistrements d'empreintes digitales suivantes soient dupliqués sur TRUE/1 :

UPDATE mytable m1 
INNER JOIN (SELECT fingerprint 
       , MIN(rowid) AS minrow 
       FROM mytable m2 
      GROUP BY fingerprint) m3 
     ON m1.fingerprint = m3.fingerprint 
     SET m1.duplicate = m3.minrow != m1.rowid; 

S'il vous plaît garder à l'esprit que cette solution ne prend pas en compte les valeurs NULL et s'il est possible pour le champ d'empreintes digitales pour être NULL, vous aurez alors besoin logique supplémentaire pour traiter ce cas.

0

Untested ...

UPDATE TheAnonymousTable 
    SET duplicate = TRUE 
WHERE rowid NOT IN 
     (SELECT rowid 
      FROM (SELECT MIN(created_date) AS created_date, fingerprint 
        FROM TheAnonymousTable 
       GROUP BY fingerprint 
       ) AS M, 
       TheAnonymousTable AS T 
     WHERE M.created_date = T.created_date 
      AND M.fingerprint = T.fingerprint 
     ); 

La logique est que la requête la plus interne renvoie le premier created_date pour chaque empreinte distincte comme alias de la table M. La requête détermine la valeur moyenne de rowid pour chacune de ces lignes; c'est une nuisance d'avoir à faire cela (mais nécessaire), et le code suppose que vous n'obtiendrez pas deux enregistrements pour la même empreinte digitale et l'horodatage. Cela vous donne le rowid pour l'enregistrement earlist pour chaque empreinte digitale séparée. Ensuite, la requête externe (UPDATE) définit le drapeau 'duplicate' sur toutes les lignes où le rowid n'est pas l'une des premières lignes.

Certains SGBD peuvent ne pas être satisfaits de l'exécution de sous-requêtes (imbriquées) sur la table en cours de mise à jour.

Questions connexes