2009-11-20 6 views
2

J'ai une table avec les colonnes A, B et C. La colonne A peut avoir des doublons.Sélectionnez les uniques et l'un des doubles

J'ai besoin d'une requête pour obtenir un resultset avec des valeurs uniques dans la colonne A, et je me fiche de la duplication possible.

Je ne sais rien d'avance sur le reste des données.

Un exemple pourrait être:

A B C 
1 8 8 
1 7 7 
2 10 10 

Dans ce cas, je veux choisir:

A B C 
1 x x 
2 10 10 

x = Peu importe quelle valeur il choisirait.

Cordialement,

Matthias Vance

Modifier

Je pensais que j'ai trouvé ma solution:

SELECT * FROM (
    SELECT * FROM test GROUP BY a 
) table_test; 

Mais cela ne fonctionnait pas après tout.

Cela se traduira par:

[Microsoft][ODBC Excel Driver] Cannot group on fields selected with '*' 
+0

Pourriez-vous élaborer un peu, quelle base de données (MS SQL)? Quel Excel 2007/2003? Excel tente-t-il d'accéder à la base de données ou d'une autre manière - où se trouve la table? –

+0

La base de données est un fichier Excel, je ne peux pas affiner la version car je permets aux utilisateurs de télécharger n'importe quelle version. J'ai juste besoin de sélectionner ce que j'ai spécifié à partir de la feuille Excel, et (pour commencer) l'afficher sur l'écran. –

+0

Qu'est-ce que la connexion au fichier Excel via ODBC? Une base de données? Une application quelconque? –

Répondre

1

Essayez ceci:

