Perché ANSI SQL definisce SUM (nessuna riga) come NULL?


28

Lo standard ANSI SQL definisce (capitolo 6.5, specifica della funzione set) il seguente comportamento per le funzioni aggregate su set di risultati vuoti:

COUNT(...) = 0
AVG(...) = NULL
MIN(...) = NULL
MAX(...) = NULL
SUM(...) = NULL

Restituire NULL per AVG, MIN e MAX ha perfettamente senso, poiché la media, il minimo e il massimo di un set vuoto non sono definiti.

L'ultimo, tuttavia, fastidi me: Matematicamente, la somma di un insieme vuoto è ben definita: 0. Usando 0, l' elemento neutro dell'aggiunta, come il caso base rende tutto coerente:

SUM({})        = 0    = 0
SUM({5})       = 5    = 0 + 5
SUM({5, 3})    = 8    = 0 + 5 + 3
SUM({5, NULL}) = NULL = 0 + 5 + NULL

La definizione SUM({})come nullfondamentalmente rende "senza righe" un caso speciale che non si adatta agli altri:

SUM({})     = NULL  = NULL
SUM({5})    = 5    != NULL + 5 (= NULL)
SUM({5, 3}) = 8    != NULL + 5 + 3 (= NULL)

C'è qualche ovvio vantaggio della scelta fatta (SUM essendo NULL) che mi sono perso?


Nota: questa è una versione generalizzata di una domanda che ho posto su StackOverflow specificamente su SQL Server .
Heinzi,

5
Sì, sono d'accordo: COUNT e SUM non si comportano in modo coerente.
AK,

Risposte:


20

Temo che il motivo sia semplicemente che le regole sono state impostate in modo ad hoc (come molte altre "caratteristiche" dello standard ISO SQL) in un momento in cui le aggregazioni SQL e la loro connessione con la matematica erano meno comprese di quanto non siano ora (*).

È solo una delle moltissime incongruenze nel linguaggio SQL. Rendono la lingua più difficile da insegnare, più difficile da imparare, più difficile da capire, più difficile da usare, più difficile per quello che vuoi, ma è così che vanno le cose. Le regole non possono essere modificate "a freddo" e "proprio così", per ovvi motivi di compatibilità con le versioni precedenti (se il comitato ISO pubblica una versione finale dello standard e i fornitori decidono quindi di implementare tale standard, tali fornitori non apprezzeranno molto se in una versione successiva, le regole sono cambiate in modo tale che le implementazioni esistenti (conformi) della precedente versione dello standard "non rispettano automaticamente" la nuova versione ...)

(*) Ora si comprende meglio che le aggregazioni su un set vuoto si comportano in modo più coerente se restituiscono sistematicamente il valore dell'identità (= quello che si chiama "elemento neutro") dell'operatore binario sottostante a portata di mano. L'operatore binario sottostante per COUNT e SUM è un'aggiunta e il suo valore di identità è zero. Per MIN e MAX, quel valore di identità è il valore più alto e più basso del tipo a portata di mano, rispettivamente, se i tipi interessati sono finiti. Tuttavia, casi come media, mezzi armonici, mediane, ecc. Sono estremamente intricati ed esotici in questo senso.


Penso che null abbia senso su un set vuoto con min e max. Potresti dire che un valore di identità è davvero sconosciuto, ma la somma di nessun valore è 0 per lo stesso motivo per cui n * 0 è sempre 0. Ma min e max sono diversi. Non penso che il risultato sia correttamente definito correndo attraverso nessun record.
Chris Travers,

Anche avg () su un set null ha senso come null perché 0/0 non è definito correttamente in questo contesto.
Chris Travers,

