【问题标题】:Strange MySQL AVG() anomaly NULL values奇怪的 MySQL AVG() 异常 NULL 值
【发布时间】:2012-12-24 11:40:23
【问题描述】:

我在做什么:

create table sample (id INT(10) PRIMARY KEY AUTO_INCREMENT,name varchar(255),marks INT(10));

insert into sample (name,marks) VALUES('sam',10);
insert into sample (name,marks) VALUES('sam',20);
insert into sample (name,marks) VALUES('sam',NULL);
insert into sample (name,marks) VALUES('sam',NULL);
insert into sample (name,marks) VALUES('sam',30);

select AVG(marks) from sample GROUP BY(name);

我预期的输出:

平均数 = (10+20+30)/5 = 12

MYSQL 的输出:

AVG = (10+20+30)/3 = 20

理想情况下,我想要的是 MYSQL 应该得到 5 行的总和并将其除以 5,但它只除以 3(非 NULL 行)

为什么会发生这种情况,我该怎么做才能获得正确的 AVG,即 60/5? PS:我不能将标记字段设为 NOT NULL,在我的数据库设计中,标记字段允许为 NULL。

谢谢

【问题讨论】:

    标签: mysql sql null


    【解决方案1】:

    这是正确的行为,因为NULL 与数字0 不同。
    这可能会让一些不会说英语的人感到惊讶,因为在许多语言中“null”等同于“zero”。

    从概念上讲,NULL 指的是“未知值”,因此它的处理方式与其他值不同。这就是为什么aggregate functions 喜欢AVG() 忽略NULLs。

    AVG() 仅计算所有“已知”值的平均值。 (= NULL)

    来自MySQL docs

    除非另有说明,否则组函数会忽略 NULL 值。

    另外,请阅读 MySQL 手册的 Section "3.3.4.6 Working with NULL Values" 中的 NULLs 概念。

    为了得到你想要的,你可能会这样做

    SELECT AVG(IFNULL(marks, 0)) 
    FROM sample 
    GROUP BY name;
    

    IFNULL() 如果值为NULL,则返回计算的第二个参数,否则通过该值。


    关于NULL 的概念存在更常见的误解。这些也在手册的Section "5.5.3 Problems with NULL" 中有说明:

    • 在 SQL 中,NULL 值与任何其他值相比永远不会为真,即使是 NULL。包含 NULL 的表达式始终会生成 NULL 值,除非文档中针对表达式中涉及的运算符和函数另有说明。

      即:NULL == 0 导致 NULL 而不是 trueNULL == NULL 也会导致 NULL,而不是 true。
    • 要搜索NULL 的列值,您不能使用expr = NULL 测试。要查找 NULL 值,您必须使用 IS NULL 测试。
    • 使用DISTINCTGROUP BYORDER BY 时,所有NULL 值都被视为相等。
    • 使用ORDER BY 时,NULL 值首先显示,如果您指定DESC 以降序排序,则在最后显示。
    • 对于某些数据类型,MySQL 专门处理 NULL 值。如果您将NULL 插入TIMESTAMP 列,则会插入当前日期和时间。
    • 如果将NULL 插入到具有AUTO_INCREMENT 属性的整数或浮点列中,则会插入序列中的下一个数字。
    • 定义了UNIQUE 键的列仍可以包含多个NULL 值。

    【讨论】:

    • 感谢@kaii,该解决方案有帮助,我的印象是 mySQL 执行了 AVG like = sum/(nos of columns in the result)
    【解决方案2】:

    试试:

    select avg(case marks when null then 0 else marks end) from sample group by name;
    

    或者尽量避免表中的空值;)

    【讨论】:

      【解决方案3】:

      您可以这样做:

      SELECT SUM(marks) / COUNT(name)
      FROM sample 
      GROUP BY name;
      

      【讨论】:

        【解决方案4】:

        这是正常行为,因为 NULL 不为零。你如何使 5 + NULL 的平均值?所以 MySQL 只取可以平均的行。

        除了其他用户已经给出的正确答案外,您还可以使用 COALESCE 函数,该函数返回您在列表中指定的第一个非 NULL 值,因此您可以将 NULL 替换为您喜欢的值:

            SELECT AVG(COALESCE(marks,0)) FROM sample GROUP BY(name);
        

        【讨论】:

          【解决方案5】:
          SELECT
                 avg(CASE WHEN price IS NULL THEN 0 else price END), 
                 avg(CASE WHEN discount IS NULL THEN 0 else discount END), 
                 avg(CASE WHEN total IS NULL THEN 0 else total END) 
          FROM pet;
          

          【讨论】:

            猜你喜欢
            • 2011-10-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多