【问题标题】:arithmetic in mysql select statement - memory size errormysql select语句中的算术 - 内存大小错误
【发布时间】:2014-09-22 21:05:20
【问题描述】:

当我在最后一个选择中添加算术时,收到此查询的以下错误消息......这是一个非常简单的计算,所以不确定问题是什么:

alert.last_email is a utc timestamp (unsigned integer)
alerts.email_rate is value in minutes (unsigned integer)

这是一个消息队列...我试图将sends_in 设置为发送消息的秒数。

例如使用正常时间而不是时间戳...最后一封电子邮件是在晚上 7:00 发送的,费率为 15 分钟,因此它将在 7:15 发送。如果当前时间是 7:10,则发送时间为 5 分钟或 300 秒。

致命错误:已用尽允许的 33554432 字节内存大小(已尝试 在第 0 行的 Unknown 中分配 505364672 字节

$stmt = $db->prepare("
    SELECT
        users.username AS username,
        computers.computer_name AS computer_name,
        alerts.last_email AS last_email,
        alerts_queue.alert_id AS alert_id,
        alerts_queue.event_title AS event_title,
        alerts_queue.event_target AS event_target,
        alerts_queue.capture_timestamp AS capture_timestamp,
        alerts.last_email + (alerts.email_rate * 60) - UNIX_TIMESTAMP() AS sends_in
    FROM computers
    INNER JOIN users
        ON users.computer_id = computers.computer_id
    INNER JOIN alerts
        ON alerts.user_id = users.user_id
    INNER JOIN alerts_queue
        ON alerts_queue.user_id = alerts.user_id
    WHERE computers.account_id = :cw_account_id AND computers.status = :cw_status
");

$binding = array(
    'cw_account_id' => $_SESSION['user']['account_id'],
    'cw_status' => 1
);
$stmt->execute($binding);

$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

更新:

计算未按预期工作:

TIMESTAMPDIFF(秒, UTC_TIMESTAMP(), FROM_UNIXTIME(UNIX_TIMESTAMP() + alerts.email_rate * 60)) AS send_in

用这个作为测试......差异应该是 900 秒,但它返回 -13500。

TIMESTAMPDIFF(秒, UTC_TIMESTAMP(), FROM_UNIXTIME(UNIX_TIMESTAMP() + 15 * 60)) AS发送_in

更新:

我的错误 - 这将返回正确的值:

TIMESTAMPDIFF(SECOND, NOW(), FROM_UNIXTIME(UNIX_TIMESTAMP() + 15 * 60)) AS发送_in

工作解决方案:

它看起来有点草率,但这是可行的。 TIMESTAMPDIFF 使用当前时区...我的 last_email 值是一个 unix 时间戳,这是 FROM_UNIXTIME 发挥作用的地方。因为last_email 是无符号整数,所以除非将其转换为有符号整数,否则算术将无法正常工作。我总是将时间戳设置为无符号的原因是因为永远不会有负数,但是在使用 MySQL 进行算术运算的情况下,这会导致问题。

TIMESTAMPDIFF(SECOND, NOW(), FROM_UNIXTIME(CONVERT(alerts.last_email,SIGNED) + alerts.email_rate * 60)) AS发送_in

【问题讨论】:

  • 这不是 mysql 错误。您可能返回的数据行过多,导致内存超载。
  • 在这个测试中,只有两行数据...在没有涉及数学的最后一个选择的情况下工作得很好。
  • 你试过使用 MySQL 的内置函数吗? TIMESTAMPDIFF(SECOND, FROM_UNIXTIME(alerts.last_email + alerts.email_rate * 60), NOW()) AS sends_in 或类似的?理想情况下,时间将存储为 DATETIME 而不是 INT...
  • @miken32 - 虽然我需要颠倒值的顺序,但这很有效。老实说,我不知道这存在,但仍然好奇为什么其他方法不起作用。此外,NOW() 返回当前时区...我需要 UTC 时间。
  • @miken32 - 谢谢...上面有一个可行的解决方案,部分满足您的建议。

标签: php mysql pdo


【解决方案1】:

这似乎是 PHP 错误,而不是 MySQL 错误。我怀疑在调用fetchAll 函数时会发生此错误;导致错误的是数组$results 的分配。 (我只是猜测。)根据错误消息,尝试分配几乎 482 MB 的内存,但 PHP 限制为 32 MB。

(我认为 PHP 中有一个设置设置了这个限制,但我不认为你真的想去那里......因为似乎没有任何理由需要分配那么多内存。

部分问题在于 fetchAll,同时将结果集中的每一行检索到内存中。

但是,如果这仅在您将最后一个表达式添加到 SELECT 列表中时发生,我怀疑 PDO 为最后一个表达式的结果保留了 过多 的内存量。例如,MySQL 在元数据中报告了一个奇怪的数据类型和/或 PDO 没有检测到该表达式返回的正确数据类型,并且正在使用一些巨大的分配来存储它。


为了调试它,尝试更改该查询以返回一个文字整数值来代替最后一个表达式,也就是说,替换这个:

alerts.last_email + (alerts.email_rate * 60) - UNIX_TIMESTAMP() AS sends_in

用这个:

42 AS sends_in 

看看 PHP 是否发出同样的错误信息。如果不是,我怀疑 PDO 没有将您的原始表达式视为简单的整数类型。下一步是将该表达式的结果 CAST 或 CONVERT 转换为整数数据类型...CONVERT(expr,UNSIGNED)CONVERT(expr,SIGNED)

也就是说,将最后一个表达式替换为:

CONVERT(alerts.last_email + (alerts.email_rate * 60) - UNIX_TIMESTAMP(),SIGNED) 
  AS sends_in

然后旋转一下,看看会产生多大的烟球。

就实际的 MySQL 语句而言,我认为尝试减去无符号整数可能会引发错误。 (我认为 SQL_MODE 中有一些东西可以控制这种行为,我认为这种行为取决于 MySQL 版本。)可能有必要将无符号整数转换/强制转换为有符号(64 位)整数,像这样丑陋的东西可能工作:

CONVERT(CONVERT(alerts.last_email,SIGNED) + (CONVERT(alerts.email_rate,SIGNED) * 60) 
  - CONVERT(UNIX_TIMESTAMP(),SIGNED),SIGNED)
  AS sends_in

就个人而言,我更愿意避免使用日期时间和时间戳值进行无符号整数数学运算,并使用 MySQL 日期时间函数。


我不确定我是否回答了你的问题。我什至不确定你问了一个问题。

【讨论】:

  • 明确设置为 42 工作正常。使用第一个 CONVERT 表达式会给出完全相同的错误。使用第二个表达式有效。我想你已经把它钉在了头上——它是使用无符号整数......我一直这样做是出于习惯,但在这种情况下,它会导致问题。
  • 在上面使用解决方案组合添加了一个工作解决方案。使用了 datetime 函数,还需要转换我自己的时间戳值。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-25
  • 2013-04-25
  • 1970-01-01
  • 2012-10-03
相关资源
最近更新 更多