Come utilizzare DISTINCT e ORDER BY nella stessa istruzione SELECT?


117

Dopo aver eseguito la seguente dichiarazione:

SELECT  Category  FROM MonitoringJob ORDER BY CreationDate DESC

Ricevo i seguenti valori dal database:

test3
test3
bildung
test4
test3
test2
test1

ma voglio che i duplicati vengano rimossi, in questo modo:

bildung
test4
test3
test2
test1

Ho provato a usare DISTINCT ma non funziona con ORDER BY in un'istruzione. Per favore aiuto.

Importante:

  1. L'ho provato con:

    SELECT DISTINCT Category FROM MonitoringJob ORDER BY CreationDate DESC

    non funziona.

  2. Order by CreationDate è molto importante.


1
Come non funziona? Uscita sbagliata?
Fedearne

Risposte:


195

Il problema è che le colonne utilizzate in ORDER BYnon sono specificate in DISTINCT. Per fare ciò, è necessario utilizzare una funzione di aggregazione per ordinare e utilizzare a GROUP BYper eseguire il DISTINCTlavoro.

Prova qualcosa di simile:

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

99
Non hai nemmeno bisogno della parola chiave DISTINCT se stai raggruppando per categoria.
MatBailie

18

Colonne chiave di ordinamento estese

Il motivo per cui ciò che vuoi fare non funziona è a causa dell'ordine logico delle operazioni in SQL , che, per la tua prima query, è (semplificato):

  • FROM MonitoringJob
  • SELECT Category, CreationDatecioè aggiungere una cosiddetta colonna chiave di ordinamento estesa
  • ORDER BY CreationDate DESC
  • SELECT Categorycioè rimuovere di nuovo la colonna della chiave di ordinamento estesa dal risultato.

Quindi, grazie alla funzionalità della colonna chiave di ordinamento estesa standard SQL , è totalmente possibile ordinare qualcosa che non è nella SELECTclausola, perché viene temporaneamente aggiunto dietro le quinte.

Allora, perché non funziona con DISTINCT?

Se aggiungiamo l' DISTINCToperazione, verrebbe aggiunta tra SELECTe ORDER BY:

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

Ma ora, con la colonna chiave di ordinamento estesa CreationDate , la semantica DISTINCTdell'operazione è stata modificata, quindi il risultato non sarà più lo stesso. Questo non è ciò che vogliamo, quindi sia lo standard SQL che tutti i database ragionevoli vietano questo utilizzo.

soluzioni alternative

Può essere emulato con la sintassi standard come segue

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

O semplicemente semplicemente (in questo caso), come mostrato anche da Prutswonder

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

Ho scritto sul blog di SQL DISTINCT e ORDER BY più in dettaglio qui .


1
Penso che ti sbagli su come DISTINCT ONfunziona e abbastanza sicuro che non aiuta qui. L'espressione tra parentesi è ciò che viene utilizzato per determinare la distinzione (la condizione di raggruppamento). Se ci sono diverse categorie con lo stesso, CreationDatesolo una di esse apparirà nel risultato! Dato che mi chiedevo se forse mi sbagliavo in qualche modo, ho anche caricato il database di esempio nel tuo post sul blog per ricontrollare: la DISTINCT ONquery che hai fornito lì ha prodotto un totale di 1000 risultati (con molti duplicati length) mentre la query sotto ha dato solo 140 valori (univoci).
Inkling

@Inkling: grazie per il tuo tempo. L'OP vuole esplicitamente rimuovere i "duplicati". Vedere la dicitura di OP "ma voglio che i duplicati vengano rimossi, in questo modo" . Probabilmente hai commesso un errore durante la copia delle query dal mio post sul blog. Ci sono due query, una che usa DISTINCT(no ON) e una che usa DISTINCT ON. Si prega di notare che quest'ultimo esplicitamente non rimuove le lunghezze duplicate, ma i titoli duplicati. Penso che la mia risposta qui sia del tutto corretta.
Lukas Eder

1
Il punto è che le tue DISTINCT ONcondizioni rimuovono i duplicati utilizzando la condizione sbagliata. Nel tuo post sul blog la DISTINCT ONquery rimuove effettivamente i titoli duplicati , tuttavia la DISTINCTquery sopra di essa e la query sotto di essa (per la quale affermi che si tratta di "sintassi zuccherina") rimuovono entrambe le lunghezze duplicate , poiché presumibilmente questo è l'intero obiettivo. La stessa cosa si applica qui: l'OP vuole che le categorie duplicate vengano rimosse, non le CreationDates duplicate come fa la DISTINCT ONquery. Se ancora non mi credi, mettiti alla prova.
Inkling

6

Se l'output di MAX (CreationDate) non è voluto - come nell'esempio della domanda originale - l'unica risposta è la seconda affermazione della risposta di Prashant Gupta:

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

Spiegazione: non è possibile utilizzare la clausola ORDER BY in una funzione inline, quindi l'istruzione nella risposta di Prutswonder non è utilizzabile in questo caso, non è possibile inserire una selezione esterna attorno ad essa e scartare la parte MAX (CreationDate).


2

Usa semplicemente questo codice, se vuoi i valori delle colonne [Category] e [CreationDate]

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

Oppure usa questo codice, se vuoi solo i valori della colonna [Categoria].

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

Avrai tutti i record distinti che cosa vuoi.


