mysql select语句中的算术 - 内存大小错误

时间:2014-09-22 21:05:21

标签: php mysql pdo

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

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

这是一个消息队列...我试图让sends_in成为发送消息的秒数值。

例如,使用正常时间而不是时间戳...最后一封电子邮件是在晚上7点发送的,费率是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 sending_in

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

  

TIMESTAMPDIFF(第二,UTC_TIMESTAMP(),FROM_UNIXTIME(UNIX_TIMESTAMP()   + 15 * 60))AS sending_in

更新:

我的错误

- 返回正确的值:

  

TIMESTAMPDIFF(第二,现在(),FROM_UNIXTIME(UNIX_TIMESTAMP()+ 15 *   60))AS sending_in

工作解决方案:

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

  

TIMESTAMPDIFF(第二,现在(),   FROM_UNIXTIME(CONVERT(alerts.last_email,SIGNED)+ alerts.email_rate *   60))AS sending_in

1 个答案:

答案 0 :(得分: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日期时间函数。


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