SQL / mysql - Selecione distinto / UNIQUE, mas retornará todas as colunas?

373
SELECT DISTINCT field1, field2, field3, ......   FROM table

Estou tentando realizar a seguinte instrução sql, mas quero que ele retorne todas as colunas, isso é possível? Algo como:

SELECT DISTINCT field1, * from table
aryaxt
fonte
12
Por que não SELECT DISTINCT * FROM tablefunciona para você?
ypercubeᵀᴹ
19
Se sua tabela tiver um PK, todas as linhas deverão ser distinctpor definição. Se você está tentando apenas selecionar, DISTINCT field1mas de alguma forma retornar todas as outras colunas, o que deve acontecer com as colunas que possuem mais de um valor para um field1valor específico ? Você precisaria usar GROUP BYe algum tipo de agregação nas outras colunas, por exemplo.
Martin Smith
11
Se você deseja linhas repetidas e não apenas linhas distintas, remova a palavra-chave distinta.
Hyperboreus
2
Você poderia dar um exemplo de como você espera que os resultados sejam? Até o momento, não consigo entender sua consulta desejada.
recursivo
3
Aqui está a resposta de uma pergunta semelhante, você precisa primeiro obter a coluna distinta com seus IDs e depois associá-la à tabela original. SELECT DISTINCT em uma coluna, retornar várias outras colunas
yadavr

Respostas:

407

Você está procurando um grupo por:

select *
from table
group by field1

Ocasionalmente, pode ser escrito com uma declaração distinta:

select distinct on field1 *
from table

Na maioria das plataformas, no entanto, nenhuma das opções acima funcionará porque o comportamento nas outras colunas não é especificado. (O primeiro funciona no MySQL, se é isso que você está usando.)

Você pode buscar os campos distintos e escolher uma única linha arbitrária a cada vez.

Em algumas plataformas (por exemplo, PostgreSQL, Oracle, T-SQL), isso pode ser feito diretamente usando as funções da janela:

select *
from (
   select *,
          row_number() over (partition by field1 order by field2) as row_number
   from table
   ) as rows
where row_number = 1

Em outros (MySQL, SQLite), você precisará escrever subconsultas que farão com que você junte toda a tabela consigo ( exemplo ), por isso não é recomendado.

Denis de Bernardy
fonte
10
A consulta não irá analisar para mim e dá um erro: The ranking function "row_number" must have an ORDER BY clause. Precisamos adicionar ordem por cláusula após partição por campo1. Portanto, a consulta correta será: select * from ( select *, row_number() over (partition by field1 order by orderbyFieldName) as row_number from table ) as rows where row_number = 1
Ankur-m
11
Obrigado! Eu estava no mesmo problema e a solução foi a: GROUP BY
Joaquin Iurchuk 13/11/2015
2
Também no Oracle (Oracle SQL Developer) você não pode especificar select *, row_number() over (partition by field1 order by field2) as row_number from table. Você tem que usar explicitamente nome da tabela / alias na consulta seleçãoselect **table**.*, row_number() over (partition by field1 order by field2) as row_number from table
meta4
11
@jarlh: Pode ser ... hoje. Como você pode notar, essa resposta tem quase 7 anos, um momento em que esse não era o caso, na medida em que posso me lembrar de quando eu estava ativo. Você pode marcar e / ou editar a resposta novamente, se achar necessário.
Denis de Bernardy
2
select distinct on (field1) * from table; trabalha também no PostgreSQL
Chilianu Bogdan
61

A partir da formulação da sua pergunta, entendo que você deseja selecionar os valores distintos para um determinado campo e para cada um desses valores ter todos os outros valores da coluna na mesma linha listados. A maioria dos DBMSs não permitirá isso com nem DISTINCTnem GROUP BYporque o resultado não é determinado.

Pense assim: se o seu field1ocorrer mais de uma vez, qual o valor de field2será listado (dado que você tem o mesmo valor field1em duas linhas, mas dois valores distintos field2nessas duas linhas).

