Чому 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({})як nullпринципу "не містить рядків" особливий випадок, який не вписується в інші:

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

Чи є якась явна перевага вибору, який я був зроблений (SUM - NULL), який я пропустив?



5
Так, я згоден: COUNT і SUM не ведуть себе послідовно.
АК

Відповіді:


20

Я боюся, що причина полягає лише в тому, що правила встановлювались по-різному (як і багато інших "особливостей" стандарту ISO SQL) у той час, коли агрегації SQL та їх зв'язок з математикою були менш зрозумілі, ніж зараз (*).

Це лише одна з надзвичайно багатьох невідповідностей у мові SQL. Вони ускладнюють викладання мови, важче вивчати, важче зрозуміти, складніше використовувати, важче для всього, що ви хочете, але це просто так. Правила не можуть бути змінені "холодно" та "просто так" з очевидних причин відсталої сумісності (Якщо комітет ISO опублікує остаточну версію стандарту, а постачальники потім налаштовані на впровадження цього стандарту, то ці постачальники не оцінять їх дуже важливо, якщо в наступній версії правила змінюються таким чином, що існуючі (сумісні) реалізації колишньої версії стандарту "автоматично не відповідають" новій версії ...)

(*) Тепер краще зрозуміти, що агрегації над порожнім набором поводяться більш послідовно, якщо вони систематично повертають значення ідентичності (= те, що ви називаєте "нейтральним елементом") базового бінарного оператора. Цей базовий двійковий оператор для COUNT і SUM є додаванням, а його ідентичне значення дорівнює нулю. Для MIN і MAX це значення ідентичності є найвищим і найнижчим значенням відповідного типу, відповідно, якщо відповідні типи кінцеві. Такі випадки, як усереднення, гармонійні засоби, медіани тощо, є надзвичайно хитромудрими та екзотичними в цьому відношенні.


Я думаю, що null має сенс для порожнього набору з 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. (Значення ідентичності не є однаковим для обох випадків, але воно існує для обох випадків.) Я погоджуюся, що результати на перший погляд виглядають вкрай протиконкурентними, однак математичної реальності немає.
Ервін Смоут

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

1
@SQL kiwi. Ви забуваєте про перевірку статичного типу? Якщо вирази типу 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нам знадобиться інший спосіб, щоб відрізнити справжню суму нуля від порожнього набору, можливо, використовуючи підрахунок. Якщо ми справді хочемо нуля для порожнього набору, то простий COALESCEзабезпечить цю вимогу.

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

1
як результат. SUM (об'єднання set1 і set2) <> SUM (set1) + SUM (set2), оскільки будь-яке число + NULL = NULL. Це має для вас сенс?
АК

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

Крім того, ми все ще не можемо відрізнити SUM (порожній набір) від SUM (набір одного або декількох NULL). Чи потрібно взагалі розрізняти?
АК

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

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

-1

Основна відмінність, яку я бачу, стосується типу даних. COUNT має чітко визначений тип повернення: Ціла кількість. Всі інші залежать від типу стовпця / виразу, який вони дивляться. Їх тип повернення повинен бути сумісним з усіма членами набору (think float, валюта, десяткова, bcd, проміжок часу, ...). Оскільки немає набору, ви не можете припустити тип повернення, тому NULL - ваш найкращий варіант.

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


5
Чому ми не можемо передбачити зворотний тип у SUM(column)виразі? У нас немає порожніх таблиць - а там усі стовпці визначають типи? Чому для порожнього набору результатів він повинен відрізнятися?
ypercubeᵀᴹ

5
Ви помиляєтесь там, де говорите: "оскільки НІМАЄ НАСТРОЙКА ". Є набір. Набір усіх можливих значень оголошеного типу задіяних стовпців або виразу. Цей заявлений тип існує, навіть якщо таблиця, яку ви дивитесь, порожня. Навіть порожні таблиці все ще мають заголовок. І саме цей оголошений тип є саме вашим "типовим типом повернення".
Ервін Смоут

Ви насправді ви читали мою записку? Так, це буде працювати для SUM на базі стовпців на даний момент. Але як тільки ви стикаєтеся зі змінним стовпчиком даних (ще не в SQL Server), вам не пощастило.
TToni

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

1
@TToni: "особливо коли ви думаєте про можливі розширення стандарту", це не той контекст, про який йдеться в ОП. ОП дуже чітко посилалася на поточну версію стандарту, яка не включає якесь поняття "динамічні типи" чи якесь таке. (О, і я лише коментував, але не спровокував. Окрім тієї крихітної промальовки, яку я взяв на себе, нічого у вашій відповіді було недостатньо помилковим, щоб надати правомочне голосування. ІМО.)
Ервін Смут
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.