¿Por qué ANSI SQL define SUM (sin filas) como NULL?


28

El estándar ANSI SQL define (capítulo 6.5, especificación de la función de conjunto) el siguiente comportamiento para funciones agregadas en conjuntos de resultados vacíos:

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

Devolver NULL para AVG, MIN y MAX tiene mucho sentido, ya que el promedio, mínimo y máximo de un conjunto vacío no está definido.

El último, sin embargo, que me molesta: Matemáticamente, la suma de un conjunto vacío está bien definido: 0. Usando 0, el elemento neutral de suma, como el caso base hace que todo sea consistente

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

Definir SUM({})como nullbásicamente hace que "sin filas" sea un caso especial que no encaja con los demás:

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

¿Hay alguna ventaja obvia de la elección que se hizo (SUM siendo NULL) que me he perdido?



55
Sí, estoy de acuerdo: COUNT y SUM no se comportan de manera consistente.
AK

Respuestas:


20

Me temo que la razón es simplemente que las reglas se establecieron de manera ad hoc (como muchas otras "características" del estándar ISO SQL) en un momento en que las agregaciones SQL y su conexión con las matemáticas eran menos entendidas de lo que son ahora. (*)

Es solo una de las muchas inconsistencias en el lenguaje SQL. Hacen que el idioma sea más difícil de enseñar, más difícil de aprender, más difícil de entender, más difícil de usar, más difícil de lo que quieras, pero así son las cosas. Las reglas no se pueden cambiar "en frío" y "así", por razones obvias de compatibilidad con versiones anteriores (si el comité ISO publica una versión final del estándar, y los proveedores se proponen implementar ese estándar, entonces esos proveedores no lo apreciarán mucho si en una versión posterior, las reglas se cambian de tal manera que las implementaciones existentes (compatibles) de la versión anterior del estándar "no cumplan automáticamente" la nueva versión ...)

(*) Ahora se entiende mejor que las agregaciones sobre un conjunto vacío se comportan de manera más consistente si devuelven sistemáticamente el valor de identidad (= lo que llama el 'elemento neutral') del operador binario subyacente en cuestión. Ese operador binario subyacente para COUNT y SUM es la suma, y ​​su valor de identidad es cero. Para MIN y MAX, ese valor de identidad es el valor más alto y más bajo del tipo en cuestión, respectivamente, si los tipos en cuestión son finitos. Sin embargo, casos como el promedio, los medios armónicos, las medianas, etc. son extremadamente intrincados y exóticos a este respecto.


Creo que nulo tiene sentido sobre un conjunto vacío con min y max. Se podría decir que un valor de identidad allí es realmente desconocido, pero la suma de ningún valor es 0 por la misma razón que n * 0 siempre es 0. Pero min y max son diferentes. No creo que el resultado esté definido correctamente y no se ejecute ningún registro.
Chris Travers

También avg () sobre un conjunto nulo tiene sentido como nulo porque 0/0 no está definido correctamente en este contexto.
Chris Travers

55
MIN y MAX no son tan diferentes. Tome un operador binario subyacente LOWESTOF (x, y) y HIGHESTOF (x, y) respectivamente. Estos operadores binarios tienen un valor de identidad. Porque en ambos casos (si el tipo involucrado es finito), existe de hecho algún valor z tal que para x: LOWESTOF (z, x) = x y para y: HIGHESTOF (y, z) = y. (El valor de identidad no es el mismo para ambos casos, pero existe para ambos). Estoy de acuerdo en que los resultados parecen extremadamente contradictorios a primera vista, pero no se puede negar la realidad matemática.
Erwin Smout

@Erwin: Estoy de acuerdo con todos sus puntos, excepto que la identidad de algunas operaciones, como HIGHEST()muchas, no será un elemento del tipo de datos, como para Reals, donde la identidad sería -Infinity(y +Infinitypara LOWEST())
ypercubeᵀᴹ

1
@SQL kiwi. ¿Te estás olvidando de la verificación de tipo estático? Si el verificador de tipo estático maneja expresiones como SUM () como si siempre devolvieran un número entero, entonces, obviamente, debería ser imposible que la invocación SUM () a veces devuelva algo que no sea un número entero (por ejemplo, una relación vacía).
Erwin Smout

3

