更新MYSQL表时While循环内的性能降低

时间:2014-07-25 09:12:25

标签: php mysql sql performance

使用表product中的数据执行WHILE LOOP并更新同一数据库中的另一个表stock_figures时,以下代码运行速度极慢。

代码循环遍历product中的每一行,从product_idwholesale_price获取值,然后在更新product之前对stock_figures表执行一些计算带有值的表。

我会感激任何可以改善查询性能的建议。

PHP WHILE LOOP

<?

// Retrieve data from database
$loop = " SELECT product_id, wholesale_price FROM product";
$query= mysql_query($loop);

while($rows=mysql_fetch_assoc($query))
    {
        $row = mysql_fetch_row($query);
        $id = $row[0];
        $price = $row[1];

?>

循环中的查询

<?

        $bawtry_stock = "

        SELECT product_id,
        ( kids_uk_j_105 + kids_c_17 + kids_c_18 + kids_c_19 + ... etc ) 

        AS SUM FROM product WHERE product_id = '$id'";

        $result_bawtry = mysql_query($bawtry_stock) or die (mysql_error());
        $line = mysql_fetch_row($result_bawtry);
        $bawtry = $line[1];

        $chain_stock = "

        SELECT product_id, 
        (quantity_c_size_26_chain + quantity_c_size_28_chain + quantity_c_size_30_chain +
        ... etc )

        AS SUM FROM product WHERE product_id = '$id'";

        $result_chain = mysql_query($chain_stock) or die (mysql_error());
        $line = mysql_fetch_row($result_chain);
        $chain = $line[1];

        /*
         * Declare the total value of all pairs from Bawtry, Chain
         */

        $totalpairs = $chain + $bawtry;

        /*
         * Insert values for stock to write to databse
         * Total stock for Bawtry, Chain
         * Total value of stock for Bawtry, Chain
         *
         */

        $bawtry_value = (float)($bawtry * $price);

        $chain_value = (float)($chain * $price);

        $total_value = (float)($price * ($bawtry + $chain));

        $sql2="

        UPDATE stock_figures SET 
        bawtry_stock='$bawtry',
        chain_stock='$chain',
        totalstock='$totalpairs',
        bawtry_value='$bawtry_value',
        chain_value='$chain_value',
        totalvalue='$total_value'

        WHERE id='$id'";

        $result2=mysql_query($sql2) or die (mysql_error());



?>

// close while loop
<? } ?>

更新代码

