2015-08-25 5 views
2

Я пытаюсь найти геометрическое среднее значений из таблицы с миллионами строк. Для тех, кто не знает, чтобы найти среднее геометрическое, вы умножаете каждое значение раз друг друга, а затем делите на количество строк.Как работает функция Average в реляционных базах данных?

Возможно, вы уже видите проблему; Число, умноженное на число, быстро превысит максимально допустимый максимальный уровень. Я нашел отличное решение, которое использует естественный журнал.

http://timothychenallen.blogspot.com/2006/03/sql-calculating-geometric-mean-geomean.html

Однако это заставило меня задаться вопросом, не будет та же проблема, применяются с среднеарифметической? Если у вас есть N записей, а N очень велико, текущая сумма также может превышать максимальный уровень системы.

Итак, как RDMS вычисляет средние значения во время запросов?

ответ

0

Очень легко проверить. Например, SQL Server 2008.

DECLARE @T TABLE(i int); 

INSERT INTO @T(i) VALUES 
(2147483647), 
(2147483647); 

SELECT AVG(i) FROM @T; 

результат

(2 row(s) affected) 
Msg 8115, Level 16, State 2, Line 7 
Arithmetic overflow error converting expression to data type int. 

Там нет никакой магии.Тип столбца: int, сервер добавляет значения вместе, используя внутреннюю переменную того же типа int, а промежуточный результат превышает диапазон для int.

Вы можете запустить аналогичную проверку для любых других СУБД, которые вы используете. Различные двигатели могут вести себя по-разному, но я ожидаю, что все они будут придерживаться исходного типа столбца. Например, усреднение двух значений int100 и 101 может привести к 100 или 101 (все еще int), но никогда 100.5.

Для SQL Server это поведение documented. Я бы ожидать что-то подобное для всех остальных двигателей:

AVG() вычисляет среднее значение из набора значений путем деления суммы этих значений с помощью подсчета ненулевых значений. Если сумма превышает максимальное значение для типа данных возвращаемого значения, будет возвращена ошибка .

Итак, вы должны быть осторожны при вычислении простого среднего, а не только продукта.


Вот выдержка из SQL 92 Standard:

6) Пусть DT будет тип данных выражения в < значение>.

9) Если SUM или AVG указано, то:

а) DT не должна быть строка символов, строка битов, или даты и времени.

б) Если СУММА задана и ДТ является точным числовым с масштабом S, то типом данных результата является точным числовым с реализацией определяется точности и масштабом S.

с) Если указан AVG и DT является точечным числовым, то тип данных результат точный численный с точностью, определяемой реализацией, а не меньше точности DT и шкалы, определяемой реализацией, а не меньше шкалы DT.

d) Если DT является приблизительным числовым, то тип данных результата равен приблизительный числовой с точностью, определяемой реализацией, не менее , чем точность DT.

e) Если DT является интервалом, то типом данных результата является интервал с той же точностью, что и DT.

Таким образом, СУБД может конвертировать int для большего типа при расчете AVG, но он должен быть exact numeric типа, а не с плавающей точкой. В любом случае, в зависимости от значений, вы все равно можете получить арифметическое переполнение.

+0

Для информации: ваш пример отлично работает в Postgres (и Oracle). –

1

Большинство баз данных не поддерживают функцию product() так, как они поддерживают среднее значение.

Однако вы можете использовать делать то, что хотите, с помощью журналов. Продукт (упрощенный), как:

select exp(sum(ln(x)) as product 

среднем будет:

select power(exp(sum(ln(x))), 1.0/count(*)) as geoaverage 

или

select EXP(AVG(LN(x))) as geoaverage 

функция LN() может быть LOG() на некоторых платформах .. .

Это схемы. Функции для exp() и ln() и power() различаются в зависимости от базы данных. Кроме того, если вы должны учитывать нулевые или отрицательные числа, логика сложнее.

+2

Не 'EXP (AVG (LN (x)))' вычисляет среднее геометрическое? –

1

Я не знаю точную реализацию для среднего арифметического в РСУБД, и вы не указали его в своем исходном вопросе. Но для РСУБД не требуется суммировать миллион строк в столбце, чтобы получить среднее арифметическое. Рассмотрим следующее суммирование:

sum = (x1 + x2 + x3 + ... + x1000000) 

Тогда среднее можно записать в виде

mean = sum/N = (x1 + x2 + x3 + ... + x1000000)/N, for N = 1,000,000 

Но это выражение можно разбить на куски, как это:

mean = [(x1 + x2 + x3)/N ] + [(x4 + x5 + x6)/N] + ... 

Другими словами, RDBMS может просто сканировать миллион строк в столбце и находить средний раздел по разделу, не рискуя переполнением. И так как каждое число в столбце, по-видимому, находится в пределах диапазона для типа его хранения, нет возможности переполнения среднего значения.

+0

Это было бы очень сложно реализовать на практике, поэтому я уверен, что все двигатели делают простой 'SUM', а затем делятся на' COUNT'. И если 'SUM' превышает диапазон значений определенного типа - будет ошибка. Некоторые двигатели могут использовать большой тип для хранения промежуточных результатов (64 бит 'int', даже если столбец 32 бит), поэтому окончательный ответ - это зависит/проверяет документы. –

+0

Основная трудность с этим подходом заключается в том, что обычно существует один или несколько условий фильтра, и перед обработкой строк трудно определить правильное значение N. –

0

Некоторые СУБД - в частности, СУБД Informix - преобразование из типа INT к типу с плавающей точкой, чтобы сделать расчет:

SQL[2148]: create table t(i int); 
SQL[2149]: insert into t values(214748347); 
SQL[2150]: insert into t values(214748347); 
SQL[2151]: insert into t values(214748347); 
SQL[2152]: select avg(i) from t; 
214748347.0 
SQL[2153]: types on; 
SQL[2154]: select i from t; 
INTEGER 
214748347 
214748347 
214748347 
SQL[2155]: select avg(i) from t; 
DECIMAL(32) 
214748347.0 
SQL[2156]: 

Аналогично с другими типами. В некоторых случаях это может закончиться переполнением; вы получите ошибку времени выполнения. Тем не менее, довольно редко вы превышаете точность - обычно для того, чтобы сумма превышала пределы, обычно требуется очень большое количество строк, даже если вы считаете дефицит США в следующем столетии в atto- Zimbabwean dollars circa 2009.

+0

'decimal' не является типом с плавающей запятой, он является« точным числом ». Но, взяв точку, некоторые СУБД преобразуют данные в более крупный тип. По-видимому, стандарт SQL не запрещает его. –

+0

@VladimirBaranov: В большинстве баз данных Informix DECIMAL (32) означает примерно 32 цифры с показателем в диапазоне ± 126, примерно. Это тип с плавающей точкой в ​​моей книге. В базе данных ANSI MODE DECIMAL (32) означает 32-значный целочисленный тип, но мало кто использует базу данных ANSI MODE с Informix. –

+0

Ну, я узнал из этого вопроса и обсуждения, что существует множество различных реализаций стандарта SQL, и я должен воздерживаться от создания таких обобщенных утверждений. Тем не менее, если Informix преобразует 'int' в точку с плавающей точкой при расчете' AVG', это противоречит стандарту (если я правильно понял стандарт, конечно). См. Выписку из стандарта в моем ответе. Что еще раз подчеркивает, что нет никакого общего ответа, и такие детали должны быть проверены с особой реализацией. –

Смежные вопросы