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

20

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.

Richard
la source
1
Oracle le répertorie en dernier. Cela m'a foutu une fois, croyant qu'il devrait se comporter comme SQL Server.
Andrei Rînea
2
"Si c'est vrai, ne trieraient-ils pas en dernier, car la valeur pourrait être supérieure à toutes les autres valeurs". La valeur peut également être inférieure à toutes les autres valeurs. Pour moi, il est intuitif qu'une valeur de falsey comme null soit à l'extrémité inférieure. Et pratique, car dans la pratique, vous voulez souvent utiliser l' descordre pour afficher les choses les plus importantes ou les plus récentes, auquel cas je serais heureux que les choses nulles soient les dernières.
mahemoff
La base de données fait ce que vous lui demandez de faire. Si vous savez que vos données contiennent des valeurs nulles et que vous avez une raison commerciale de trier les données d'une certaine manière, vous devez spécifier cela dans la requête ou dans le code / la vue qui traite / affiche les données. Ne laissez jamais le tri au comportement de la base de données par défaut.
rien n'est nécessaire

Réponses:

19

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 aucune 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 les null. 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.

Mark Storey-Smith
la source
C'est donc un appel au jugement. Cela a du sens. Merci!
Richard
6

Vous avez raison, cela NULLpeut 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é de 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
ypercubeᵀᴹ
la source
De cette façon, ORDER BY COALESCE (test, 2147483647) SQL Server ne peut pas utiliser Index.
Ardalan Shahgholi
3

Je ne sais pas pourquoi cela 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 plus en détail).

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 set ( 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 EXCEPTou INTERSECTS) pour générer votre ensemble de données, sauvegardez vos données dans une table temporaire comme ci-dessus.

Simon Righarts
la source
... ou utilisez la sortie UNIONed comme table dérivée.
Andriy M
0

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

ORDER BY -test DESC

NULLsont les valeurs les plus basses possibles, les DESCmet donc à la fin. Pendant ce temps, les valeurs non nulles ont le signe inversé, ce DESCqui correspond en fait ASCaux valeurs réelles. Cela devrait être plus rapide que CASEet je suppose que l'optimiseur de requêtes peut également utiliser des index sur la testcolonne.

Luca
la source
3
Non, il ne pourrait pas utiliser d'index pour le tri. Sauf si vous avez un index sur l'expression calculée (- test).
ypercubeᵀᴹ
1
Intelligent, même s'il est limité aux données numériques uniquement (approprié pour l'exemple OP de toute façon). Je ne sais pas si cela serait en effet plus rapide que d'utiliser CASE mais je suis sûr qu'il n'utiliserait pas d'index (sauf si c'est ce que dit @ ypercubeᵀᴹ - mais alors une expression CASE pourrait être indexée exactement de la même manière).
Andriy M