En un sentido pragmático, el resultado existente de NULLes útil. Considere la siguiente tabla y declaraciones:

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 primera instrucción devuelve NULL y la segunda devuelve cero. Si un conjunto vacío devuelve cero SUM, necesitaríamos otro medio para distinguir una suma verdadera de cero de un conjunto vacío, tal vez usando count. Si realmente queremos cero para el conjunto vacío, entonces un simple COALESCEproporcionará ese requisito.

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

1
como resultado., SUM (unión de set1 y set2) <> SUM (set1) + SUM (set2), porque cualquier número + NULL = NULL. ¿Tiene sentido para usted?
AK

2
@Leigh: El uso COALESCE()de este tipo no distinguirá la ( 0suma) de un conjunto vacío de la ( NULLsuma) (decir la mesa que había una (10, NULL)fila.
ypercubeᵀᴹ

Además, todavía no podemos distinguir SUM (conjunto vacío) de SUM (conjunto de uno o más NULL). ¿Necesitamos distinguir en absoluto?
AK

@AlexKuznetsov: podemos distinguir una suma de un conjunto vacío de una suma de un conjunto que contiene uno o más valores nulos siempre que al menos una fila contenga un valor. Tiene razón en que si el conjunto contiene solo NULL, entonces no podemos distinguir el conjunto NULL de este conjunto de todos los valores NULL. Mi punto no fue que sea útil en todos los casos, simplemente que puede ser útil. Si tengo SUMuna columna y vuelvo a cero, lo sé sin tener que verificar que haya al menos una fila que no sea NULL para mostrarme el resultado.
Leigh Riffel

@ypercude: tienes toda la razón. Mi punto fue que el comportamiento actual de SUM distingue un conjunto vacío de un conjunto que contiene valores (incluso si algunos son nulos). Es más simple usar COALESCE cuando no se requiere la distinción que usar algo como DECODE(count(c2),0,NULL,sum(c2))cuando lo es.
Leigh Riffel

-1

La principal diferencia que puedo ver es con respecto al tipo de datos. COUNT tiene un tipo de retorno bien definido: un número entero. Todos los demás dependen del tipo de columna / expresión que están viendo. Su tipo de retorno debe ser compatible con todos los miembros del conjunto (piense en flotante, moneda, decimal, bcd, intervalo de tiempo, ...). Como no hay un conjunto, no puede implicar un tipo de retorno, por lo tanto, NULL es su mejor opción.

Nota: En la mayoría de los casos, podría implicar un tipo de retorno del tipo de columna que está viendo, pero puede hacer SUMAS no solo en columnas sino en todo tipo de cosas. Implicar un tipo de retorno puede ser muy difícil, si no imposible, bajo ciertas circunstancias, especialmente cuando se piensa en posibles expansiones del estándar (los tipos dinámicos vienen a la mente).


55
¿Por qué no podemos implicar un tipo de retorno en una SUM(column)expresión? ¿No tenemos tablas vacías y todas las columnas tienen tipos definidos? ¿Por qué debería ser diferente para un conjunto de resultados vacío?
ypercubeᵀᴹ

55
Te equivocas cuando dices "ya que NO hay SET ". Hay un conjunto. El conjunto de todos los valores posibles del tipo declarado de las columnas o expresiones involucradas. Ese tipo declarado existe incluso si la tabla que está mirando está vacía. Incluso las mesas vacías todavía tienen un encabezado. Y ese tipo declarado es exactamente su "tipo de retorno implícito".
Erwin Smout

¿Ustedes dos realmente leyeron mi nota? Sí, funcionaría para SUMs basados ​​en columnas a partir de ahora. Pero tan pronto como encuentre una columna de tipo de datos variable (aún no en SQL Server), no tiene suerte.
TToni

2
¿Cómo definirás la suma en ese caso? ¿Cuál será el resultado de 24 + 56.07 + '2012-10-05' + 'Red'ser? Quiero decir que no hay problema en preocuparse por cómo SUM()se comportará cuando tengamos un problema para definir la suma.
ypercubeᵀᴹ

1
@TToni: "especialmente cuando piensas en posibles expansiones del estándar" no es el contexto al que se refería el OP. el OP se refería muy claramente a la versión actual del estándar, que no incluye ningún tipo de noción de "tipos dinámicos" o algo así. (Ah, y solo comenté, pero no voté negativamente. Aparte de ese pequeño error con el que tuve problemas, nada en su respuesta fue lo suficientemente incorrecto como para justificar un voto negativo. OMI.)
Erwin Smout
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.