Como usar DISTINCT e ORDER BY na mesma instrução SELECT?

117

Depois de executar a seguinte declaração:

SELECT  Category  FROM MonitoringJob ORDER BY CreationDate DESC

Estou recebendo os seguintes valores do banco de dados:

test3
test3
bildung
test4
test3
test2
test1

mas quero que as duplicatas sejam removidas, assim:

bildung
test4
test3
test2
test1

Tentei usar DISTINCT, mas não funciona com ORDER BY em uma instrução. Por favor ajude.

Importante:

  1. Eu tentei com:

    SELECT DISTINCT Category FROM MonitoringJob ORDER BY CreationDate DESC

    não funciona.

  2. O pedido por data de criação é muito importante.

rr
fonte
1
Como não funciona? Saída errada?
Fedearne

Respostas:

195

O problema é que as colunas usadas no ORDER BYnão são especificadas no DISTINCT. Para fazer isso, você precisa usar uma função agregada para classificar e usar um GROUP BYpara fazer o DISTINCTtrabalho.

Experimente algo assim:

SELECT DISTINCT Category, MAX(CreationDate) 
FROM MonitoringJob 
GROUP BY Category 
ORDER BY MAX(CreationDate) DESC, Category
Prutswonder
fonte
99
Você nem precisa da palavra-chave DISTINCT se estiver agrupando por categoria.
MatBailie
18

Colunas-chave de classificação estendida

O motivo pelo qual o que você deseja fazer não funciona é a ordem lógica das operações em SQL , que, para sua primeira consulta, é (simplificada):

  • FROM MonitoringJob
  • SELECT Category, CreationDateou seja, adicione a chamada coluna de chave de classificação estendida
  • ORDER BY CreationDate DESC
  • SELECT Categoryou seja, remova a coluna de chave de classificação estendida novamente do resultado.

Portanto, graças ao recurso de coluna-chave de classificação estendida do padrão SQL , é totalmente possível ordenar por algo que não está na SELECTcláusula, porque está sendo adicionado temporariamente a ele nos bastidores.

Então, por que isso não funciona com DISTINCT?

Se adicionarmos a DISTINCToperação, ela será adicionada entre SELECTe ORDER BY:

  • FROM MonitoringJob
  • SELECT Category, CreationDate
  • DISTINCT
  • ORDER BY CreationDate DESC
  • SELECT Category

Mas agora, com a coluna de chave de classificação estendida CreationDate , a semântica da DISTINCToperação foi alterada, portanto, o resultado não será mais o mesmo. Isso não é o que queremos, portanto, o padrão SQL e todos os bancos de dados razoáveis ​​proíbem esse uso.

Soluções Alternativas

Pode ser emulado com a sintaxe padrão da seguinte maneira

SELECT Category
FROM (
  SELECT Category, MAX(CreationDate) AS CreationDate
  FROM MonitoringJob
  GROUP BY Category
) t
ORDER BY CreationDate DESC

Ou, simplesmente (neste caso), como mostrado também por Prutswonder

SELECT Category, MAX(CreationDate) AS CreationDate
FROM MonitoringJob
GROUP BY Category
ORDER BY CreationDate DESC

Eu tenho um blog sobre SQL DISTINCT e ORDER BY mais detalhadamente aqui .