select A, B, C 
from test x 
where not exists (select * 
        from test y 
        where y.A = x.A 
         and (y.B < x.B or (y.B = x.B and y.C < x.C)) 
order by A 

Mais puisqu'il contient peut-il sous-requête corrélée lente. (OTOH il est au moins théoriquement possible pour le moteur de base de données pour optimiser dans quelque chose que je montre ci-dessous.)


Qu'en est-il quelque chose en dehors de SQL? Qu'allez-vous faire du résultat?

Si vous allez le traiter avec un programme, pourquoi ne pas simplement obtenir:

select A, B, C from test order by A, B, C 

et faire quelque chose comme:

prev_a = None 
for a, b, c in get_query_result(): 
    if a != prev_a: 
     prev_a = a 
     yield (a, b, c) 

dans votre application?

Je ne sais pas PHP mais je pense que ce serait quelque chose comme ceci:

$query = "SELECT a,b,c FROM test ORDER BY a,b,c"; 
$result = odbc_exec($connect, $query); 
$prev_a = NULL; # I don't know what you would normally use here in PHP 
while (odbc_fetch_row($result)) { 
    $a = odbc_result($result, 1); 
    if (is_null($prev_a) or $a != $prev_a) { 
    $b = odbc_result($result, 2); 
    $c = odbc_result($result, 3); 
    print("A = $a, B = $b, C = $c\n"); 
    $prev_a = $a; 
    } 
} 
+0

Je pense que je vais effectivement faire ce côté client (comme dans, client du serveur SQL), c'est la solution la plus rapide. Il peut y avoir jusqu'à 20 colonnes, et l'utilisation de toutes ces sous-requêtes se traduira par un système très lent. Je devrais juste utiliser ORDER BY, et vérifier par rapport au résultat précédent. Je vous remercie! –

+0

Les 20 colonnes ne sont pas toutes utilisées pour décider quelle ligne choisir, n'est-ce pas? Donc avant de choisir la version, vérifiez ce qui est mieux. S'il y a beaucoup de doublons et que les lignes sont grandes, il peut être plus rapide de le faire en SQL. Vérifiez le plan d'exécution pour la requête et mesurez les performances. –

0

Toutes les lignes qui ont une valeur unique dans un

SELECT * FROM table t1 INNER JOIN 
(SELECT A FROM table GROUP BY A HAVING COUNT(A) = 1) as t2 
ON t1.A = t2.A 

Je ne comprends pas ce que vous entendez par « L'une des lignes qui est l'un des les valeurs en double dans A ". Pourriez-vous l'expliquer un peu mieux?

En utilisant votre exemple, MySQL simplement faire

SELECT * FROM table GROUP BY A 

volonté vous a donné le résultat souhaité:

A B C 
1 8 8 
2 10 10 
+0

J'ai ajouté un exemple pour illustrer mon problème. Matthias –

+0

Malheureusement, le pilote Microsoft ODBC Excel ne permet pas d'utiliser GROUP BY lorsque vous avez "SELECT *", il fonctionne pour les colonnes simples. –

+0

Il suffit de mettre tout le monde des champs au lieu de * SELECT A, B, C DU GROUPE table PAR UN – Sergi

0
-- All rows that are unique in column A 
select * 
from table 
where col_a in (select col_a from table group by col_a having count(*)=1) 
-- One row per dupe 
select * 
from table 
where col_a in (select max(col_a) from table group by col_a having count(*)>1) 
+0

Votre deuxième requête ne va pas travailler à cause de cela: SELECT MAX (col_a) FROM table GROUP BY COUNT AYANT col_a (*)> 1; // Cela renverra (à titre d'exemple): 1 SELECT * FROM table OERE IN (1); // Cela retournera toutes les lignes où a = 1, qui sont deux lignes. Matthias –

+0

Merci matt, vous avez raison, je voulais dire un champ différent pour l'expression Max (col_a), mais était une faute de frappe. Mais quel que soit le champ utilisé, l'expression Max() devra être unique pour chaque col_a. Je souhaite que nous ayons de vrais noms de colonnes pour faciliter les exemples ... – Sparky

+0

Je ne peux pas fournir d'autres noms de colonne réels en dehors de A, car c'est la seule colonne qui soit garantie dans le fichier. (Les fichiers sont fournis par l'utilisateur). Je comprends très bien que tout est un peu plus difficile à écrire/comprendre. –

1

Le plus dur devient b et c de la même rangée. La requête suivante utilise une sous-requête pour éliminer les lignes qui n'ont pas la valeur la plus faible pour b ou c. Il joint la table sur lui-même et dit qu'il ne peut pas y avoir de lignes avec une valeur inférieure à b ou c. Le "non" est implémenté par le prev.a is null dans la clause WHERE.

La sous-requête est appelée semiunique car il peut toujours y avoir des lignes dupliquées avec b et c identiques. La requête externe prend soin de ceux avec un GROUP BY. Puisque b et c sont identiques, peu importe la ligne que nous choisissons, nous pouvons en choisir une en utilisant min().

select a, min(b), min(c) 
from (
    select cur.a, cur.b, cur.c 
    from YourTable cur 
    left outer join YourTable prev 
     on cur.a = prev.a 
     and (cur.b > prev.b 
      or (cur.b = prev.b and cur.c > prev.c)) 
    where prev.a is null    
) semiunique 
group by semiunique.a 

par votre commentaire, une version beaucoup plus simple à saisir "quelque chose" pour b et c:

select a, min(b), min(c) 
from YourTable 
group by a 
+0

J'ai essayé, et j'ai échoué, de réécrire votre requête pour ignorer les valeurs des colonnes B et C. Je veux juste tout obtenir indépendamment des valeurs des autres colonnes. Seriez-vous si gentil avec (aidez-moi) à réécrire cela? –

+0

Réponse éditée, mais je ne suis pas sûr si je vous ai bien compris. Peut-être pourriez-vous éditer la question avec un exemple plus détaillé? – Andomar

+0

Si vous utilisez min (b) et min (c), ils peuvent renvoyer des valeurs de différentes lignes. Je ne suis pas sûr que ce soit correct. –

1

Cela fonctionne dans SQL Server 2008, ce qui illustre le concept. Vous avez besoin d'une colonne unique.

declare @temp as table (
id int identity(1,1), 
a int, 
b int, 
c int) 

insert into @temp 
    select 1 as A, 8 as B, 8 as C 
    union 
    select 1, 7, 7 
    union 
    select 2, 10, 10 

select a, b, c from @temp 
where id in (select MAX(id) from @temp 
group by a) 

Voyant que vous utilisez Excel, j'utiliserais le même principe. Ajoutez une autre colonne à la feuille de calcul et assurez-vous qu'elle est unique. Utilisez cette colonne comme colonne d'ID.

+0

Malheureusement, le pilote ODBC Excel ne prend pas en charge la commande ALTER TABLE. –

+0

Ceci est la meilleure solution donnée jusqu'à présent, et la seule certitude de travailler. Vous avez affaire à Excel, écrire une macro VBA pour ajouter la colonne unique, il ne doit pas être fait en SQL. – Donnie

0

Une autre option consisterait à utiliser la fonction ROW_NUMBER(). Je ne sais pas si elle est valide dans le pilote ODBC Excel si:

select a, b, c from (
select * 
, ROW_NUMBER() OVER (PARTITION BY A ORDER BY A) as RN 
from @temp 
) q where rn = 1 
+0

J'ai déjà vu cette solution et l'ai essayée, mais le pilote ne prend pas en charge ROW_NUMBER(). "Fonction non définie 'ROW_NUMBER' dans l'expression" –

0
select * 
from table T 
where id = (
    select min(id) from table where a = T.a 
) 

UPD. Mais s'il n'y a pas de clé primaire dans votre table (pourquoi?), Puis:

select A, min(B), min(C) 
from TABLE 
group by A 
+0

Cela ne fonctionnera pas, vérifiez mon commentaire à la réponse de Sparky sur l'utilisation de MIN() et MAX(). –

0

Je sais que c'est une façon sale, mais fonctionnera ce cas.

code pseudo:

créer la table #tmpStaging avec la clé primaire col (A)

pour chaque ligne de la Fichierplat/excel/whatever commencer commencer essayer insert en #tmpstaging extrémité essayer

commencent attraper --do rien cran de fin fin

select * from #tmpstaging vous donnera des lignes sans dups

+0

Je reçois une erreur de syntaxe "[Microsoft] [ODBC Excel Driver] dans l'instruction CREATE TABLE". lorsque j'essaie d'exécuter "CREATE TABLE #tempdata (id INTEGER)". –

2

ne serait-ce travail de recherche simple:

SELECT A, MIN(B), MIN(C) FROM test GROUP BY A 

groupes par un et juste de sélectionner le valeurs minimales de B et C dans les rangées de A. Les valeurs de B et C peuvent provenir de rangées différentes, par ex.

A B C 
1 2 3 
1 4 1 

retournerait

A B C 
1 2 1 
+0

Je ne peux pas faire cela, car le fichier Excel contiendra des données client. La colonne A peut être dupliquée, mais le reste des données doit être en ordre. Ce serait une bonne solution si je n'en avais pas besoin. –

1
Select A 
    , Max(b) //Since You don't care about the Value 
    , Max(c) //Since You don't care about the Value 
From table t 
Group By A 
+0

Le seul problème potentiel est que la valeur de B et C pourrait provenir de rangées différentes, que le PO n'a pas précisé être acceptable ou non. –

+0

C'est, en effet, un problème pour moi. (Voir aussi mon commentaire sur le post de HakonB) –

0

Cela vous donnera le premier de chaque doublon

SELECT DISTINCT 
    A, 
    (SELECT TOP 1 B FROM @Table tB WHERE tb.A = t.A) B, 
    (SELECT TOP 1 C FROM @Table tB WHERE tb.A = t.A) C 
FROM @Table t 
+0

oopsy ... je n'ai pas vu votre message non plus :) – CoderHawk

0

Essayez ce,

SELECT UT.[A], 
(SELECT TOP 1 B FROM [YourTable] WHERE [YourTable].A= UT.A) AS B, 
(SELECT TOP 1 C FROM [YourTable] WHERE [YourTable].A= UT.A) AS C FROM [YourTable] AS UT GROUP BY UT.[A] 

Je ne l'ai pas encore essayé ... qui sait :)

Questions connexes