No entanto, você pode usar funções agregadas (explicitamente para todos os campos que deseja que sejam mostrados) e usar um em GROUP BYvez de DISTINCT:

SELECT field1, MAX(field2), COUNT(field3), SUM(field4), .... FROM table GROUP BY field1
Costi Ciudatu
fonte
4
+1 para esta solução. Para que possamos fazer SELECT field1, MIN(field2), MIN(field3), MIN(field4), .... FROM table GROUP BY field1, e os campos 2, 3, 4 ,,, não precisam ser números inteiros (ou outros dígitos), eles também podem ser campos de caracteres
perscreva
Estava funcionando bem até ficar preso em uma coluna booleana. Os valores da coluna MIN (Dinâmico) são modificados para false, mesmo que sejam verdadeiros. Qualquer outra função agregada disponível para tratar boolean - signonsridhar há 6 minutos. Sum (dinâmico) alterado false para 1
signonsridhar 22/08/16
11
Ótima sugestão, levou-me à minha solução, que eu acho mais universal - dê uma olhada!
Garrett Simpson
@signonsridhar converte seu booleano em uma int e use sum; por exemplo:sum(cast(COL as int)) > 0
Drew
26

Se entendi seu problema corretamente, é semelhante ao que acabei de ter. Você deseja limitar a usabilidade do DISTINCT a um campo especificado, em vez de aplicá-lo a todos os dados.

Se você usar GROUP BY sem uma função agregada, em qualquer campo que você GROUP BY será seu arquivo DISTINCT.

Se você fizer sua consulta:

SELECT * from table GROUP BY field1;

Ele mostrará todos os seus resultados com base em uma única instância do campo1.

Por exemplo, se você tiver uma tabela com nome, endereço e cidade. Uma única pessoa tem vários endereços registrados, mas você só quer um único endereço para a pessoa, pode consultar da seguinte maneira:

SELECT * FROM persons GROUP BY name;

O resultado será que apenas uma instância desse nome aparecerá com seu endereço e a outra será omitida da tabela resultante. Cuidado: se seus arquivos tiverem valores atômicos como firstName, lastName, você deseja agrupar por ambos.

SELECT * FROM persons GROUP BY lastName, firstName;

porque se duas pessoas tiverem o mesmo sobrenome e você agrupar apenas por sobrenome, uma dessas pessoas será omitida dos resultados. Você precisa manter essas coisas em consideração. Espero que isto ajude.

rocklandcitizen
fonte
Como mencionado na resposta aceita, funcionaria para a maioria das encarnações do SQL - somente para MYSQL
Garrett Simpson
15
SELECT  c2.field1 ,
        field2
FROM    (SELECT DISTINCT
                field1
         FROM   dbo.TABLE AS C
        ) AS c1
        JOIN dbo.TABLE AS c2 ON c1.field1 = c2.field1
