【问题标题】:MySQL SUM returns unexpected valueMySQL SUM 返回意外值
【发布时间】:2019-09-04 10:31:28
【问题描述】:

我一直在尝试使用SUM() 来汇总我数据库中的一些数值,但它似乎返回了意外的值。这是生成 SQL 的 PHP 端:

public function calcField(string $field, array $weeks)
{
    $stmt = $this->conn->prepare('SELECT SUM(`'. $field .'`) AS r FROM `ws` WHERE `week` IN(?);');
    $stmt->execute([implode(',', $weeks)]);

    return $stmt->fetch(\PDO::FETCH_ASSOC)['r'];
}

让我们为大家提供一些示例数据:

$field = 'revenue';
$weeks = [14,15,16,17,18,19,20,21,22,23,24,25,26];

这会返回这个值:

4707.92

在没有看到数据的情况下,这似乎已经奏效,但以下是那几周的行:

+----+------+------+---------+-------+---------+---------+------+----------+
| id | week | year | revenue | sales | gpm_ave | uploads | pool | sold_ave |
+----+------+------+---------+-------+---------+---------+------+----------+
|  2 |   14 | 2019 | 4707.92 |   292 |      13 |       0 | 1479 |       20 |
|  3 |   15 | 2019 | 4373.32 |   304 |      13 |       0 | 1578 |       19 |
|  4 |   16 | 2019 | 4513.10 |   275 |      14 |       0 | 1460 |       19 |
|  5 |   17 | 2019 | 4944.80 |   336 |      14 |       0 | 1642 |       20 |
|  6 |   18 | 2019 | 4343.87 |   339 |      13 |       0 | 1652 |       21 |
|  7 |   19 | 2019 | 3918.59 |   356 |      14 |       0 | 1419 |       25 |
|  8 |   20 | 2019 | 4091.20 |   247 |      19 |       0 | 1602 |       15 |
|  9 |   21 | 2019 | 4177.22 |   242 |      12 |       0 | 1588 |       15 |
| 10 |   22 | 2019 | 3447.88 |   227 |      18 |       0 | 1585 |       14 |
| 11 |   23 | 2019 | 3334.18 |   216 |      15 |       0 | 1675 |       13 |
| 12 |   24 | 2019 | 4736.15 |   281 |      13 |       0 | 1388 |       20 |
| 13 |   25 | 2019 | 4863.84 |   252 |      12 |       0 | 1465 |       17 |
| 14 |   26 | 2019 | 4465.95 |   281 |      21 |       0 | 1704 |       16 |
+----+------+------+---------+-------+---------+---------+------+----------+

如您所见,总数应该远远大于4707.92 - 我注意到第一行收入 = 4707.92。

如果我将它添加到函数中,事情会变得很奇怪:

echo 'SELECT SUM(`'. $field .'`) AS r FROM `ws` WHERE `week` IN('. implode(',', $weeks) .');';

哪些输出:

SELECT SUM(revenue) AS r FROM ws WHERE week IN(14,15,16,17,18,19,20,21,22,23,24,25,26);

将其复制并粘贴到 MySQL CLI 中返回:

MariaDB [nmn]> SELECT SUM(revenue) AS r FROM ws WHERE week IN(14,15,16,17,18,19,20,21,22,23,24,25,26);
+----------+
| r        |
+----------+
| 55918.02 |
+----------+
1 row in set (0.00 sec)

哪个,看起来更准确。但是,同样的 SQL 语句会返回第一行的值,而不是对那几周的列求和。

此函数由 AJAX 脚本触发:

$d = new Page\Snapshot\D();

# at the minute only outputting dump of values to see what happens
echo '<pre>'. print_r(
        $d->getQuarterlySnapshot(new Page\Snapshot\S(), new App\Core\Date(), $_POST['quarter'], '2019'),
        1
    ). '</pre>';

函数$d-&gt;getQuarterlySnapshot函数:

public function getQuarterlySnapshot(S $s, Date $date, int $q, string $year)
{
    switch($q)
    {
        case 1:
            $start = $year. '-01-01 00:00:00';
            $end = $year. '-03-31 23:59:59';
            break;
        case 2:
            $start = $year. '-04-01 00:00:00';
            $end = $year. '-06-30 23:59:59';
            break;
        case 3:
            $start = $year. '-07-01 00:00:00';
            $end = $year. '-09-30 23:59:59';
            break;
        case 4:
            $start = $year. '-10-01 00:00:00';
            $end = $year. '-12-31 23:59:59';
            break;
    }

    $weeks = $date->getWeeksInRange('2019', 'W', $start, $end);
    foreach ($weeks as $key => $week){$weeks[$key] = $week[0];}

    return [
        'rev' => $s->calcField('revenue', $weeks),
        'sales' => $s->calcField('sales', $weeks),
        'gpm_ave' => $s->calcField('gpm_ave', $weeks),
        'ul' => $s->calcField('uploads', $weeks),
        'pool' => $s->calcField('pool', $weeks),
        'sold_ave' => $s->calcField('sold_ave', $weeks)
    ];
}

所以我不会在任何地方覆盖该值(至少我可以看到)。如何将SUM() 与IN() 条件结合使用?

【问题讨论】:

  • 您可能的意思是使用where id IN (... 而不是where week in (..,因为您最后显示的数据是从 id = 1 到 13
  • @MadhurBhaiya 啊,不,我的意思是一周 - 但实际上并没有在一秒钟内检查周值 xD,让我快速测试另一个......更多的人口范围并回复你 xD
  • 将货币值存储为浮点数不是一个好主意
  • SHOW CREATE TABLE ws like @EdHeal 建议它接缝你正在使用 float 你应该使用 decimal .. 浮点数是近似值,所以确实像 Ed 说的不聪明使用近似值来表示金钱价值。
  • 更改表格以添加一个十进制类型的新列revenue_decimal .. 运行UPDATE SET revenue_decimal = revenue .... 并检查...之间的数据... 当数据正确时删除浮点类型并重命名十进制类型.. 或者创建一个新表,这也是可能的并运行INSERT INTO new_table SELECT * FROM old_table.. 无论如何不要对生活数据进行数据类型转换,在这种情况下最有可能不会发生任何坏事,但最好是安全然后抱歉..

标签: mysql sql


【解决方案1】:

正如 @MadhurBhaiya 在 cmets 中指出的:

逗号分隔的 week_id 值在查询中作为单个字符串传递: week_id in ('1,2,3...,15') 。 MySQL 将 this 隐式类型转换为 1,因此仅获取第一行。您将需要更改查询准备代码

导致我使用 foreach 循环将单个 ? 分解为命名参数:

public function calcField(string $field, array $weeks)
{
    $data = [];
    $endKey = end(array_keys($weeks));

    $sql = 'SELECT SUM(`'. $field .'`) AS r FROM `ws` WHERE `week` IN(';

    foreach ($weeks as $key => $week)
    {
        $sql .= ':field'. $key;
        $sql .= ($key !== $endKey ? ',' : '');

        $data[':field'. $key] = $week;
    }

    $sql .= ');';

    $stmt = $this->conn->prepare($sql);
    $stmt->execute($data);

    return $stmt->fetch(\PDO::FETCH_ASSOC)['r'];
}

现在每个数值都被单独处理,这让我得到了预期值。

【讨论】:

    【解决方案2】:

    是的,它只显示查询中的第一行数据,因为您提到了where week IN (1,2,3,4,5,6,7,8,9,10,11,12,13);,并且您的数据显示您有第 13 周,并且来自查询的 IN 子句的数据中不存在第 1 到 12 周。

    【讨论】:

      【解决方案3】:

      您只选择第 13 周的行,第 1-12 周在您的数据中不存在。这就是为什么你的查询结果是43.900001525878906

      解决方案 1:

      您可能希望更改 IN() 中的值,因为您正在按 week 列过滤数据。

      SELECT SUM(`revenue`) as `r` FROM `ws` WHERE `week` IN (13,14,15,16,17,18,19,20,21,22,23,24,25);
      

      解决方案2:

      您可以将where 子句中的week 更改为id

      SELECT SUM(`revenue`) as `r` FROM `ws` WHERE `id` IN (1,2,3,4,5,6,7,8,9,10,11,12,13);
      

      【讨论】:

      • 解决方案 2 != 解决方案,我想要一周 ^
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-12-08
      • 2020-06-24
      • 1970-01-01
      • 1970-01-01
      • 2013-06-26
      • 2014-12-11
      • 2016-01-30
      相关资源
      最近更新 更多