5
MIN e MAX non sono così diversi. Prendi un operatore binario sottostante LOWESTOF (x, y) e HIGHESTOF (x, y) rispettivamente. Questi operatori binari hanno un valore di identità. Perché in entrambi i casi (se il tipo in questione è finito), esiste effettivamente un valore z tale che forall x: LOWESTOF (z, x) = x e forall y: HIGHESTOF (y, z) = y. (Il valore dell'identità non è lo stesso per entrambi i casi, ma esiste per entrambi i casi.) Concordo sul fatto che i risultati sembrano estremamente controintuitivi a prima vista, ma non si può negare la realtà matematica.
Erwin Smout,

@Erwin: sono d'accordo su tutti i tuoi punti, tranne per il fatto che l'identità di alcune operazioni, come HIGHEST()molte non essere un elemento del tipo di dati, come per Reals dove l'identità sarebbe il -Infinity(e +Infinityper LOWEST())
ypercubeᵀᴹ

1
Kiwi @SQL. Ti stai dimenticando del controllo del tipo statico? Se espressioni come SUM () sono gestite dal controllo del tipo statico come se restituissero sempre un numero intero, allora ovviamente dovrebbe essere impossibile per l'invocazione SUM () restituire qualcosa che non è un numero intero (ad esempio una relazione vuota).
Erwin Smout,

3

In senso pragmatico, il risultato esistente di NULLè utile. Considera la seguente tabella e le seguenti dichiarazioni:

C1 C2
-- --
 1  3 
 2 -1 
 3 -2 

SELECT SUM(C2) FROM T1 WHERE C1 > 9;

SELECT SUM(C2) FROM T1 WHERE C1 < 9;

La prima istruzione restituisce NULL e la seconda restituisce zero. Se un set vuoto restituisse zero perché SUMavremmo bisogno di un altro mezzo per distinguere una somma reale di zero da un set vuoto, magari usando count. Se vogliamo davvero zero per l'insieme vuoto, allora un semplice COALESCEfornirà quel requisito.

SELECT COALESCE(SUM(C2),0) FROM T1 WHERE C1 > 9;

1
di conseguenza., SUM (unione di set1 e set2) <> SUM (set1) + SUM (set2), poiché qualsiasi numero + NULL = NULL. ha senso per te?
AK,

2
@Leigh: l'utilizzo in COALESCE()questo modo non distinguerà la 0somma ( ) di un set vuoto dalla NULLsomma () (supponiamo che la tabella avesse una (10, NULL)riga.
ypercubeᵀᴹ

Inoltre, non possiamo ancora distinguere SUM (set vuoto) da SUM (set di uno o più NULL). Dobbiamo distinguere affatto?
AK,

@AlexKuznetsov - Possiamo distinguere una somma di un set vuoto da una somma di un set che contiene uno o più valori null purché almeno una riga contenga un valore. Hai ragione nel dire che se il set contiene solo NULL, non possiamo distinguere il set NULL da questo set di tutti i valori NULL. Il mio punto non era che fosse utile in ogni caso, ma semplicemente che potesse essere utile. Se ho SUMuna colonna e torno a zero, lo so senza dover controllare che ci sia almeno una riga non NULL utilizzata per mostrarmi il risultato.
Leigh Riffel,

@ypercude - Hai assolutamente ragione. Il mio punto era che l'attuale comportamento di SUM distingue un set vuoto da un set che contiene valori (anche se alcuni sono nulli). È più semplice usare COALESCE quando non è richiesta la distinzione che usare qualcosa come DECODE(count(c2),0,NULL,sum(c2))quando lo è.
Leigh Riffel,

-1

La differenza principale che posso vedere è per quanto riguarda il tipo di dati. COUNT ha un tipo di ritorno ben definito: un numero intero. Tutti gli altri dipendono dal tipo di colonna / espressione che stanno guardando. Il loro tipo di ritorno deve essere compatibile con tutti i membri dell'insieme (think float, currency, decimal, bcd, timespan, ...). Poiché non esiste un set, non è possibile implicare un tipo restituito, quindi NULL è l'opzione migliore.

Nota: nella maggior parte dei casi è possibile implicare un tipo restituito dal tipo di colonna che si sta guardando, ma è possibile eseguire SUM non solo sulle colonne ma su tutti i tipi di cose. Imporre un tipo di ritorno potrebbe diventare molto difficile, se non impossibile, in determinate circostanze, specialmente quando si pensa a possibili espansioni dello standard (vengono in mente i tipi dinamici).


5
Perché non possiamo implicare un tipo restituito in SUM(column)un'espressione? Non abbiamo tabelle vuote - e lì tutte le colonne hanno tipi definiti? Perché dovrebbe essere diverso per un set di risultati vuoto?
ypercubeᵀᴹ

5
Ti sbagli dove dici "poiché non c'è SET ". C'è un set. L'insieme di tutti i possibili valori del tipo dichiarato delle colonne o espressioni interessate. Quel tipo dichiarato esiste anche se la tabella che stai guardando è vuota. Anche i tavoli vuoti hanno ancora un'intestazione. E quel tipo dichiarato è esattamente il tuo "tipo di ritorno implicito".
Erwin Smout,

Entrambi avete davvero letto la mia nota? Sì, funzionerebbe per i SUM basati su colonne sin da ora. Ma non appena si incontra una colonna di tipo di dati variabile (non ancora in SQL Server), si è sfortunati.
TToni,

2
Come definirai la somma in quel caso? Quale sarà il risultato di 24 + 56.07 + '2012-10-05' + 'Red'essere? Voglio dire che non c'è pinta nel preoccuparsi di come SUM()si comporterà quando abbiamo un problema nella definizione dell'aggiunta.
ypercubeᵀᴹ

1
@TToni: "soprattutto quando si pensa a possibili ampliamenti dello standard" non è il contesto a cui si riferiva l'OP. il PO si riferiva molto chiaramente all'attuale versione dello standard, che non include alcun tipo di nozione di "tipi dinamici" o alcuni di questi. (Oh, e ho solo commentato, ma non ho votato. A parte quella piccola
lettera di cui ho discusso
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.