Почему ANSI SQL определяет SUM (без строк) как NULL?


28

Стандарт ANSI SQL определяет (глава 6.5, спецификация функции набора) следующее поведение для агрегатных функций в пустых результирующих наборах:

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

Возврат NULL для AVG, MIN и MAX имеет смысл, поскольку среднее, минимальное и максимальное значения пустого набора не определены.

Последний, однако, беспокоит меня: Математически SUM пустого множества хорошо определен: 0. Использование 0, нейтрального элемента сложения, в качестве базового варианта делает все согласованным:

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

Определение SUM({})as в nullосновном делает «без строк» ​​особым случаем, который не вписывается в другие:

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

Есть ли какое-то очевидное преимущество сделанного выбора (SUM, NULL), который я пропустил?



5
Да, я согласен: COUNT и SUM не ведут себя последовательно.
АК

Ответы:


20

Я боюсь, что причина в том, что правила были установлены по принципу ad hoc (как и многие другие «особенности» стандарта ISO SQL) в то время, когда агрегаты SQL и их связь с математикой были менее понятны, чем сейчас (*).

Это всего лишь одна из чрезвычайно многих несоответствий в языке SQL. Они делают язык труднее учить, труднее учить, труднее понять, труднее использовать, труднее всего, что вы хотите, но это просто так. Правила не могут быть изменены «холодными» и «просто так» по очевидным причинам обратной совместимости (если комитет ИСО публикует окончательную версию стандарта, а поставщики затем собираются внедрить этот стандарт, то эти поставщики не оценят это очень сильно, если в последующей версии правила изменяются так, что существующие (совместимые) реализации прежней версии стандарта «автоматически не соответствуют» новой версии ...)

(*) Теперь лучше понять, что агрегаты по пустому набору ведут себя более согласованно, если они систематически возвращают значение идентичности (= то, что вы называете «нейтральным элементом») базового бинарного оператора под рукой. Этот базовый двоичный оператор для COUNT и SUM является сложением, а его значение идентичности равно нулю. Для MIN и MAX это значение идентификатора является наивысшим и наименьшим значением имеющегося типа, соответственно, если рассматриваемые типы конечны. Однако случаи, такие как усреднение, гармонические средние, медианы и т. Д., Чрезвычайно сложны и экзотичны в этом отношении.


Я думаю, что ноль имеет смысл для пустого набора с минимальным и максимальным Вы могли бы сказать, что значение идентичности там действительно неизвестно, но сумма нулевых значений равна 0 по той же причине, что n * 0 всегда равно 0. Но min и max различны. Я не думаю, что результат правильно определен и не содержит записей.
Крис Трэверс

Также avg () для нулевого набора имеет смысл как ноль, потому что 0/0 не определено должным образом в этом контексте.
Крис Треверс

5
MIN и MAX не так уж отличаются. Возьмем базовый бинарный оператор LOWESTOF (x, y) и HIGHESTOF (x, y) соответственно. Эти бинарные операторы имеют значение идентичности. Поскольку в обоих случаях (если задействованный тип конечен), действительно существует некоторое значение z, такое, что для x: LOWESTOF (z, x) = x и для y: HIGHESTOF (y, z) = y. (Значение идентичности не одинаково для обоих случаев, но оно существует для обоих случаев.) Я согласен, что результаты выглядят крайне противоречивыми на первый взгляд, но нет никакой отрицания математической реальности.
Эрвин Смут

@ Эрвин: Я согласен по всем вашим пунктам, за исключением того, что идентичность некоторых операций, как и HIGHEST()многих, не является элементом типа данных, например, для Реалов, где идентичность будет -Infinity+Infinityдля LOWEST())
ypercubeᵀᴹ

1
@SQL киви. Вы забыли о статической проверке типов? Если такие выражения, как SUM (), обрабатываются статическим средством проверки типов, как если бы они всегда возвращали целое число, то, очевидно, для вызова SUM () иногда невозможно вернуть что-то, что не является целым числом (например, пустое отношение).
Эрвин Смут

3

В прагматическом смысле существующий результат NULLполезен. Рассмотрим следующую таблицу и утверждения:

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

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

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

