Come selezionare record univoci tramite SQL


89

Quando eseguo "SELEZIONA * DALLA tabella" ottengo risultati come di seguito:

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

Come puoi vedere, ci sono record duplicati dalla colonna2 (l'elemento1 viene cancellato). Quindi come potrei ottenere risultati come questo:

1 item1 data1
2 item2 data3
3 item3 data4

Viene restituito un solo record dal duplicato, insieme al resto dei record univoci.


L'articolo 1 non è tecnicamente duplicato. Come mostrato, le righe 1 e 2 sono osservazioni uniche. E se volessi mantenere la riga 2 e non la riga 1?
Cibernetico

Risposte:


107

Con la distinctparola chiave con nomi di colonne singole e multiple, ottieni record distinti:

SELECT DISTINCT column 1, column 2, ...
FROM table_name;

15
Può essere che la risposta sia effettivamente sbagliata? DISTINCT viene applicato a tutte le colonne selezionate (almeno su un DB2), che restituirà comunque valori duplicati nelle singole colonne.
Konstantin

26

Se hai solo bisogno di rimuovere i duplicati, usa DISTINCT. GROUP BYdovrebbe essere utilizzato per applicare operatori aggregati a ciascun gruppo

GROUP BY v DISTINCT


11

Dipende da quale modello vuoi restituire per ogni articolo unico. I tuoi dati sembrano indicare il valore minimo dei dati, quindi in questo caso per SQL Server.

SELECT item, min(data)
FROM  table
GROUP BY item

11

Ci sono 4 metodi che puoi usare:

  1. DISTINTO
  2. RAGGRUPPA PER
  3. Sottoquery
  4. Common Table Expression (CTE) con ROW_NUMBER ()

Considera il seguente esempio TABLEcon i dati del 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

Opzione 1: SELEZIONA DISTINTO

Questo è il modo più semplice e diretto, ma anche il più limitato:

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

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

Opzione 2: GROUP BY

Il raggruppamento consente di aggiungere dati aggregati, come il min(id), max(id), count(*), ecc:

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|
*/

Opzione 3: sottoquery

Utilizzando una sottoquery, puoi prima identificare le righe duplicate da ignorare e quindi filtrarle nella query esterna con il WHERE NOT IN (subquery)costrutto:

/** 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|
*/

Opzione 4: espressione di tabella comune con ROW_NUMBER ()

In Common Table Expression (CTE), selezionare ROW_NUMBER (), partizionato dalla colonna del gruppo e ordinato nell'ordine desiderato. Quindi SELEZIONA solo i record che hanno 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|
*/

6

usa solo inner join perché group by non funzionerà con più colonne che dicono non contenute in nessuna delle due funzioni di aggregazione.

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;

Questa è la risposta a una domanda diversa, probabilmente quella che dovrebbe essere taggata con il più grande-n-per-gruppo
a_horse_with_no_name

Questa e la soluzione di Dave Baker sono le soluzioni corrette per la domanda SO. Il vantaggio di questa soluzione è che consente di selezionare righe con solo alcune colonne distinte specificate e una colonna MIN (id) AS id deve essere definita per selezionare solo una delle più colonne specificate.
giordano

1

Trovo che se non posso usare DISTINCT per qualsiasi motivo, GROUP BY funzionerà.


1

Per ottenere tutte le colonne nel tuo risultato devi inserire qualcosa come:

SELECT distinct a, Table.* FROM Table

metterà una come prima colonna e il resto sarà TUTTE le colonne nello stesso ordine della tua definizione. Cioè, la colonna a verrà ripetuta.


1
Sei sicuro di questo? L'ho provato su w3schools e ha restituito lo stesso di SELECT *, tranne che a era la prima colonna
Freakishly

@ Freakishly sì ed è esattamente ciò che dice che farà nella mia risposta: /
htafoya

Questo non funzionerà, non puoi selezionare * dopo il distinto in questo modo (riceverai un errore 1064 - Errore nella sintassi SQL)
tim.baker

@Mohsinkhan beh, ho dimenticato di inserire che è necessario scrivere il nome della tabella. In qualche modo quando l'ho scritto ha funzionato, ma l'ho appena testato e non ha funzionato senza il nome della tabella prima del *
htafoya

2
Questo è esattamente lo stesso diselect distinct * from ...
a_horse_with_no_name

-4

Seleziona Eff_st da (seleziona EFF_ST, ROW_NUMBER () su (PARTITION BY eff_st) XYZ - da ABC.CODE_DIM

) dove XYZ = 1 ordine da EFF_ST recupera solo le prime 5 righe

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.