Comment utiliser DISTINCT et ORDER BY dans la même instruction SELECT?


117

Après avoir exécuté l'instruction suivante:

SELECT  Category  FROM MonitoringJob ORDER BY CreationDate DESC

J'obtiens les valeurs suivantes de la base de données:

test3
test3
bildung
test4
test3
test2
test1

mais je veux que les doublons soient supprimés, comme ceci:

bildung
test4
test3
test2
test1

J'ai essayé d'utiliser DISTINCT mais cela ne fonctionne pas avec ORDER BY dans une instruction. Veuillez aider.

Important:

  1. Je l'ai essayé avec:

    SELECT DISTINCT Category FROM MonitoringJob ORDER BY CreationDate DESC

    ça ne marche pas.

  2. La commande par CreationDate est très importante.


1
Comment ça ne marche pas? Mauvaise sortie?
Fedearne

Réponses:


195

Le problème est que les colonnes utilisées dans le ORDER BYne sont pas spécifiées dans le DISTINCT. Pour ce faire, vous devez utiliser une fonction d'agrégation pour effectuer le tri et utiliser a GROUP BYpour effectuer le DISTINCTtravail.

Essayez quelque chose comme ceci:

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

99
Vous n'avez même pas besoin du mot clé DISTINCT si vous regroupez par catégorie.
MatBailie

18

Colonnes de clé de tri étendues

La raison pour laquelle ce que vous voulez faire ne fonctionne pas est à cause de l' ordre logique des opérations dans SQL , qui, pour votre première requête, est (simplifié):

  • FROM MonitoringJob
  • SELECT Category, CreationDatec'est-à-dire ajouter une colonne de clé de tri étendue
  • ORDER BY CreationDate DESC
  • SELECT Categoryc'est-à-dire supprimer à nouveau la colonne de clé de tri étendue du résultat.

Ainsi, grâce à la fonctionnalité de colonne de clé de tri étendue standard SQL , il est tout à fait possible de trier par quelque chose qui n'est pas dans la SELECTclause, car il y est temporairement ajouté en arrière-plan.

Alors, pourquoi cela ne fonctionne-t-il pas avec DISTINCT?

Si nous ajoutons l' DISTINCTopération, elle serait ajoutée entre SELECTet ORDER BY:

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

Mais maintenant, avec la colonne de clé de tri étendue CreationDate , la sémantique duDISTINCT opération a été modifiée, de sorte que le résultat ne sera plus le même. Ce n'est pas ce que nous voulons, donc le standard SQL et toutes les bases de données raisonnables interdisent cette utilisation.

Solutions de contournement

Il peut être émulé avec la syntaxe standard comme suit

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

Ou, tout simplement (dans ce cas), comme le montre également Prutswonder

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

J'ai blogué sur SQL DISTINCT et ORDER BY plus en détail ici .


1
Je pense que vous vous trompez sur la façon dont DISTINCT ONfonctionne et à peu près sûr que cela n'aide pas ici. L'expression entre parenthèses est ce qui est utilisé pour déterminer la distinction (la condition de regroupement). S'il y a différentes catégories avec la même chose, CreationDateune seule d'entre elles apparaîtra dans le résultat! Comme je me demandais si j'avais peut-être tort, j'ai également chargé la base de données d'exemple dans votre article de blog pour vérifier: la DISTINCT ONrequête que vous y avez donnée a produit un total de 1000 résultats (avec beaucoup de doublons length) tandis que la requête ci-dessous a donné seulement 140 valeurs (uniques).
Inkling

@Inkling: Merci pour votre temps. Le PO veut explicitement supprimer les "doublons". Voir le libellé de l'OP "mais je veux que les doublons soient supprimés, comme ceci" . Vous avez probablement fait une erreur en copiant les requêtes de mon article de blog. Il existe deux requêtes, une qui utilise DISTINCT(non ON) et une qui utilise DISTINCT ON. Veuillez noter que ce dernier ne supprime pas explicitement les longueurs en double, mais les titres en double. Je pense que ma réponse ici est tout à fait correcte.
Lukas Eder

1
Mon point est que vos DISTINCT ONconditions suppriment les doublons en utilisant la mauvaise condition. Dans votre article de blog, la DISTINCT ONrequête supprime effectivement les titres en double , mais la DISTINCTrequête au-dessus et la requête en dessous (pour laquelle vous prétendez qu'il s'agit d'un «sucre de syntaxe») suppriment toutes les deux les longueurs en double , car c'est vraisemblablement l'objectif global. La même chose s'applique ici: l'OP veut que les catégories en double soient supprimées, pas les CreationDates en double comme le DISTINCT ONfait la requête. Si vous ne me croyez toujours pas, testez par vous-même.
Inkling

6

Si la sortie de MAX (CreationDate) n'est pas souhaitée - comme dans l'exemple de la question d'origine - la seule réponse est la deuxième déclaration de la réponse de Prashant Gupta:

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

Explication: vous ne pouvez pas utiliser la clause ORDER BY dans une fonction en ligne, donc l'instruction dans la réponse de Prutswonder n'est pas utilisable dans ce cas, vous ne pouvez pas mettre une sélection externe autour d'elle et supprimer la partie MAX (CreationDate).


2

Utilisez simplement ce code, si vous voulez des valeurs des colonnes [Category] et [CreationDate]

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

Ou utilisez ce code, si vous ne voulez que les valeurs de la colonne [Category].

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

Vous aurez tous les enregistrements distincts que vous voulez.


ces accolades [] sont totalement déroutantes ... est-ce que cette syntaxe SQL est valide?
m13r

1
Les crochets servent à échapper les mots-clés, tels que Order, event, etc. donc si vous avez (par exemple) une colonne dans votre table appelée, Eventvous pouvez écrire [Event]au lieu de Eventpour empêcher SQL de lancer une erreur d'analyse.
Ben Maxfield

1

2) L'ordre par CreationDate est très important

