Question

Si j'ai une table avec 2 colonnes importantes,

CREATE TABLE foo (id INT, a INT, b INT, KEY a, KEY b);

Comment trouver toutes les lignes dont a et b sont identiques dans les deux lignes? Par exemple, dans cet ensemble de données

id | a | b
----------
1  | 1 | 2
2  | 5 | 42
3  | 1 | 42
4  | 1 | 2 
5  | 1 | 2
6  | 1 | 42

Je souhaite récupérer toutes les lignes à l'exception de id = 2 car il est unique dans (a, b) . En gros, je veux trouver toutes les lignes incriminées qui pourraient arrêter un

ALTER TABLE foo ADD UNIQUE (a, b);

Quelque chose de mieux qu’une boucle n ^ 2 serait bien, car ma table a 10 millions de lignes.

Pour les points de bonus : comment puis-je supprimer toutes les lignes sauf une (peu m'importe quelles lignes, tant qu'il en reste une)

Était-ce utile?

La solution

SELECT * 
FROM foo first
JOIN foo second
  ON ( first.a = second.a
       AND first.b = second.b ) 
  AND (first.id <> second.id )

Doit apparaître avec toutes les lignes où plus d'une ligne a la même combinaison de a et b.

J'espère juste que vous avez un index sur les colonnes a et b.

Autres conseils

select * from foo where a = b

Ou est-ce que je manque quelque chose?

===

Mise à jour pour plus de clarté:

select * from 
foo as a
inner join foo as b
on a.a = b.a AND b.a = b.b
and a.id != b.id

++++++++++ Après la 3ème clarté, éditez:

select f1.id
FROM foo as f1
INNER JOIN foo as f2
ON f1.a = f2.a AND f1.b=f2.b AND f1.id != f2.id

Mais je me fais tirer dessus, alors vérifiez-le vous-même.

Essayez ceci:

    With s as (Select a,b from foo group by a,b having Count(1)>1)
Select foo.* from foo,s where foo.a=s.a and foo.b=s.b

Cette requête doit afficher les lignes en double dans la table foo.

Pourriez-vous clarifier ce que vous devez faire en fin de compte? La meilleure solution peut en dépendre (par exemple, voulez-vous simplement supprimer toutes les lignes de la clé de duplication?)

Une solution consiste à manipuler cette table (vous ne savez pas si mySQL la prend en charge, mais bien à partir de SYBASE) si vous ne voulez que des lignes à clé unique:

SELECT MIN(id), A, B FROM FOO GROUP BY A, B HAVING COUNT(*)>1

Votre question exacte (même si je ne comprends pas très bien pourquoi vous auriez besoin de toutes les lignes sauf id = 2) est la suivante:

SELECT F1.*  
FROM FOO F1 , 
     (SELECT A, B FROM FOO GROUP BY A, B HAVING COUNT(*)>1) F2
WHERE F1.A=F2.A and F1.B=F2.B

Pour supprimer tous les doublons, vous pouvez par exemple faire

DELETE FOO WHERE NOT EXISTS
(SELECT 1 from
    (SELECT MIN(id) 'min_id' FROM FOO GROUP BY A, B HAVING COUNT(*)>1) UINIQUE_IDS 
 WHERE id = min_id)

Comme alternative, vous pouvez faire

  SELECT MIN(id) 'id', A, B INTO TEMPDB..NEW_TABLE 
  FROM FOO GROUP BY A, B HAVING COUNT(*)>1

  TRUNCATE TABLE FOO
  // Drop indices on FOO
  INSERT FOO SELECT * FROM NEW_TABLE
  // Recreate indices on FOO

cela ne devrait-il pas fonctionner?

SELECT * FROM foo WHERE a = b

=== modifier ===

le que diriez-vous

SELECT a, b FROM foo GROUP BY a, b HAVING COUNT(*) > 1

=== ré-édition finale avant d'abandonner cette question ===

SELECT foo.* FROM foo, (
   SELECT a, b FROM foo GROUP BY a, b HAVING COUNT(*) > 1
) foo2
WHERE foo.a = foo2.a AND foo.b = foo2.b

voici une autre approche

select * from foo f1 where exists(
  select * from foo f2 where
    f1.id != f2.id and
    f1.a = f2.a and
    f1.b = f2.b )

de toute façon, même si je le trouve un peu plus lisible, si vous avez une table aussi énorme, vous devriez vérifier le plan d’exécution, les sous-requêtes ont une mauvaise réputation impliquant des performances ...

vous devriez également envisager de créer l'index (sans la clause unique, évidemment) pour accélérer la requête ... dans le cas d'opérations de grande taille, il est parfois préférable de passer du temps à créer l'index, à effectuer la mise à jour, puis à supprimer l'index. .. dans ce cas, je suppose qu'un index sur (a, b) devrait certainement beaucoup aider ...

Votre objectif déclaré est de supprimer toutes les combinaisons en double de (a, b) . Pour cela, vous pouvez utiliser un multi-table DELETE:

DELETE t1
  FROM foo t1
  JOIN foo t2 USING (a, b)
 WHERE t2.id > t1.id

Avant de l'exécuter, vous pouvez vérifier quelles lignes seront supprimées avec:

SELECT DISTINCT t1.id
  FROM foo t1
  JOIN foo t2 USING (a, b)
 WHERE t2.id > t1.id

La clause WHERE étant t2.id > t1.id cela supprimera tout sauf celui avec la plus grande valeur pour id . Dans votre cas, seules les lignes avec id égal à 2, 5 ou 6 resteraient.

Si la valeur id n'a aucune importance dans le produit final, c'est-à-dire si vous pouviez les renuméroter toutes et que tout irait bien, et si id est une colonne en série, il vous suffit de "sélectionner distinct" et de "distinct". Dans les deux colonnes d'une nouvelle table, supprimez toutes les données de l'ancienne table, puis copiez les valeurs temporaires.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top