quelle parentesi graffe [] sono totalmente confuse ... è questa sintassi SQL valida?
m13r

1
Le parentesi sono per l'escape di parole chiave, come Order, event, ecc. Quindi se hai (per esempio) una colonna nella tua tabella chiamata Eventpuoi scrivere [Event]invece di Eventfermare SQL che genera un errore di analisi.
Ben Maxfield

1

2) Order by CreationDate è molto importante

I risultati originali indicavano che "test3" aveva più risultati ...

È molto facile iniziare a utilizzare MAX tutto il tempo per rimuovere i duplicati in Group By ... e dimenticare o ignorare qual è la domanda sottostante ...

L'OP presumibilmente si rese conto che usare MAX gli stava dando l'ultimo "creato" e usando MIN avrebbe dato il primo "creato" ...


3
Questo non sembra davvero rispondere alla domanda, sembra essere un commento sugli usi di altri MAXrispondenti, piuttosto che qualcosa che sta da solo come risposta alla domanda.
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

0

Per sottoquery, dovrebbe funzionare:

    SELECT distinct(Category) from MonitoringJob  where Category in(select Category from MonitoringJob order by CreationDate desc);

Ummm ... non credo che lo farà. La selezione esterna non è ordinata.
Hossam El-Deen,

non funzionerà, sono qui perché non funziona
Amirreza il

-1

Distinct ordina i record in ordine crescente. Se vuoi ordinare in ordine decrescente usa:

SELECT DISTINCT Category
FROM MonitoringJob
ORDER BY Category DESC

Se vuoi ordinare i record in base al campo CreationDate, questo campo deve essere nell'istruzione select:

SELECT DISTINCT Category, creationDate
FROM MonitoringJob
ORDER BY CreationDate DESC

12
Questo verrà eseguito ma non darà ciò di cui l'OP ha bisogno. L'OP vuole categorie distinte, non combinazioni distinte di Category e CreateDate. Questo codice può produrre diverse istanze della stessa categoria, ciascuna con valori di CreationDate diversi.
MatBailie

-1

Puoi usare CTE:

WITH DistinctMonitoringJob AS (
    SELECT DISTINCT Category Distinct_Category FROM MonitoringJob 
)

SELECT Distinct_Category 
FROM DistinctMonitoringJob 
ORDER BY Distinct_Category DESC

-3

Prova dopo, ma non è utile per dati enormi ...

SELECT DISTINCT Cat FROM (
  SELECT Category as Cat FROM MonitoringJob ORDER BY CreationDate DESC
);

4
"La clausola ORDER BY non è valida nelle viste, nelle funzioni inline, nelle tabelle derivate, nelle sottoquery e nelle espressioni di tabella comuni, a meno che non venga specificato anche TOP o FOR XML."
TechplexEngineer

Questo non funziona perché non hai specificato la colonna CreationDate nell'ordine da.
Mauro Bilotti

1
@TechplexEngineer Il tuo commento non è corretto. L'utilizzo ORDER BYnelle sottoquery è assolutamente valido. E qualcuno ha persino votato il tuo commento errato.
Racil Hilan

Lo sto provando e ho lo stesso errore con @TechplexEngineer. Sto usando un ordine personalizzato con custodia quando.
Ege Bayrak

-4

Può essere fatto usando una query interna come questa

$query = "SELECT * 
            FROM (SELECT Category  
                FROM currency_rates                 
                ORDER BY id DESC) as rows               
            GROUP BY currency";

-5
SELECT DISTINCT Category FROM MonitoringJob ORDER BY Category ASC

2
ne ho bisogno ordinato per data di creazione !! è molto importante
rr

Quindi è impossibile aggiungere la colonna che vuoi ordinare da solo? Il tuo esempio mostrava voci in ordine alfabetico. Se devi ordinare per data di creazione, aggiungilo. Non è davvero così difficile.
Furicane

8
-1: L'OP ci ha provato, non ha funzionato, perché è impossibile e apparentemente hai ignorato questo fatto quando hai patrocinato l'OP. Il punto è che l'operatore DISTINCT raccoglierà diversi record con lo stesso valore di categoria, ciascuno con date di creazione potenzialmente diverse. Quindi è logicamente impossibile quando si utilizza DISTINCT. Questo spinge la logica richiesta a GROUP BY invece che a DISTINCT, consentendo un aggregato (MAX) alla data di creazione.
MatBailie

In realtà, se dai un'occhiata più da vicino a quello che ha fatto l'OP, che è un SQL assolutamente malformato, non ho commesso un singolo errore e il risultato fornito corrisponde a quello che ha richiesto. Non mi preoccuperò di -1, leggi la prossima volta prima di correggere le persone. Grazie.
Furicane

8
Suggerisci direttamente di aggiungere il campo CreationDate, anche dicendo "non è poi così difficile". In questo modo si ottiene l'SQL non valido. Hai ottenuto -1 per aver patrocinato l'OP, aver dato un consiglio che riporta l'OP alla dichiarazione che aveva originariamente pubblicato e non aver notato la contesa tra DISTINCT e l'ordinamento da un campo non nel DISTINCT. Inoltre, "b" viene prima di "t" e "1" viene prima di "4", quindi i risultati forniti dall'OP non sono categoricamente in ordine alfabetico. Posso suggerire il tuo consiglio allora: leggi (più attentamente) la prossima volta.
MatBailie
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.