Tormentoso
fonte
Por que há C aliasquando ele pode funcionar sem ele? na linhaFROM dbo.TABLE AS C
Talha 02/10
2
Eu acredito que isso se deve ao meu uso do RedGate SQLPrompt. Do jeito que eu o configurei, ele sempre adiciona aliases - mesmo que desnecessários. Está lá "por precaução" #
Stormy
Isso pareceu promissor para mim, mas ainda trouxe de volta todas as linhas, não o campo distinto1. :(
Michael Fever
13

Essa é uma pergunta muito boa. Eu já li algumas respostas úteis aqui, mas provavelmente posso adicionar uma explicação mais precisa.

Reduzir o número de resultados da consulta com uma instrução GROUP BY é fácil, desde que você não consulte informações adicionais. Vamos supor que você tenha a seguinte tabela 'locais'.

--country-- --city--
 France      Lyon
 Poland      Krakow
 France      Paris
 France      Marseille
 Italy       Milano

Agora a consulta

SELECT country FROM locations
GROUP BY country

vai resultar em:

--country--
 France
 Poland
 Italy

No entanto, a seguinte consulta

SELECT country, city FROM locations
GROUP BY country

... gera um erro no MS SQL, porque como seu computador pode saber qual das três cidades francesas "Lyon", "Paris" ou "Marselha" você deseja ler no campo à direita de "França"?

Para corrigir a segunda consulta, você deve adicionar essas informações. Uma maneira de fazer isso é usar as funções MAX () ou MIN (), selecionando o valor maior ou menor entre todos os candidatos. MAX () e MIN () não são apenas aplicáveis ​​aos valores numéricos, mas também comparam a ordem alfabética dos valores das strings.

SELECT country, MAX(city) FROM locations
GROUP BY country

vai resultar em:

--country-- --city--
 France      Paris
 Poland      Krakow
 Italy       Milano

ou:

SELECT country, MIN(city) FROM locations
GROUP BY country

vai resultar em:

--country-- --city--
 France      Lyon
 Poland      Krakow
 Italy       Milano

Essas funções são uma boa solução, desde que você esteja bem ao selecionar seu valor nas extremidades da ordem alfabética (ou numérica). Mas e se não for esse o caso? Vamos supor que você precise de um valor com uma determinada característica, por exemplo, começando com a letra 'M'. Agora as coisas ficam complicadas.

A única solução que eu encontrei até agora é colocar toda a sua consulta em uma subconsulta e construir manualmente a coluna adicional fora dela:

SELECT
     countrylist.*,
     (SELECT TOP 1 city
     FROM locations
     WHERE
          country = countrylist.country
          AND city like 'M%'
     )
FROM
(SELECT country FROM locations
GROUP BY country) countrylist

vai resultar em:

--country-- --city--
 France      Marseille
 Poland      NULL
 Italy       Milano
Ulf Sanne
fonte
5

Ótima pergunta @aryaxt - você pode dizer que foi uma ótima pergunta porque você a perguntou há 5 anos e eu me deparei com ela hoje tentando encontrar a resposta!

Eu apenas tentei editar a resposta aceita para incluir isso, mas caso minha edição não consiga:

Se sua tabela não era tão grande e assumindo que sua chave primária era um número inteiro com auto-incremento, você poderia fazer algo assim:

SELECT 
  table.*
FROM table
--be able to take out dupes later
LEFT JOIN (
  SELECT field, MAX(id) as id
  FROM table
  GROUP BY field
) as noDupes on noDupes.id = table.id
WHERE
  //this will result in only the last instance being seen
  noDupes.id is not NULL
Garrett Simpson
fonte
5

Tentar

SELECT table.* FROM table 
WHERE otherField = 'otherValue'
GROUP BY table.fieldWantedToBeDistinct
limit x
Pedro Ramos
fonte
3

Você pode fazer isso com uma WITHcláusula.

Por exemplo:

WITH c AS (SELECT DISTINCT a, b, c FROM tableName)
SELECT * FROM tableName r, c WHERE c.rowid=r.rowid AND c.a=r.a AND c.b=r.b AND c.c=r.c

Isso também permite selecionar apenas as linhas selecionadas na WITHconsulta de cláusulas.

user2225399
fonte
2

No SQL Server, você pode usar as funções de janelas dense_rank e adicionais para obter todas as linhas E colunas com valores duplicados nas colunas especificadas. Aqui está um exemplo...

with t as (
    select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r1' union all
    select col1 = 'c', col2 = 'b', col3 = 'a', other = 'r2' union all
    select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r3' union all
    select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r4' union all
    select col1 = 'c', col2 = 'b', col3 = 'a', other = 'r5' union all
    select col1 = 'a', col2 = 'a', col3 = 'a', other = 'r6'
), tdr as (
    select 
        *, 
        total_dr_rows = count(*) over(partition by dr)
    from (
        select 
            *, 
            dr = dense_rank() over(order by col1, col2, col3),
            dr_rn = row_number() over(partition by col1, col2, col3 order by other)
        from 
            t
    ) x
)

select * from tdr where total_dr_rows > 1

Isso leva uma contagem de linhas para cada combinação distinta de col1, col2 e col3.

dotjoe
fonte
muito complicado e específico para uma implementação de SQL
Garrett Simpson
1
select min(table.id), table.column1
from table 
group by table.column1
KadoJ
fonte
Isso funcionou para mim !! Vale a pena notar que, se você estiver usando fetch_array (), precisará chamar cada linha por meio de um rótulo de índice, em vez de chamar implicitamente o nome da linha. Não há caracteres suficientes para escrever o exemplo que tenho: X desculpe!
Brandon Printiss 03/06
0
SELECT *
FROM tblname
GROUP BY duplicate_values
ORDER BY ex.VISITED_ON DESC
LIMIT 0 , 30

em ORDER BYAcabei de colocar exemplo aqui, você também pode adicionar o campo ID neste

SagarPPanchal
fonte
Como mencionado na resposta aceite, iria trabalhar para a maioria das encarnações de SQL - apenas para MYSQL
Garrett Simpson
0

Encontrei isso em outro lugar aqui, mas esta é uma solução simples que funciona:

 WITH cte AS /* Declaring a new table named 'cte' to be a clone of your table */
 (SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY val1 DESC) AS rn
 FROM MyTable /* Selecting only unique values based on the "id" field */
 )
 SELECT * /* Here you can specify several columns to retrieve */
 FROM cte
 WHERE rn = 1
