【问题标题】:Memory exhausted using an array in a nested loop在嵌套循环中使用数组耗尽内存
【发布时间】:2015-05-27 18:52:44
【问题描述】:

这是我遇到的错误。

致命错误:允许的内存大小已用完。

我正在获取包含日期 from 和日期 till 的数组。我正在尝试获取介于两者之间的所有日期并将它们添加到新数组中。显然嵌套循环和多数组很累。

我需要一种不那么费力的方法来获取所有日期。

这是我的代码:

$query = "SELECT *
          FROM reservate
          ORDER BY from";
$result = $connect->query($query);

$reservations = array();
if ($result->num_rows > 0) {
    while($row = $result->fetch_assoc()) {
        $reservations[] = $row;
    }
}

$connect->close();

//AVAILABILITY CHECK
$nonAvailable = array();
foreach($reservations as $reservation){
    $from = $reservation['from'];
    $till = $reservation['till'];

    while($from <= $till){
        $nonAvailable[] = $from;
        $from = date('Y-m-d', strtotime("+1 days"));
    }
}

【问题讨论】:

  • 虽然我认为@robbmj 已经发现了这个问题,但这些日期范围有多大?它们是用来做什么的?如果您只是在进一步查询中找到不可用的日期,那么可以只返回不可用的日期作为 SQL 查询的结果。
  • 我测试的日期在 8 天左右。最终它将不得不处理最多 2 个月的日期范围。至于 sql,我还没有花太多精力学习比基础知识更多的东西。我最终的目标是:我有一个生成的日历,我想突出显示不可用的日期。我或多或少地让它工作了,但不得不做出一些改变。
  • 我会写一个额外的答案给你一些想法,尽管已经接受的答案是对你实际问题的完美答案。

标签: php mysql multidimensional-array out-of-memory nested-loops


【解决方案1】:

看起来你做了一个无限循环1

// if $till is in the future, this is an infinite loop
while($from <= $till){
    // appending the same value of $from on each iteration
    $nonAvailable[] = $from;
    // as $from never changes value
    $from = date('Y-m-d', strtotime("+1 days"));
}

1 天添加到$from 的当前值

while ($from <= $till){
     $nonAvailable[] = $from;
     // add 1 day to $from
     $from = date('Y-m-d', strtotime($from, "+1 days"));
}

还有一些可以改进的地方:

  • 在以下示例中,未将整个表复制到 reservation 数组,我们只检索我们需要的列 从表。
  • 比较 integers 而不是 strings
  • 手动将1 day 添加到$from,而不是依靠strtotime 来完成。
$query = "SELECT from, till
          FROM reservate
          ORDER BY from";
$result = $connect->query($query);

$nonAvailable = array();

if ($result->num_rows > 0) {
    while ($row = $result->fetch_assoc()) {
        $from = strtotime($row['from']);
        $till = strtotime($row['till']);

        while ($from <= $till) {
            $nonAvailable[] = date('Y-m-d', $from);
            $from += 60*60*24;
        }
    }
}

$connect->close();

这个算法很可能会变得更有效率。例如,您是否需要一份详尽的清单,列出每一天的预订情况?如果不是,那么只需开始日期和结束日期就足够了。


1 如果有足够的时间和内存,循环将退出,因为strtotime("+1 days") 最终会返回一个大于$till 的值。

【讨论】:

  • @iDobbler,不用担心,我很乐意提供帮助。我更新了答案,提供了一些您可能感兴趣的提示,请看一下。
  • 感谢您的建议。今天对我来说不是一个美好的一天xD。我采用了一种全新的方法,制作了一个不同的功能,现在它可以正常工作了。
【解决方案2】:

另一种方法,只使用一个查询。

这将获取您的预订表并将其与几个获取数字范围的子查询交叉连接。每个得到数字 0 到 9,一个用作单位,一个用作十。将它们组合在一起可以得到数字 0 到 99(即 99 天的范围)。

WHERE 子句仅检查起始日期 (van) 加上天数(从 0 到 99)是否小于或等于截止日期 (tot)。因此,一个房间(我假设有多个房间,或者其他一些被预订的项目)在每个预订日期都被淘汰:-

SELECT room, DATE_ADD(van, INTERVAL (tens.i * 10 + units.i) DAY) AS non_available
FROM reservate
CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units
CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) tens
WHERE DATE_ADD(van, INTERVAL (tens.i * 10 + units.i) DAY) <= tot
ORDER BY room, non_available

SQL 小提琴示例:-

http://www.sqlfiddle.com/#!9/83054/1

您可以使用整数表代替 2 个子查询。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-12-28
    • 2018-03-01
    • 2016-09-17
    • 2014-10-04
    • 2014-12-13
    • 1970-01-01
    • 2021-11-24
    • 2019-04-25
    相关资源
    最近更新 更多