【问题标题】:How does the Average function work in relational databases?Average 函数在关系数据库中如何工作?
【发布时间】:2015-08-25 13:58:28
【问题描述】:

我正在尝试从具有数百万行的表中找到值的几何平均值。对于那些不知道的人,要找到几何平均值,您可以将每个值相乘,然后除以行数。

您可能已经看到了问题;数字相乘的数字将很快超过最大允许的系统最大值。我找到了一个使用自然日志的绝佳解决方案。

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

但是,这让我想知道算术平均值是否也适用于同样的问题?如果你有 N 条记录,并且 N 很大,运行总和也可能超过系统最大值。

那么 RDMS 如何在查询期间计算平均值?

【问题讨论】:

    标签: sql math rdbms


    【解决方案1】:

    我不知道 RDBMS 中算术平均值的确切实现,您也没有在原始问题中指定一个。但是 RDBMS 不需要对列中的一百万行求和以获得算术平均值。考虑以下总结:

    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 可以简单地扫描一列中的百万行并逐节查找平均值,而不会冒溢出的风险。而且由于列中的每个数字都可能在存储它的类型的范围内,因此平均值本身不会溢出。

    【讨论】:

    • 在实践中实现这一点非常困难,所以我打赌所有引擎都做简单的SUM,然后除以COUNT。如果SUM 超出某种类型的值范围 - 就会出现错误。一些引擎可能使用大类型来存储中间结果(64 位 int,即使列是 32 位),所以最终答案是 - 这取决于/检查文档。
    • 这种方法的一个主要困难是通常有一个或多个过滤条件,并且在处理行之前很难确定 N 的正确值。
    【解决方案2】:

    大多数数据库不支持 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() 的函数因数据库而异。另外,如果你必须考虑零或负数,逻辑就更复杂了。

    【讨论】:

    • EXP(AVG(LN(x))) 不计算几何平均值吗?
    【解决方案3】:

    很容易检查。例如,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的范围。

    您可以对您使用的任何其他 DBMS 运行类似的检查。不同的引擎可能表现不同,但我希望它们都坚持原来的列类型。例如,平均两个int100101 可能会导致100101(仍然是int),但绝不会导致100.5

    对于 SQL Server,此行为是 documented。我希望所有其他引擎都有类似的东西:

    AVG() 通过除以总和来计算一组值的平均值 这些值由非空值的计数。如果总和超过 返回值的数据类型的最大值,错误将是 返回。

    因此,在计算简单平均值时也必须小心,而不仅仅是乘积。


    这是SQL 92 Standard的摘录:

    6) 设DT为的数据类型。

    9) 如果指定了 SUM 或 AVG,则:

    a) DT 不能是字符串、位串或日期时间。

    b) 如果指定了 SUM 并且 DT 是具有刻度 S 的精确数字,则 结果的数据类型是具有实现定义的精确数字 精度和尺度S。

    c) 如果指定了 AVG 并且 DT 是精确数字,那么数据类型 结果是精确的数字,具有实现定义的精度不是 小于 DT 的精度和实现定义的比例不 小于DT的规模。

    d) 如果 DT 是近似数值,那么结果的数据类型是 具有实现定义的精度不少于的近似数值 比DT的精度。

    e) 如果 DT 为区间,则结果的数据类型为区间 与 DT 具有相同的精度。

    所以,DBMS 在计算AVG 时可以将int 转换为更大的类型,但它必须是exact numeric 类型,而不是浮点型。在任何情况下,根据值,您仍然可以得到算术溢出。

    【讨论】:

    • 供您参考:您的示例在 Postgres(和 Oracle)中运行良好。
    【解决方案4】:

    一些 DBMS — 特别是 Informix DBMS — 从 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 中计算下个世纪的美国赤字,通常也需要非常多的行才能使总和超过限制。

    【讨论】:

    • decimal 不是浮点类型,它是exact numeric。但是,重点是,一些 DBMS 将数据转换为更大的类型。显然,SQL 标准并没有禁止它。
    • @VladimirBaranov:在大多数 Informix 数据库中,DECIMAL(32) 表示最多 32 位数字,指数大致在 ±126 范围内。那是我书中的浮点类型。在 MODE ANSI 数据库中,DECIMAL(32) 表示 32 位整数类型,但很少有人将 MODE ANSI 数据库与 Informix 一起使用。
    • 嗯,我从这个问题和讨论中了解到,SQL 标准有许多不同的实现,我应该避免做出这样的通用语句。尽管如此,如果 Informix 在计算 AVG 时将 int 转换为浮点数,则违反标准(当然,如果我正确理解了标准)。请参阅我的答案中的标准摘录。这只再次强调没有一个通用的答案,这些细节应该通过特定的实现来检查。
    • 我绝对同意笼统的陈述是危险的(我很久以前就知道了,很难)。我也同意 DBMS 实现既曲折又多样——都在某些方面滥用标准,有些比其他方面更严重。几乎所有没有规定哪个 DBMS 只能邀请“取决于”答案的问题,并选择了一些 DBMS 中的示例以适合回答的人。
    猜你喜欢
    • 1970-01-01
    • 2020-05-05
    • 1970-01-01
    • 2010-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-26
    相关资源
    最近更新 更多