Michael Fever
fonte
Funciona para MSSQL
Michael Fever
-1

Adicione GROUP BY ao campo que você deseja verificar se há duplicatas. Sua consulta pode parecer

SELECT field1, field2, field3, ......   FROM table GROUP BY field1

campo1 será verificado para excluir registros duplicados

ou você pode consultar como

SELECT *  FROM table GROUP BY field1

registros duplicados do campo1 são excluídos de SELECT

iCodeCrew
fonte
11
A cláusula GROUP BY deve corresponder aos campos selecionados. caso contrário, ele lançará um erro comofiled2 must appear in the GROUP BY clause or be used in an aggregate function
Viuu -a
-2

Basta incluir todos os seus campos na cláusula GROUP BY.

Wayneh
fonte
3
Para fazer desta uma boa resposta, você deve incluir um pouco mais de detalhes sobre o que você quer dizer.
Robbert
-2

Isso pode ser feito por consulta interna

$query = "SELECT * 
            FROM (SELECT field
                FROM table
                ORDER BY id DESC) as rows               
            GROUP BY field";
Zaheer Babar
fonte
2
Isso não responder à pergunta, o OP estava tentando obter todos os dados da tabela, mas remover linhas contendo cópias de um único campo
Garrett Simpson
-3
SELECT * from table where field in (SELECT distinct field from table)
Andrew
fonte
7
Isso não fará o trabalho. Você selecionou a coluna distinta na subconsulta, mas a cláusula where obtém todas as colunas com esse valor. Portanto, a consulta é tão boa quanto escrever 'select * from table', a menos que a coluna 'field' seja uma coluna única. Nesse caso, a distinção nessa coluna não é necessária.
Ankur-m
-3

SELECT DISTINCT FIELD1, FIELD2, FIELD3 FROM TABLE1 funcionará se os valores de todas as três colunas forem exclusivos na tabela.

Se, por exemplo, você tiver vários valores idênticos para o primeiro nome, mas o sobrenome e outras informações nas colunas selecionadas forem diferentes, o registro será incluído no conjunto de resultados.

Doris Gammenthaler
fonte
2
Isso não responde à pergunta, o OP estava tentando obter todos os dados da tabela, mas remove linhas contendo duplicatas de um único campo
Garrett Simpson
-3

Eu sugeriria usar

SELECT  * from table where field1 in 
(
  select distinct field1 from table
)

dessa forma, se você tiver o mesmo valor no campo1 em várias linhas, todos os registros serão retornados.

Ioannis K
fonte
11
Não é diferente com SELECT * FROM table;. Ainda mais É lento.
Shin Kim
Por favor, tente sua resposta primeiro.
Sherif 25/09