Comment sélectionner des enregistrements uniques par SQL

87

Lorsque j'effectue "SELECT * FROM table", j'ai obtenu des résultats comme ci-dessous:

1 item1 data1
2 item1 data2
3 item2 data3
4 item3 data4

Comme vous pouvez le voir, il existe des enregistrements dupliqués de la colonne 2 (les éléments 1 sont dupliqués). Alors, comment pourrais-je obtenir un résultat comme celui-ci:

1 item1 data1
2 item2 data3
3 item3 data4

Un seul enregistrement est renvoyé à partir du doublon, avec le reste des enregistrements uniques.

Yinan
la source
L'élément 1 n'est pas techniquement dupliqué. Comme indiqué, les lignes 1 et 2 sont des observations uniques. Et si vous vouliez conserver la ligne 2 et non la ligne 1?
Cybernetic le

Réponses:

105

Avec le distinctmot - clé avec des noms de colonnes simples et multiples, vous obtenez des enregistrements distincts:

SELECT DISTINCT column 1, column 2, ...
FROM table_name;
mjallday
la source
14
La réponse est-elle en fait erronée? DISTINCT est appliqué à toutes les colonnes sélectionnées (au moins sur un DB2), qui renverra toujours des valeurs en double dans des colonnes individuelles.
Konstantin le
26

Si vous avez seulement besoin de supprimer les doublons, utilisez DISTINCT. GROUP BYdoit être utilisé pour appliquer des opérateurs d'agrégation à chaque groupe

GROUP BY v DISTINCT

rahul
la source
11

Cela dépend du rang que vous souhaitez retourner pour chaque article unique. Vos données semblent indiquer la valeur de données minimale donc dans cette instance pour SQL Server.

SELECT item, min(data)
FROM  table
GROUP BY item
Dave Barker
la source
10

Vous pouvez utiliser 4 méthodes:

  1. DISTINCT
  2. PAR GROUPE
  3. Sous-requête
  4. Expression de table commune (CTE) avec ROW_NUMBER ()

Considérez l'exemple suivant TABLEavec les données de test:

/** Create test table */
CREATE TEMPORARY TABLE dupes(word text, num int, id int);

/** Add test data with duplicates */
INSERT INTO dupes(word, num, id)
VALUES ('aaa', 100, 1)
      ,('bbb', 200, 2)
      ,('ccc', 300, 3)
      ,('bbb', 400, 4)
      ,('bbb', 200, 5)     -- duplicate
      ,('ccc', 300, 6)     -- duplicate
      ,('ddd', 400, 7)
      ,('bbb', 400, 8)     -- duplicate
      ,('aaa', 100, 9)     -- duplicate
      ,('ccc', 300, 10);   -- duplicate

Option 1: SELECT DISTINCT

C'est le moyen le plus simple et le plus direct, mais aussi le plus limité:

SELECT DISTINCT word, num 
FROM    dupes
ORDER BY word, num;

/*
word|num|
----|---|
aaa |100|
bbb |200|
bbb |400|
ccc |300|
ddd |400|
*/

Option 2: GROUP BY

Regroupement vous permet d'ajouter des données agrégées, comme min(id), max(id), count(*), etc:

SELECT  word, num, min(id), max(id), count(*)
FROM    dupes
GROUP BY word, num
ORDER BY word, num;

/*
word|num|min|max|count|
----|---|---|---|-----|
aaa |100|  1|  9|    2|
bbb |200|  2|  5|    2|
bbb |400|  4|  8|    2|
ccc |300|  3| 10|    3|
ddd |400|  7|  7|    1|
*/

Option 3: sous-requête

À l'aide d'une sous-requête, vous pouvez d'abord identifier les lignes dupliquées à ignorer, puis les filtrer dans la requête externe avec la WHERE NOT IN (subquery)construction:

/** Find the higher id values of duplicates, distinct only added for clarity */
    SELECT  distinct d2.id
    FROM    dupes d1
        INNER JOIN dupes d2 ON d2.word=d1.word AND d2.num=d1.num
    WHERE d2.id > d1.id

