【问题标题】:Foreach too slowforeach 太慢了
【发布时间】:2013-03-10 22:39:18
【问题描述】:

我有 2 个数组:

$questions: pid => name
$answers: pid => rid

它在数据库中插入所有问题(pid),如果有答案(rid)则插入答案;如果没有答案,则插入 0。

foreach($questions as $value) {
    $idanswer = ($answers[$value[pid]]) ? $answers[$value[pid]] : 0;
    $idquestion = $value[pid];
    $sql = "INSERT INTO solucion ( rid, pid) VALUES ( '$idanswer ', '$idquestion ')";
    $db - > query($sql);
}

在我缺乏经验的情况下,我几乎对所有事情都使用 foreach,但在这种情况下太慢了。有什么建议吗?

【问题讨论】:

  • 我非常怀疑这与foreach 有什么关系,而是与您的数据库查询有关。
  • In my inexperience, I use foreach for almost everything 很遗憾听到这个消息。相反,您应该为所有新行形成 one INSERT 查询。
  • @Svish:INSERT 会很快;其中1,500个不会。将单个 INSERT 包装在 foreach 中是错误的。
  • @LightnessRacesinOrbit 我可以向您保证,如果您删除 $db->query($sql);,循环将接近瞬时。它正在运行需要时间的查询,这意味着解决方案,就像其他人已经在这里回答的那样,是尽量减少您运行的查询数量。与“foreach 的速度”无关。
  • @Svish:同意。但是,当 OP 选择使用 foreach 来解决导致延迟的问题时,试图声称它与 foreach 无关似乎是一件奇怪的事情。无论如何,事实证明我们都在用相反的方式说同样的话。 :)

标签: php mysql arrays foreach


【解决方案1】:

不要在 PHP 循环中包装单个 INSERT 查询,而是使用 PHP 循环在字符串中构建 单个 INSERT 查询,然后在最后执行该单个查询。

提示:INSERT 可以同时插入多个行。

虽然单个 INSERT 速度很快,但 MySQL 查询的组合往返总是无法扩展,因为您重复连接/通信/解释/文件访问开销每个您的N 数组元素...所有这些都是不必要的。

【讨论】:

  • 你也可以使用事务。只需在循环之前开始它并在之后提交。它将提高性能。仅适用于 InnoDb 表引擎。
  • 非常喜欢。在 800k 刀片上测试。再说一次,只有 InnoDb 才能大大提高性能
  • @Vadim 但是您仍然在不必要地进行 80 万次往返。也就是说,您不会对 800k 行使用 single 字符串/查询。
  • 你是对的。我建议将此选项作为解决问题的替代方法。这主要取决于主题,如果您需要获取最后插入的 ID 并在另一个插入中使用它并执行一些您无法使用批量插入的操作。
【解决方案2】:

您为什么不在单个查询中插入所有行,而不是对要插入的每一行运行一个查询?查询是您在那里支付的昂贵费用,肯定不是 foreach 的问题。

你为什么不按照以下方式去做:

$sql = "INSERT INTO solucion (rid, pid) VALUES ";
$vls = array();
foreach($questions as $value) {
    $idanswer = ($answers[$value[pid]]) ? $answers[$value[pid]] : 0;
    $idquestion = $value[pid];
    $vls[] = " ( '$idanswer ', '$idquestion ')";

}
$sql .= implode(', ', $vls);
$db->query($sql);

【讨论】:

  • @LightnessRacesinOrbit,我们都需要例子。
  • 有时。 在那个时候,示例应该补充基于散文的解释。你只是递给他一段代码,没有其他内容……真是太可惜了。这也是 SO 被“为我制作代码”非问题所淹没的原因。
  • OP 需要的是指向正确方向的指针 - OP 只会复制和粘贴您的代码,不知道它为什么或做什么
  • @Jueecy 这你的责任。这是我们所有人的责任。否则你到底为什么会在这里?这个答案现在好多了。
  • 我看不出区别。
【解决方案3】:

我建议构建一个插入多个值的 INSERT 语句,因为每个记录的结构都相同。

$values = [];
foreach($questions as $value) {
  $idanswer = ($answers[$value[pid]]) ? $answers[$value[pid]] : 0;
  $idquestion = $value[pid];
  array_push($values, "('$idanswer', '$idquestion')");
 }
 $db->query("INSERT INTO solucion (rid, pid) VALUES " . implode(',', $values));

上面的sn-p会构建如下形式的INSERT-statement

INSERT INTO table (column, column) VALUES ('value', 'value'), ('value', 'value')

最好减少插入所有记录所需的数据库命中次数。

有关 INSERT 语句的完整参考,请阅读documentation

【讨论】:

  • WHY 在此处添加另一个答案的副本 ... UPVOTE 当前答案 ...
  • 我赞成很好地描述问题及其解决方案的文字答案。感谢您指出。
【解决方案4】:

这很慢,因为每次 foreach 迭代执行 1 个事务。

在 foreach 循环中构建整个 SQL 语句,然后在 foreach 完成后执行一次。

INSERT INTO tbl_name (a,b) VALUES(1,2),(4,5),(7,8);

【讨论】:

    【解决方案5】:

    不要运行多个插入语句,而是构建一个 INSERT 语句,然后只运行一个语句。例如见下文。

    $query = "INSERT INTO solucion ( rid, pid) VALUES";
    
    foreach($questions as $value) {
        $idanswer = ($answers[$value[pid]]) ? $answers[$value[pid]] : 0;
        $idquestion = $value[pid];
        $query .= " ( '$idanswer ', '$idquestion '),";
    }
    
    $query = rtrim($query, ",").";";
    
    $db - > query($sql);
    

    【讨论】:

    • WHY 在此处添加另一个答案的副本 ... UPVOTE 当前答案 ...
    • 我在任何答案出现之前点击了答案,当我点击提交时,另一个答案已被提出,我只是回答时没有一些人那么快,抱歉。
    猜你喜欢
    • 1970-01-01
    • 2019-04-15
    • 2014-06-07
    • 2016-05-31
    • 2011-07-07
    • 2015-08-23
    • 2012-07-05
    • 2016-01-08
    • 2014-03-12
    相关资源
    最近更新 更多