$sql = "SELECT product_id, wholesale_price,
        (kids_uk_j_105 + kids_c_17 + kids_c_18 + kids_c_19 + kids_c_20 + kids_c_21 +
        ... )

        AS bawtry,

        (quantity_c_size_26_chain + quantity_c_size_28_chain + quantity_c_size_30_chain +
        ... )

        AS chain from product";

        $result = mysql_query($sql) or die (mysql_error());

        while ($line=mysql_fetch_assoc($result)) 

        {
            $id = $line['product_id'];
            $price = $line['wholesale_price'];
            $bawtry = $line['bawtry'];
            $chain = $line['chain'];

        /*
         * Declare the total value of all pairs from Bawtry, Chain 
         */

        $totalpairs = $chain + $bawtry;

        /*
         * Insert values for stock to write to database
         * Total stock for Bawtry, Chain
         * Total value of stock for Bawtry, Chain
         *
         */

        $bawtry_value = (float)($bawtry * $price);

        $chain_value = (float)($chain * $price);

        $total_value = (float)($price * ($bawtry + $chain));

        $sql2="

        UPDATE stock_figures SET 
        bawtry_stock='$bawtry',
        chain_stock='$chain',
        totalstock='$totalpairs',
        bawtry_value='$bawtry_value',
        chain_value='$chain_value',
        totalvalue='$total_value'

        WHERE id='$id'";

        $result2=mysql_query($sql2) or die (mysql_error());

然而,它仍然需要一个绝对的年龄才能完成。当我在最后注释掉UPDATE语句时,它似乎运行得非常快。显然这需要保留在代码中,所以我可能会将整个事情作为一个cronjob来运行。

除非可以建议进一步改进吗?

3 个答案:

答案 0 :(得分:3)

看起来你做了很多浪费的选择。

首先从表格产品中选择一些数据,然后从同一个表格中再次选择每一行。两次。然后最后将其插入另一个表stock_figures。

你正在做的唯一操作是将大量数字加在一起。

所有这一切都可以在一次查询中完成。

select product_id,
       whole_sale_price, 
       sum(kids_uk_j_105, 
           kids_c_17,
           ...) as bawtry,
       sum(quantity_c_size_26_chain, 
           quantity_c_size_28_chain, 
           ...) as chain
  from products; 

如果这仍然需要花费大量时间,您需要检查一些服务器设置和行数

您所做的每一次写操作都是一个事务,并且根据您的ACID级别,执行提交可能会很慢。将innodb-flush-log-at-trx-commit更改为2将加快写入速度。

您正在对产品表执行全表扫描。我想这是有意的,但如果那个表读数很大,则需要一段时间,将所有这些行写回stock_figures将需要更长的时间。

考虑另一种方法。对于每次写入(插入,更新或删除)产品都有一个触发器更新stock_figures中的相应行。它不仅可以消除批处理作业,还可以使stock_figures在任何给定时间都正确。

答案 1 :(得分:1)

首先是:

 $row = mysql_fetch_row($query);
 $id = $row[0];
 $price = $row[1];

我不知道它是否适合您,但您已经在while条件下占用$行,所以可能应将其更改为:

 $id = $rows['product_id'];
 $price = $row['wholesale_price'];

接下来的2个查询可以组合信息:

SELECT product_id,
        ( kids_uk_j_105 + kids_c_17 + kids_c_18 + kids_c_19 + ... etc ) 

        AS `SUM` FROM product WHERE product_id = '$id'

UNION ALL

SELECT product_id, 

        (quantity_c_size_26_chain + quantity_c_size_28_chain + quantity_c_size_30_chain +
        ... etc )

        AS `SUM` FROM product WHERE product_id = '$id'

甚至:

SELECT product_id,
        ( kids_uk_j_105 + kids_c_17 + kids_c_18 + kids_c_19 + ... etc ) 

        AS `SUM1`,
        (quantity_c_size_26_chain + quantity_c_size_28_chain + quantity_c_size_30_chain +
        ... etc )
       AS `SUM2`
  FROM product WHERE product_id = '$id'

因为这两个查询在同一个表上运行。

但事实上,您可以只使用一个查询来获取有关您产品的所有内容,因为 Andreas Wederbrand 在他的回答中指出。

但还有更多问题:

  1. 您使用旧的mysql_函数而不是mysqli_PDO,并且您的代码容易受到SQL注入攻击

  2. 对于每个产品,您运行2个额外的查询(如果您按照我的方式更新,请选择union all。)

  3. 我不知道你有多少产品,但如果你有1000个产品或10000个产品,你就不会想到它会非常快。在这种情况下,您应该以某种方式在cron中运行您的脚本或刷新页面并为少量产品(例如一次10或100)完成工作。
  4. 您还应该考虑您的数据库结构是否最好。通常在此使用多列,kids_uk_j_105kids_c_17kids_c_18不是最佳选择。
  5. 我希望您至少在product_id列设置了关键的primary_id。

答案 2 :(得分:0)

执行许多SQL命令时,解析它们需要一些时间。您可以使用http://php.net/manual/en/mysqli.quickstart.prepared-statements.php来减少此开销 你获得了多少,取决于具体情况。

出于安全原因,准备好的陈述也很有用。

这个答案不会使其他答案无效。尝试通过减少查询数量,分析他们的工作,在可能的情况下合并它们来提高效率等。