Lukas Eder
fonte
1
Acho que você está enganado sobre como DISTINCT ONfunciona e tenho certeza de que não ajuda aqui. A expressão entre parênteses é o que é usado para determinar a distinção (a condição de agrupamento). Se houver categorias diferentes com as mesmas CreationDate, apenas uma delas aparecerá no resultado! Como estava me perguntando se talvez eu estivesse errado de alguma forma, também carreguei o banco de dados de exemplo em sua postagem de blog para verificar novamente: a DISTINCT ONconsulta que você forneceu produziu um total de 1000 resultados (com muitas duplicatas length), enquanto a consulta abaixo deu apenas 140 valores (únicos).
Inkling de
@Inkling: Obrigado pelo seu tempo. O OP deseja explicitamente que as "duplicatas" sejam removidas. Veja a formulação do OP "mas eu quero as duplicatas removidas, assim" . Você provavelmente cometeu um erro ao copiar as consultas do meu blog. Existem duas consultas, uma que usa DISTINCT(não ON) e outra que usa DISTINCT ON. Observe que o último explicitamente não remove comprimentos duplicados, mas títulos duplicados. Eu realmente acho que minha resposta aqui está totalmente correta.
Lukas Eder
1
Meu ponto é que suas DISTINCT ONcondições estão removendo duplicatas usando a condição errada. Em sua postagem no blog, a DISTINCT ONconsulta realmente remove títulos duplicados , no entanto, a DISTINCTconsulta acima e a consulta abaixo (que você afirma ser "sintaxe sugar" para) removem comprimentos duplicados , pois esse é provavelmente o objetivo inteiro. A mesma coisa se aplica aqui: o OP deseja que as Categorias duplicadas sejam removidas, não as CreationDates duplicadas como a DISTINCT ONconsulta faz. Se você ainda não acredita em mim, teste você mesmo.
Inkling
6

Se a saída de MAX (CreationDate) não for desejada - como no exemplo da pergunta original - a única resposta é a segunda declaração da resposta de Prashant Gupta:

SELECT [Category] FROM [MonitoringJob] 
GROUP BY [Category] ORDER BY MAX([CreationDate]) DESC

Explicação: você não pode usar a cláusula ORDER BY em uma função embutida, portanto, a instrução na resposta de Prutswonder não é utilizável neste caso, você não pode colocar um select externo em torno dela e descartar a parte MAX (CreationDate).

Marc_Sei
fonte
2

Basta usar este código, se desejar valores das colunas [Category] e [CreationDate]

SELECT [Category], MAX([CreationDate]) FROM [MonitoringJob] 
             GROUP BY [Category] ORDER BY MAX([CreationDate]) DESC

Ou use este código, se quiser apenas os valores da coluna [Categoria].

SELECT [Category] FROM [MonitoringJob] 
GROUP BY [Category] ORDER BY MAX([CreationDate]) DESC

Você terá todos os registros distintos o que quiser.

Prashant Gupta
fonte
essas chaves [] são totalmente confusas ... esta é uma sintaxe SQL válida?
m13r
1
Os colchetes são para palavras-chave de escape, como Order, event, etc., portanto, se você tiver (por exemplo) uma coluna em sua tabela chamada, Eventvocê pode escrever em [Event]vez de Eventimpedir que o SQL lance um erro de análise.
Ben Maxfield
1

2) Ordenar por Data de Criação é muito importante

Os resultados originais indicaram que "test3" teve vários resultados ...

É muito fácil começar a usar MAX o tempo todo para remover duplicatas em Group By's ... e esquecer ou ignorar qual é a questão subjacente ...

O OP presumivelmente percebeu que usar MAX estava dando a ele o último "criado" e usando MIN daria o primeiro "criado" ...

JohnSurrey
fonte
3
Isso realmente não parece responder à pergunta, parece ser um comentário sobre o uso de outro respondente MAX, ao invés de algo que permanece sozinho como uma resposta à pergunta.
DaveyDaveDave
0
if object_id ('tempdb..#tempreport') is not null
begin  
drop table #tempreport
end 
create table #tempreport (
Category  nvarchar(510),
CreationDate smallint )
insert into #tempreport 
select distinct Category from MonitoringJob (nolock) 
select * from #tempreport  ORDER BY CreationDate DESC
Prumo
fonte
0

Por subconsulta, deve funcionar:

    SELECT distinct(Category) from MonitoringJob  where Category in(select Category from MonitoringJob order by CreationDate desc);
Shiwangini
fonte
Ummm ... eu não acho que vá. A seleção externa não é classificada.
Hossam El-Deen
não vai funcionar, estou aqui porque não está funcionando
Amirreza
-1