/*
id|
--|
 5|
 6|
 8|
 9|
10|
*/

/** Use the previous query in a subquery to exclude the dupliates with higher id values */
SELECT  *
FROM    dupes
WHERE   id NOT IN (
    SELECT  d2.id
    FROM    dupes d1
        INNER JOIN dupes d2 ON d2.word=d1.word AND d2.num=d1.num
    WHERE d2.id > d1.id
)
ORDER BY word, num;

/*
word|num|id|
----|---|--|
aaa |100| 1|
bbb |200| 2|
bbb |400| 4|
ccc |300| 3|
ddd |400| 7|
*/

Option 4: Expression de table commune avec ROW_NUMBER ()

Dans l'expression de table commune (CTE), sélectionnez le ROW_NUMBER (), partitionné par la colonne de groupe et ordonné dans l'ordre souhaité. Sélectionnez ensuite uniquement les enregistrements qui ont ROW_NUMBER() = 1:

WITH CTE AS (
    SELECT  *
           ,row_number() OVER(PARTITION BY word, num ORDER BY id) AS row_num
    FROM    dupes
)
SELECT  word, num, id 
FROM    cte
WHERE   row_num = 1
ORDER BY word, num;

/*
word|num|id|
----|---|--|
aaa |100| 1|
bbb |200| 2|
bbb |400| 4|
ccc |300| 3|
ddd |400| 7|
*/
isapir
la source
6

utilisez simplement la jointure interne, car group by ne fonctionnera pas avec plusieurs colonnes indiquant non contenues dans une fonction d'agrégation.

SELECT a.*
FROM yourtable a
INNER JOIN 
  (SELECT yourcolumn,
    MIN(id) as id
  FROM yourtable 
  GROUP BY yourcolumn
) AS b
  ON a.yourcolumn= b.yourcolumn
  AND a.id = b.id;
Ankit Kashyap
la source
C'est la réponse à une question différente, probablement celle qui devrait être taguée avec le plus grand-n-par-groupe
a_horse_with_no_name
Ceci et la solution de Dave Baker sont les bonnes solutions pour la question SO. L'avantage de cette solution est qu'elle permet de sélectionner des lignes avec seulement certaines colonnes distinctes spécifiées et une colonne MIN (id) AS id doit être définie pour sélectionner une seule des multiples colonnes spécifiées.
giordano
1

Je trouve que si je ne peux pas utiliser DISTINCT pour quelque raison que ce soit, alors GROUP BY fonctionnera.

John Hamelink
la source
1

Pour obtenir toutes les colonnes de votre résultat, vous devez placer quelque chose comme:

SELECT distinct a, Table.* FROM Table

il placera a comme première colonne et le reste sera TOUTES les colonnes dans le même ordre que votre définition. C'est-à-dire que la colonne a sera répétée.

htafoya
la source
1
Es-tu sûr de ça? J'ai essayé ceci sur w3schools et il a retourné le même que SELECT *, sauf que c'était la première colonne
Freakishly
@Freakishly yes et c'est exactement ce que dit ça va faire dans ma réponse: /
htafoya
Cela ne fonctionnera pas, vous ne pouvez pas sélectionner * après le différent comme ça (vous obtiendrez une erreur 1064 - Erreur dans votre syntaxe SQL)
tim.baker
@Mohsinkhan bien j'ai oublié de placer que vous devez écrire le nom de la table. Quand j'ai écrit cela, cela a fonctionné mais je viens de tester maintenant et cela n'a pas été le cas sans le nom de la table avant le *
htafoya
2
C'est exactement la même chose queselect distinct * from ...
a_horse_with_no_name
-4

Sélectionnez Eff_st de (sélectionnez EFF_ST, ROW_NUMBER () sur (PARTITION BY eff_st) XYZ - de ABC.CODE_DIM

) où XYZ = 1 ordre par EFF_ST récupère les 5 premières lignes uniquement

Shailendra Singhai
la source