web-dev-qa-db-fra.com

Pourquoi les valeurs NULL sont-elles triées en premier?

Pourquoi est-ce que lorsque nous avons une valeur NULL dans une colonne et que nous classons par valeur croissante, les NULL sont triés en premier?

select 1 as test
union all
select 2
union all
select NULL
union all
select 3
union all
select 4
order by test

résulte en

NULL
1
2
3
4

Je continue de penser que NULL signifiait "Indéterminant" ou possible "Inconnu". Si c'est vrai, ne trieraient-ils pas en dernier, car la valeur pourrait être supérieure à toutes les autres valeurs? (Ou est-ce une option de tri quelque part?)

Je suis sur SQL Server 2008R2, mais je soupçonne que cela est vrai sur tous les serveurs SQL, et probablement sur tous les SGBDR.

20
Richard

BOL : Une valeur NULL indique que la valeur est inconnue. Une valeur NULL est différente d'une valeur vide ou nulle. Il n'y a pas deux valeurs nulles égales. Les comparaisons entre deux valeurs nulles ou entre une valeur NULL et toute autre valeur retournent inconnue car la valeur de chaque valeur NULL est inconnue.

NULL signifie inconnu. Aucune autre interprétation n'est valable.

Si c'est vrai, ne trieraient-ils pas en dernier, car la valeur pourrait être supérieure à toutes les autres valeurs?

Il n'y a pas pourrait être . Il n'y a pas de valeur potentielle . Inconnu est inconnu est inconnu.

Quant à savoir pourquoi il apparaît en premier, plutôt qu'en dernier, cela n'est pas pris en charge par les normes SQL publiées et est malheureusement laissé à la discrétion du fournisseur du SGBDR:

Wikipedia : Le standard SQL ne définit pas explicitement un ordre de tri par défaut pour Nulls. Au lieu de cela, sur les systèmes conformes, les valeurs Null peuvent être triées avant ou après toutes les valeurs de données en utilisant respectivement les clauses NULLS FIRST ou NULLS LAST de la liste ORDER BY. Cependant, tous les fournisseurs de SGBD n'implémentent pas cette fonctionnalité. Les fournisseurs qui n'implémentent pas cette fonctionnalité peuvent spécifier des traitements différents pour le tri nul dans le SGBD.

20
Mark Storey-Smith

Vous avez raison de dire que NULL peut signifier "Indéterminant" ou "Uknownn" ou "Pas encore connu" ou "Ne pas appliquer". Mais il n'y a aucune raison de placer les Nulls en premier ou en dernier. Si nous ne connaissons pas les valeurs réelles, alors ils peuvent être petits ou grands.

Je pense que la norme pour déterminer le comportement souhaité des Nulls pendant le tri est:

ORDER BY 
    test NULLS LAST                      --- or NULLS FIRST for the opposite

Malheureusement, SQL-Server n'a pas encore adopté cette syntaxe. Si je ne me trompe pas, PostgreSQL et Oracle l'ont.

Une solution:

ORDER BY 
     CASE WHEN test IS NOT NULL 
            THEN 0 
          ELSE 1 
     END 
   , test

Une autre solution qui doit être ajustée en fonction du type de données - mais qui ne fonctionnera pas bien, car elle ne peut pas utiliser d'index sur (test):

ORDER BY 
    COALESCE(test, 2147483647)               --- if it's a 4-byte signed integer
6
ypercubeᵀᴹ

Je ne sais pas pourquoi c'est fait de cette façon, mais par définition, NULLS ne peut pas être comparé à des non-NULLS, donc ils doivent aller au début ou à la fin (la réponse de Mark couvre cela dans beaucoup plus de détails).

Pour obtenir le comportement que vous souhaitez - Pour autant que je sache, il n'y a pas d'option de tri pour mettre les valeurs nulles en dernier, vous devez donc les héberger en utilisant une colonne calculée pour les forcer en dernier. Cependant, dans SQL Server, vous ne pouvez pas trier par une colonne calculée (CASE WHEN ...) lorsque vos données contiennent un opérateur d'ensemble (UNION ALL). Donc:

CREATE TABLE #sorttest(test int)
INSERT INTO #sorttest values(1)
INSERT INTO #sorttest values(5)
INSERT INTO #sorttest values(4)
INSERT INTO #sorttest values(NULL)
INSERT INTO #sorttest values(3)
INSERT INTO #sorttest values(2)
SELECT test
FROM #sorttest
ORDER BY CASE WHEN test IS NULL THEN 1 ELSE 0 END, test

DROP TABLE #sorttest

Fonctionne pour le tri des valeurs nulles en dernier. Si vous devez utiliser UNION (ou EXCEPT ou INTERSECTS) pour générer votre ensemble de données, puis videz vos données dans une table temporaire comme ci-dessus.

3
Simon Righarts

Si vous avez affaire à des chiffres, vous pouvez également utiliser

ORDER BY -test DESC

NULL sont les valeurs les plus basses possibles, donc DESC les met à la fin. Pendant ce temps, les valeurs non nulles ont le signe inversé, donc DESC est en fait un ASC sur les valeurs réelles. Cela devrait être plus rapide que CASE et je suppose que l'optimiseur de requête peut également utiliser des index sur la colonne test.

0
Luca