it-swarm.it

Perché i NULL vengono ordinati per primi?

Perché quando abbiamo un valore NULL in una colonna e ordiniamo in base al valore crescente, i NULL vengono ordinati per primi?

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

risultati in

NULL
1
2
3
4

Continuo a pensare che NULL significava "indeterminato" o possibile "sconosciuto". Se questo è vero, non sarebbero ordinati per ultimi, poiché il valore potrebbe essere maggiore di tutti gli altri valori? (O questa è un'opzione di ordinamento da qualche parte?)

Sono su SQL Server 2008R2, ma sospetto che ciò sia vero su tutti i server SQL e probabilmente su tutti i RDBMS.

20
Richard

BOL : un valore NULL indica che il valore è sconosciuto. Un valore di NULL è diverso da un valore vuoto o zero. Nessun due valori null sono uguali. Il confronto tra due valori null o tra un valore NULL e qualsiasi altro valore restituisce sconosciuto perché il valore di ciascun NULL è sconosciuto.

NULL significa sconosciuto. Nessuna altra interpretazione è valida.

Se questo è vero, non sarebbero ordinati per ultimi, poiché il valore potrebbe essere maggiore di tutti gli altri valori?

Non esiste potrebbe essere . Non esiste un valore potenziale . Sconosciuto è sconosciuto è sconosciuto.

Per quanto riguarda il motivo per cui appare prima, piuttosto che ultimo, questo non è soddisfatto dagli standard SQL pubblicati ed è purtroppo lasciato alla discrezione del fornitore RDBMS:

Wikipedia : lo standard SQL non definisce esplicitamente un ordinamento predefinito per Nulls. Invece, sui sistemi conformi, i valori null possono essere ordinati prima o dopo tutti i valori dei dati utilizzando rispettivamente le clausole NULLS FIRST o NULLS LAST dell'elenco ORDER BY. Tuttavia, non tutti i fornitori di DBMS implementano questa funzionalità. I fornitori che non implementano questa funzionalità possono specificare trattamenti diversi per l'ordinamento null nel DBMS.

20

Hai ragione sul fatto che NULL può significare "indeterminato" o "sconosciuto" o "non ancora noto" o "non applicabile". Ma non c'è motivo di mettere i Null prima o alla fine. Se non conosciamo i valori effettivi, allora possono essere piccoli o grandi.

Penso che lo standard per determinare il comportamento desiderato di Nulls durante l'ordinamento sia:

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

Sfortunatamente SQL Server non ha ancora adottato questa sintassi. Se non sbaglio PostgreSQL e Oracle ce l'hanno.

Una soluzione:

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

Un'altra soluzione che necessita di modifiche a seconda del tipo di dati, ma non si preforma bene, poiché non può utilizzare un indice su (test):

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

Non lo so perché è fatto in questo modo, ma per definizione NULLS non può essere paragonato a non NULLS, quindi devono andare all'inizio o alla fine (la risposta di Mark copre questo in molti più dettagli).

Per ottenere il comportamento che desideri - Per quanto ne so, non esiste un'opzione di ordinamento per mettere gli null alla fine, quindi devi evitarlo usando una colonna calcolata per forzarli per ultimi. Tuttavia, in SQL Server non è possibile ordinare in base a una colonna calcolata (CASE WHEN ...) quando i tuoi dati contengono un operatore set (UNION ALL). Così:

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

Funzionerà per l'ordinamento degli null ultimi. Se devi utilizzare UNION (o EXCEPT o INTERSECTS) per generare il tuo set di dati, scarica i tuoi dati in una tabella temporanea come sopra.

3
Simon Righarts

Se hai a che fare con numeri puoi anche usare

ORDER BY -test DESC

NULL sono i valori più bassi possibili, quindi DESC li mette alla fine. Nel frattempo i valori non nulli hanno il segno invertito, quindi DESC in realtà è un ASC sui valori reali. Questo dovrebbe essere più veloce di CASE e suppongo che Query Optimizer possa anche utilizzare gli indici nella colonna test.

0
Luca