SQL: comment faire en sorte que les valeurs nulles viennent en dernier lors du tri croissant

297

J'ai une table SQL avec un champ datetime. Le champ en question peut être nul. J'ai une requête et je veux que les résultats soient triés dans l'ordre croissant par le champ datetime, mais je veux des lignes où le champ datetime est nul à la fin de la liste, pas au début.

Existe-t-il un moyen simple d'accomplir cela?

David Božjak
la source

Réponses:

394
select MyDate
from MyTable
order by case when MyDate is null then 1 else 0 end, MyDate
RedFilter
la source
2
Notez cependant que si vous placez un index sur la colonne de tri pour améliorer les performances (*), cette méthode compliquera quelque peu le plan de requête et perdra une grande partie des performances. * - les index ont fourni les données pré-triées, évitant ainsi un tri par exécution de requête. Il est conseillé de filtrer les enregistrements NULL si possible pour éviter complètement ce problème.
redcalx
2
Belle réponse également donnée ici avec toutes les façons possibles avec des avantages et des inconvénients nickstips.wordpress.com/2010/09/30/…
sudhAnsu63
40
order by case when MyDate is null then 1 else 0 endest une très longue façon de direORDER BY MyDate IS NULL
Martin
5
@Martin Notez que cette question n'est pas taguée mysql. J'ai fourni une solution généralisée - il existe de nombreuses façons différentes de faire la même chose sur différents dbs.
RedFilter
1
@KyleDelaney Car order by 0est interprété comme un index de colonne et les index de colonne sont basés sur 1. Pour trier une requête par la 3ème colonne, vous pouvez dire order by 3(ce qui est une idée terrible pour les requêtes de production), mais très pratique (tel quel *) lors de l'expérimentation.
RedFilter
159

