Como selecionar registros únicos por SQL

87

Quando eu executo "SELECT * FROM table", obtive resultados como abaixo:

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

Como você pode ver, existem registros dup da coluna2 (item1 são duplicados). Então, como eu poderia obter resultados como este:

1 item1 data1
2 item2 data3
3 item3 data4

Apenas um registro é retornado da duplicata, junto com o restante dos registros exclusivos.

Yinan
fonte
O item 1 não é tecnicamente duplicado. Conforme mostrado, as linhas 1 e 2 são observações únicas. E se você quisesse manter a linha 2 e não a linha 1?
Cybernetic

Respostas:

105

Com a distinctpalavra-chave com nomes de coluna única e múltipla, você obtém registros distintos:

SELECT DISTINCT column 1, column 2, ...
FROM table_name;
mjallday
fonte
14
Será que a resposta está realmente errada? DISTINCT é aplicado a todas as colunas selecionadas (pelo menos em um DB2), que ainda retornará valores duplicados em colunas individuais.
Konstantin
26

Se você só precisa remover duplicatas, use DISTINCT. GROUP BYdeve ser usado para aplicar operadores agregados a cada grupo

GROUP BY v DISTINCT

Rahul
fonte
11

Depende de qual linha você deseja devolver para cada item exclusivo. Seus dados parecem indicar o valor mínimo dos dados, nesta instância do SQL Server.

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

Existem 4 métodos que você pode usar:

  1. DISTINTO
  2. GRUPO POR
  3. Subconsulta
  4. Expressão de tabela comum (CTE) com ROW_NUMBER ()

Considere o seguinte exemplo TABLEcom dados de teste:

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

Opção 1: SELECIONE DISTINTO

Esta é a maneira mais simples e direta, mas também a mais limitada:

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

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

Opção 2: GROUP BY

Agrupamento permite que você adicione dados agregados, como o 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|
*/

Opção 3: subconsulta

Usando uma subconsulta, você pode primeiro identificar as linhas duplicadas a serem ignoradas e, em seguida, filtrá-las na consulta externa com a WHERE NOT IN (subquery)construção:

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

Opção 4: Expressão de tabela comum com ROW_NUMBER ()

Na Expressão de Tabela Comum (CTE), selecione ROW_NUMBER (), particionado pela coluna do grupo e ordenado na ordem desejada. Em seguida, SELECIONE apenas os registros que possuem 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
fonte
6

apenas use a junção interna porque agrupar por não funcionará com várias colunas dizendo que não está contido em nenhuma função agregada.

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
fonte
Essa é a resposta para uma pergunta diferente, provavelmente uma que deveria ser marcada com maior-n-por-grupo
a_horse_with_no_name
Esta e a solução de Dave Baker são as soluções corretas para a questão do SO. A vantagem desta solução é que ela permite selecionar linhas com apenas algumas colunas distintas especificadas e uma coluna MIN (id) AS id deve ser definida para selecionar apenas uma das múltiplas colunas especificadas.
giordano
1

Acho que, se não conseguir usar o DISTINCT por algum motivo, o GROUP BY funcionará.

John Hamelink
fonte
1

Para obter todas as colunas em seu resultado, você precisa colocar algo como:

SELECT distinct a, Table.* FROM Table

ele colocará um como a primeira coluna e o resto será TODAS as colunas na mesma ordem de sua definição. Ou seja, a coluna a será repetida.

Htafoya
fonte
1
Você tem certeza disso? Eu tentei isso em w3schools e retornou o mesmo que SELECT *, exceto que a era a primeira coluna
Freakishly
@Freakishly yes e isso é exatamente o que diz que fará na minha resposta: /
htafoya
Isso não funcionará, você não pode selecionar * após o distinto como esse (você receberá um erro 1064 - Erro em sua sintaxe SQL)
tim.baker
@Mohsinkhan bem esqueci de colocar que você precisa para escrever o nome da tabela. De alguma forma, quando eu escrevi isso funcionou, mas eu apenas testei agora e não funcionou sem o nome da tabela antes de *
htafoya
2
É exatamente igual aselect distinct * from ...
a_horse_with_no_name
-4

Selecione Eff_st de (selecione EFF_ST, ROW_NUMBER () sobre (PARTITION BY eff_st) XYZ - de ABC.CODE_DIM

) onde XYZ = 1 pedido por EFF_ST busca as primeiras 5 linhas apenas

Shailendra Singhai
fonte