Les résultats originaux indiquaient que «test3» avait plusieurs résultats ...

Il est très facile de commencer à utiliser MAX tout le temps pour supprimer les doublons dans Group By ... et oublier ou ignorer la question sous-jacente ...

L'OP s'est vraisemblablement rendu compte que l'utilisation de MAX lui donnait le dernier "créé" et l'utilisation de MIN donnerait le premier "créé" ...


3
Cela ne semble pas vraiment répondre à la question, cela semble être un commentaire sur les utilisations d'autres répondants MAX, plutôt que quelque chose qui constitue une réponse autonome à la question.
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

Par sous-requête, cela devrait fonctionner:

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

Ummm ... Je ne pense pas que ce sera le cas. La sélection externe n'est pas triée.
Hossam El-Deen

ça ne fonctionnera pas, je suis ici parce que ça ne marche pas
Amirreza

-1

Distinct triera les enregistrements par ordre croissant. Si vous souhaitez trier par ordre décroissant, utilisez:

SELECT DISTINCT Category
FROM MonitoringJob
ORDER BY Category DESC

Si vous souhaitez trier les enregistrements en fonction du champ CreationDate, ce champ doit se trouver dans l'instruction select:

SELECT DISTINCT Category, creationDate
FROM MonitoringJob
ORDER BY CreationDate DESC

12
Cela s'exécutera mais ne donnera pas ce dont l'OP a besoin. Le PO veut des catégories distinctes, pas des combinaisons distinctes de Category et CreateDate. Ce code peut générer plusieurs instances de la même catégorie, chacune avec des valeurs CreationDate différentes.
MatBailie

-1

Vous pouvez utiliser CTE:

WITH DistinctMonitoringJob AS (
    SELECT DISTINCT Category Distinct_Category FROM MonitoringJob 
)

SELECT Distinct_Category 
FROM DistinctMonitoringJob 
ORDER BY Distinct_Category DESC

-3

Essayez ensuite, mais ce n'est pas utile pour les données volumineuses ...

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

4
"La clause ORDER BY n'est pas valide dans les vues, les fonctions en ligne, les tables dérivées, les sous-requêtes et les expressions de table courantes, sauf si TOP ou FOR XML est également spécifié."
TechplexEngineer

Cela ne fonctionne pas car vous n'avez pas spécifié la colonne CreationDate sur la commande par.
Mauro Bilotti

1
@TechplexEngineer Votre commentaire est incorrect. L'utilisation ORDER BYdans les sous-requêtes est absolument valide. Et quelqu'un a même voté pour votre commentaire incorrect.
Racil Hilan

J'essaye ceci et ai la même erreur avec @TechplexEngineer. J'utilise une commande personnalisée avec cas quand.
Ege Bayrak

-4

Cela peut être fait en utilisant une requête interne comme ceci

$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
j'en ai besoin trié par date de création !! c'est très important
rr

Est-il donc impossible d'ajouter la colonne que vous souhaitez commander vous-même? Votre exemple montre les entrées classées par ordre alphabétique. Si vous avez besoin de commander par date de création, ajoutez-le simplement. Ce n'est vraiment pas si difficile.
Furicane

8
-1: Le PO a essayé ça, ça n'a pas marché, parce que c'est impossible et vous avez apparemment ignoré ce fait lorsque vous fréquentez l'OP. Le fait est que l'opérateur DISTINCT rassemblera plusieurs enregistrements avec la même valeur de catégorie, chacun avec des dates de création potentiellement différentes. Ainsi, il est logiquement impossible lors de l'utilisation de DISTINCT. Cela pousse la logique requise vers un GROUP BY au lieu de DISTINCT, permettant un agrégat (MAX) à la date de création.
MatBailie

En fait, si vous regardez de plus près ce qu'a fait l'OP, qui est un SQL absolument malformé - je n'avais pas fait une seule erreur et le résultat donné correspond à celui qu'il a demandé. Je ne prendrai pas la peine de -1, lisez juste la prochaine fois avant de corriger les gens. Je vous remercie.
Furicane

8
Vous suggérez directement d'ajouter le champ CreationDate, en disant même "ce n'est vraiment pas si difficile". Cela produit le SQL mal formé. Vous avez -1 pour avoir patronné l'OP, donner des conseils qui ramènent l'OP à la déclaration qu'il a initialement publiée et ne pas avoir remarqué le conflit entre DISTINCT et la commande par un champ ne figurant pas dans DISTINCT. De plus, «b» vient avant «t» et «1» vient avant «4», ainsi les résultats donnés par l'OP ne sont pas catégoriquement classés par ordre alphabétique. Puis-je suggérer votre propre conseil: lisez (plus attentivement) la prochaine fois.
MatBailie
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.