(Un "peu" en retard, mais cela n'a pas du tout été mentionné)

Vous n'avez pas spécifié votre SGBD.

En SQL standard (et dans les SGBD les plus modernes comme Oracle, PostgreSQL, DB2, Firebird, Apache Derby, HSQLDB et H2), vous pouvez spécifier NULLS LASTou NULLS FIRST:

Utilisez NULLS LASTpour les trier jusqu'à la fin:

select *
from some_table
order by some_column DESC NULLS LAST
un cheval sans nom
la source
16
AFAIK NULLS FIRSTet NULLS LASTont été ajoutés dans SQL: 2003 mais aucune implémentation standard n'est disponible dans les différents DMBS. Selon le moteur de base de données, utilisez ORDER BY expr some_column DESC NULLS LAST(Oracle), ORDER BY ISNULL(some_column, 1), some_column ASC(MSSQL) ou ORDER BY ISNULL(some_column), some_column ASC(MySQL avec une implémentation ISNULL () différente).
SaschaM78
3
@ SaschaM78: le tri par défaut des valeurs NULL dépend du SGBD. Certains les trient à la fin, d'autres au début. Certains ne se soucient pas de ASC/ DESCavec des nulls certains le font. La seule façon de garantir cela est donc d'utiliser NULL FIRST/LAST si le SGBD le prend en charge. À elle documente ce que vous avez l'intention. L'utilisation de isnull()ou d'autres fonctions est une solution de contournement pour le support manquant de NULLS FIRST/LAST(qui est pris en charge par Oracle, PostgreSQL, DB2, Firebird, Apache Derby, HSQLDB et H2)
a_horse_with_no_name
Vous avez parfaitement raison, je voulais juste ajouter le fait qu'il existe de nombreux SGBD qui ne suivent pas (encore) la norme ou qui ont leurs spécialités comme Oracle nécessitant le exprmot - clé lors de l'utilisation de NULLS FIRST / LAST. Et merci pour les valeurs nulles affichées en premier / dernier variant d'un type de base de données à l'autre, je ne le savais pas!
SaschaM78
2
Cela semble prometteur, mais malheureusement, je l'ai essayé et NULLS LASTn'ai pas fonctionné dans ma base de données MySQL.
AdmiralThrawn
1
@AdmiralAdama: vous pouvez toujours passer à Postgres
a_horse_with_no_name
35

Je suis également tombé sur cela et ce qui suit semble faire l'affaire pour moi, sur MySQL et PostgreSQL:

ORDER BY date IS NULL, date DESC

comme trouvé à https://stackoverflow.com/a/7055259/496209

Luksurious
la source
Cela semble prometteur, mais malheureusement, je l'ai essayé IS NULLet IS NOT NULLn'ai pas fonctionné dans ma base de données MySQL.
AdmiralThrawn
14
order by coalesce(date-time-field,large date in future)
Gratzy
la source
10
Bien que cela fonctionne généralement, il convient de noter que cette réponse présente quelques problèmes: une date importante à l'avenir peut entrer en collision ou être inférieure aux données réelles, ce qui entraîne un tri imparfait. De plus, c'est une solution de «nombre magique» qui n'est pas auto-documentée.
RedFilter
Cela s'avère être une excellente alternative à la réponse @RedFilter lorsque vous devez comparer la colonne en question à une autre colonne de date. J'utilise ceci pour la liste d'ancienneté syndicale. Si l'employé a une date qualifiée (qui peut être annulée), cette date enregistre des bulles vers le haut, sinon utilisez HireDate. L'utilisation de ORDER BY ISNULL (QualifiedDate, '1-1-2099'), HireDate, LastName, etc. fait que la date qualifiée n'entre pas en conflit avec la date HiredDate et la liste de souvenirs correcte est produite.
Alan Fisher
14

Vous pouvez utiliser la fonction intégrée pour vérifier null ou non null, comme ci-dessous. Je le teste et son bon fonctionnement.

select MyDate from MyTable order by ISNULL(MyDate,1) DESC, MyDate ASC;

Majdi M. Aburahelah
la source
Pour que les dates fonctionnent avec mssql, j'ai trouvé utile de mettre une date future dans la fonction ISNULL, c'est-à-dire ISNULL (MyDate, '2100-01-01')
Emi-C
Dans MySQL, il dit que je passe trop de paramètres. J'ai réussi à analyser cela en faisant ISNULL(MyDate) DESC, MyDate ASC, mais cela n'a pas été trié dans le bon ordre.
AdmiralThrawn
12

Si votre moteur le permet ORDER BY x IS NULL, xou l' ORDER BY x NULLS LASTutilise. Mais si ce n'est pas le cas, cela pourrait aider:

Si vous triez par type numérique, vous pouvez le faire: (Emprunter le schéma d' une autre réponse .)

SELECT *          
FROM Employees
ORDER BY ISNULL(DepartmentId*0,1), DepartmentId;

résultat montrant trié par DepartmentId avec des valeurs nulles en dernier

Tout nombre non nul devient 0 et les valeurs nulles deviennent 1, ce qui trie les valeurs nulles en dernier.

Vous pouvez également le faire pour les chaînes:

SELECT *
FROM Employees
ORDER BY ISNULL(LEFT(LastName,0),'a'), LastName

résultat montrant trié par LastName avec des null en dernier

Parce que 'a'> ''.

Cela fonctionne même avec les dates en contraignant à un entier nullable et en utilisant la méthode pour les entiers ci-dessus:

SELECT *
FROM Employees
ORDER BY ISNULL(CONVERT(INT, HireDate)*0, 1), HireDate

(Supposons que le schéma a HireDate.)

Ces méthodes évitent d'avoir à trouver ou à gérer une valeur "maximale" de chaque type ou à corriger les requêtes si le type de données (et le maximum) change (deux problèmes dont souffrent les autres solutions ISNULL). De plus, ils sont beaucoup plus courts qu'un CASE.

infogulch
la source
Fonctionne très bien ! Merci
Vani
8

Lorsque votre colonne d'ordre est numérique (comme un rang), vous pouvez la multiplier par -1, puis l'ordre décroissant. Il gardera l'ordre que vous attendez mais mettra NULL en dernier.

select *
from table
order by -rank desc
Luizgrs
la source
Était sur le point de commenter cela. Je l' ai appris de stackoverflow.com/a/8174026/1193304 et c'est génial
Chris
4
SELECT *          
FROM Employees
ORDER BY ISNULL(DepartmentId, 99999);

Voir cet article de blog .

user3923117
la source
3

Merci à RedFilter d'avoir fourni une excellente solution au problème de bogue du tri du champ datetime nullable.

J'utilise la base de données SQL Server pour mon projet.

La modification de la valeur nulle de datetime à «1» résout le problème du tri pour la colonne de type de données datetime. Cependant, si nous avons une colonne avec un type de données autre que datetime, elle ne parvient pas à être gérée.

Pour gérer un tri de colonne varchar, j'ai essayé d'utiliser 'ZZZZZZZ' car je savais que la colonne n'avait pas de valeurs commençant par 'Z'. Cela a fonctionné comme prévu.

Sur les mêmes lignes, j'ai utilisé les valeurs maximales +1 pour les types de données int et autres pour obtenir le tri comme prévu. Cela m'a également donné les résultats requis.

Cependant, il serait toujours idéal d'obtenir quelque chose de plus facile dans le moteur de base de données lui-même qui pourrait faire quelque chose comme:

Order by Col1 Asc Nulls Last, Col2 Asc Nulls First 

Comme mentionné dans la réponse fournie par a_horse_with_no_name.

Kasim Husaini
la source
3

Dans Oracle, vous pouvez utiliser NULLS FIRSTou NULLS LAST: spécifie que les valeurs NULL doivent être renvoyées avant / après les valeurs non NULL:

ORDER BY { column-Name | [ ASC | DESC ] | [ NULLS FIRST | NULLS LAST ] }

Par exemple:

ORDER BY date DESC NULLS LAST

Réf: http://docs.oracle.com/javadb/10.8.3.0/ref/rrefsqlj13658.html

Joaquinglezsantos
la source
3

Si vous utilisez MariaDB, ils mentionnent les éléments suivants dans la documentation des valeurs NULL .

Commande

Lorsque vous commandez par un champ qui peut contenir des valeurs NULL, tous les NULL sont considérés comme ayant la valeur la plus basse. Ainsi, la commande dans l'ordre DESC verra les NULL apparaître en dernier. Pour forcer les valeurs NULL à être considérées comme des valeurs les plus élevées, on peut ajouter une autre colonne qui a une valeur plus élevée lorsque le champ principal est NULL. Exemple:

SELECT col1 FROM tab ORDER BY ISNULL(col1), col1;

Ordre décroissant, avec NULL en premier:

SELECT col1 FROM tab ORDER BY IF(col1 IS NULL, 0, 1), col1 DESC;

Toutes les valeurs NULL sont également considérées comme équivalentes aux fins des clauses DISTINCT et GROUP BY.

Ce qui précède montre deux façons de classer par valeurs NULL, vous pouvez également les combiner avec les mots-clés ASC et DESC. Par exemple, l'autre façon d'obtenir d'abord les valeurs NULL serait:

SELECT col1 FROM tab ORDER BY ISNULL(col1) DESC, col1;
--                                         ^^^^
3limin4t0r
la source
2

La solution utilisant le "cas" est universelle, mais n'utilisez pas les index.

order by case when MyDate is null then 1 else 0 end, MyDate

Dans mon cas, j'avais besoin de performances.

 SELECT smoneCol1,someCol2  
 FROM someSch.someTab
 WHERE someCol2 = 2101 and ( someCol1 IS NULL ) 
  UNION   
 SELECT smoneCol1,someCol2
 FROM someSch.someTab
 WHERE someCol2 = 2101 and (  someCol1 IS NOT NULL)  
Adam111p
la source
1
Si vous êtes intéressé par la performance, vous devriez l'utiliser UNION ALL.
RedFilter
0
order by -cast([nativeDateModify] as bigint) desc
paparazzo
la source