Distinct classificará os registros em ordem crescente. Se você deseja classificar em ordem decrescente, use:

SELECT DISTINCT Category
FROM MonitoringJob
ORDER BY Category DESC

Se você deseja classificar os registros com base no campo CreationDate, este campo deve estar na instrução de seleção:

SELECT DISTINCT Category, creationDate
FROM MonitoringJob
ORDER BY CreationDate DESC
C Patel
fonte
12
Isso será executado, mas não fornecerá o que o OP precisa. O OP deseja categorias distintas, não combinações distintas de Categoria e CreateDate. Este código pode gerar várias instâncias da mesma categoria, cada uma com diferentes valores CreationDate.
MatBailie
-1

Você pode usar CTE:

WITH DistinctMonitoringJob AS (
    SELECT DISTINCT Category Distinct_Category FROM MonitoringJob 
)

SELECT Distinct_Category 
FROM DistinctMonitoringJob 
ORDER BY Distinct_Category DESC
Jair
fonte
-3

Tente a seguir, mas não é útil para dados enormes ...

SELECT DISTINCT Cat FROM (
  SELECT Category as Cat FROM MonitoringJob ORDER BY CreationDate DESC
);
Máťa - Stitod.cz
fonte
4
"A cláusula ORDER BY é inválida em visualizações, funções sequenciais, tabelas derivadas, subconsultas e expressões de tabela comuns, a menos que TOP ou FOR XML também seja especificado."
TechplexEngineer
Isso não funciona porque você não especificou a coluna CreationDate no pedido por.
Mauro Bilotti
1
@TechplexEngineer Seu comentário está incorreto. Usar ORDER BYem subconsultas é absolutamente válido. E alguém até votou positivamente em seu comentário incorreto.
Racil Hilan
Estou tentando isso e tendo o mesmo erro com @TechplexEngineer. Estou usando um pedido personalizado com caso quando.
Ege Bayrak,
-4

Isso pode ser feito usando uma consulta interna como esta

$query = "SELECT * 
            FROM (SELECT Category  
                FROM currency_rates                 
                ORDER BY id DESC) as rows               
            GROUP BY currency";
Zaheer Babar
fonte
-5
SELECT DISTINCT Category FROM MonitoringJob ORDER BY Category ASC
Furacão
fonte
2
eu preciso classificar pela data de criação !! é muito importante
rr
Portanto, é impossível adicionar a coluna que você deseja ordenar sozinho? Seu exemplo mostrou entradas ordenadas em ordem alfabética. Se precisar fazer o pedido pela data de criação, basta adicioná-lo. Realmente não é tão difícil.
Furacão de
8
-1: O OP tentou isso, não funcionou, porque é impossível e você aparentemente ignorou esse fato ao patrocinar o OP. A questão é que o operador DISTINCT agrupará vários registros com o mesmo valor de categoria, cada um com datas de criação potencialmente diferentes. Portanto, é logicamente impossível ao usar DISTINCT. Isso empurra a lógica necessária para um GROUP BY em vez de DISTINCT, permitindo uma agregação (MAX) na data de criação.
MatBailie
Na verdade, se você olhar mais de perto o que o OP fez, que é um SQL absolutamente malformado - não cometi um único erro e o resultado dado corresponde ao que ele pediu. Não vou me preocupar em -1, apenas leia na próxima vez antes de corrigir as pessoas. Obrigado.
Furacão de
8
Você sugere adicionar diretamente o campo CreationDate, mesmo dizendo "não é tão difícil assim". Fazer isso produz o SQL malformado. Você recebeu -1 por patrocinar o OP, dando conselhos que levam o OP de volta à instrução que ele postou originalmente e deixando de notar a contenção entre DISTINCT e ordenação por um campo que não está no DISTINCT. Além disso, 'b' vem antes de 't' e '1' vem antes de '4', portanto, os resultados fornecidos pelo OP não estão categoricamente em ordem alfabética. Posso sugerir seu próprio conselho: leia (com mais cuidado) na próxima vez.
MatBailie