doujuan9698 2014-11-06 21:15
浏览 76
已采纳

MYSQL和PHP:在PHP while循环中运行INSERT INTO SELECT查询,运行缓慢

I'm really new to php and MYSQL, i knew nothing about either a month ago, so please forgive my sloppy/poor code :)

I have the following code within my PHP:

$starttime = microtime(true);
$q_un = 'SELECT i.id AS id
            FROM items i 
            WHERE i.id NOT IN (SELECT item_id FROM purchased_items WHERE user_id=' . $user_id . ')';
$r_un = mysqli_query($dbc, $q_un);
if (mysqli_num_rows($r_un) > 0) {
while ($row_un = mysqli_fetch_array($r_un, MYSQLI_ASSOC)) {
    $item_id = $row_un['id'];
    $q_rec = 'INSERT INTO compatibility_recommendations (
                `recommendation`,
                `user_id`,
                `item_id`)
                SELECT
                    ((SUM(a.rating*(a.compat-80)))/(SUM(a.compat-80)))*10 AS rec,
                    a.user_id AS user_id,
                    a.item_id AS item_id
                FROM
                    (SELECT r.rating AS rating, 
                        c.user2_id AS rater, 
                        c.user1_id AS user_id, 
                        c.compatibility AS compat, 
                        r.item_id AS item_id 
                    FROM ratings r
                    RIGHT JOIN compatibility_ratings c ON r.user_id=c.user2_id
                    WHERE c.user1_id=' . $user_id . ' AND r.item_id=' . $item_id . ' AND c.compatibility>80) a
                ON DUPLICATE KEY UPDATE
                    recommendation = VALUES(recommendation)';
    $r_rec = mysqli_query($dbc, $q_rec);
}
}
$endtime = microtime(true);
$duration = $endtime - $starttime;</code>

The first query selects a list of items that the current user, $user_id, hasn't purchased yet. I then run a while loop on each row (item) that is returned, performing the main query within this loop.

This next query is taking info from the ratings table where the item_id is equal to the current item_id which is being queried, and joins it to a precomputed user compatibility table with a right join.

I then run arithmetic on the ratings and compatibility ratings to form a recommendation value, and then insert the recommendation, item_id and user_id into another table to be called later. There's a 2 column unique key on the (item_id,user_id) columns, hence the ON DUPLICATE KEY UPDATE at the end

So i wrote this code this morning and was quite happy with myself as it does exactly what i need it to do.

The problem is that, predictably, it's slow. On my test DB, with 5 test users and 100 test items and a random assortment of 200 ratings, it's taking 2.5 seconds to run through the while loop. I was expecting it to be slow, but not this slow. it's really going to struggle once more users and items are added. The main problem is on the insert...on duplicate key update part, my disk utilisation goes to 100% and i can tell my laptop's HDD is seeking like crazy. I know I will probably use SSDs in production, but I would still anticipate a major scale issue with thousand of items and users.

So my main question here is: can anyone give any advice on how to optimise my code, or completely rejig things to improve speed. I'm sure that the insert query within a while loop is a poor way of doing this, i just can't think of any other way to obtain the exact same results

Thanks in advance and sorry if i formatted my question incorrectly

  • 写回答

3条回答 默认 最新

  • douhao5280 2014-11-07 12:30
    关注
    $starttime = microtime(true);
    $q_un = "
    
     INSERT INTO compatibility_recommendations 
     (recommendation
     ,user_id
     ,item_id
     )
     SELECT ((SUM(a.rating*(a.compat-80)))/(SUM(a.compat-80)))*10 rec
          , a.user_id 
          , a.item_id 
       FROM
          ( SELECT r.rating rating
                 , c.user2_id rater
                 , c.user1_id user_id
                 , c.compatibility compat
                 , r.item_id 
              FROM compatibility_ratings c
              JOIN ratings r
                ON r.user_id = c.user2_id
    
              JOIN items i
                ON i.id = r.item_id
    
              LEFT
              JOIN purchased_items p
                ON p.item_id = i.id
               AND p.user_id = $user_id
    
             WHERE c.user1_id =  $user_id
               AND c.compatibility > 80
               AND p.item_id IS NULL
          ) a
     GROUP BY a.item_id
     ON DUPLICATE KEY UPDATE recommendation = VALUES(recommendation);
    
     ";
    
    $r_rec = mysqli_query($dbc, $q_rec);
    }
    }
    $endtime = microtime(true);
    $duration = $endtime - $starttime;</code>
    

    For any further improvement, we'd really need to see proper DDLs AND the EXPLAIN for the SELECT above.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥15 安卓adb backup备份应用数据失败
  • ¥15 eclipse运行项目时遇到的问题
  • ¥15 关于#c##的问题:最近需要用CAT工具Trados进行一些开发
  • ¥15 南大pa1 小游戏没有界面,并且报了如下错误,尝试过换显卡驱动,但是好像不行
  • ¥15 没有证书,nginx怎么反向代理到只能接受https的公网网站
  • ¥50 成都蓉城足球俱乐部小程序抢票
  • ¥15 yolov7训练自己的数据集
  • ¥15 esp8266与51单片机连接问题(标签-单片机|关键词-串口)(相关搜索:51单片机|单片机|测试代码)
  • ¥15 电力市场出清matlab yalmip kkt 双层优化问题
  • ¥30 ros小车路径规划实现不了,如何解决?(操作系统-ubuntu)