Первый оператор возвращает NULL, а второй возвращает ноль. Если бы пустой набор возвратил ноль, SUMнам понадобится другое средство, чтобы отличить истинную сумму нуля от пустого набора, возможно, используя count. Если мы действительно хотим ноль для пустого множества, то простое COALESCEбудет соответствовать этому требованию.

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

1
в результате. SUM (объединение set1 и set2) <> SUM (set1) + SUM (set2), потому что любое число + NULL = NULL. Это имеет смысл для вас?
AK

2
@Leigh: Использование COALESCE()подобным образом не будет отличать 0сумму ( ) пустого набора от NULLсуммы () (скажем, в таблице есть (10, NULL)строка.
ypercubeᵀᴹ

Кроме того, мы все еще не можем отличить SUM (пустой набор) от SUM (набор из одного или нескольких NULL). Нужно ли вообще различать?
AK

@AlexKuznetsov - Мы можем отличить сумму пустого набора от суммы набора, который содержит один или несколько нулей, если хотя бы одна строка содержит значение. Вы правы в том, что если набор содержит только NULL, то мы не можем отличить набор NULL от этого набора всех значений NULL. Моя точка зрения заключалась не в том, что это полезно в каждом случае, просто в том, что это может быть полезно. Если я SUMстолбец и получаю ноль, я знаю, не проверяя, есть ли хотя бы одна строка, не равная NULL, для отображения результата.
Ли Риффель

@ypercude - Вы абсолютно правы. Моя точка зрения заключалась в том, что текущее поведение SUM отличает пустой набор от набора, который содержит значения (даже если некоторые из них являются нулевыми). Использовать COALESCE проще, когда различие не требуется, чем использовать что-то вроде того, DECODE(count(c2),0,NULL,sum(c2))когда оно есть.
Ли Риффель

-1

Основное различие, которое я вижу, это тип данных. COUNT имеет четко определенный тип повторения: целое число. Все остальные зависят от типа столбца / выражения, на которое они смотрят. Их тип возвращаемого значения должен быть совместим со всеми членами набора (например, с плавающей точкой, валютой, десятичной дробью, bcd, timespan, ...). Поскольку нет набора, вы не можете подразумевать тип возвращаемого значения, поэтому NULL - ваш лучший вариант.

Примечание. В большинстве случаев вы можете подразумевать тип возвращаемого значения для типа столбца, который вы просматриваете, но вы можете делать SUMs не только для столбцов, но и для всех видов вещей. Подразумевать, что тип возврата может быть очень трудным, если не невозможным, при определенных обстоятельствах, особенно когда вы думаете о возможных расширениях стандарта (на ум приходят динамические типы).


5
Почему мы не можем подразумевать возвращаемый тип в SUM(column)выражении? Разве у нас нет пустых таблиц - и там все столбцы имеют определенные типы? Почему это должно быть иначе для пустого набора результатов?
ypercubeᵀᴹ

5
Вы ошибаетесь, когда говорите «так как нет комплекта ». Есть множество. Множество всех возможных значений объявленного типа задействованных столбцов или выражений. Этот объявленный тип существует, даже если таблица, которую вы просматриваете, пуста. Даже пустые таблицы все еще имеют заголовок. И этот объявленный тип является именно вашим «подразумеваемым типом возврата».
Эрвин Смут

Вы оба действительно читали мою записку? Да, это будет работать для SUM на основе столбцов на данный момент. Но как только вы встретите переменную datatype-column (не в SQL Server - пока), вам не повезло.
TToni

2
Как вы определите сумму в этом случае? Каким будет результат 24 + 56.07 + '2012-10-05' + 'Red'? Я имею в виду, что нет смысла беспокоиться о том, как SUM()себя поведет, когда у нас возникнут проблемы с определением дополнения.
ypercubeᵀᴹ

1
@TToni: «особенно, когда вы думаете о возможных расширениях стандарта», это не тот контекст, на который ссылался ОП. ОП очень четко ссылался на текущую версию стандарта, которая не включает какое-либо понятие «динамические типы» или что-то подобное. (О, и я только прокомментировал, но не понизил. Помимо этого крошечного промаха, с которым я не согласился, ничто в вашем ответе не было достаточно неправильным, чтобы оправдать понижение. IMO.)
Эрвин Смут
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.