Comment pouvez-vous trouver les lignes avec des colonnes égales?
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)
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.