SQL 更新耗时较长

SQL Update taking a long time

我正在尝试更新数据库中的许多行(100 000 多行),但这需要一段时间(超过 10 分钟但仍未完成)。我想知道这是预期的行为还是我的代码有问题。为了防止数据库在执行更新时挂起,我被告知一次更新一行,不确定是否应该这样实现。

我正在更新我的歌曲 table 中的图像,如果这些歌曲在我的播放列表中播放过 table

private function updateBlogSongs ($blog_id) {

        $db = Yii::app()->db;

        $affectedRows = 0;


        $sql = "SELECT *

                FROM `firstdatabase`.song s

                INNER JOIN `seconddatabase`.playlist p ON s.name LIKE p.song_name";


        $dataReader = $db->createCommand($sql)->query(); // Rows from the song table that were played in the given blog

        $row = $dataReader->read();


        while ($row != false) {

            $sql = "UPDATE `firstdatabase`.song s

                    SET s.image = NULL

                    WHERE s.song_id = " . $row['song_id'];


            $affectedRows += $db->createCommand($sql)->execute();

            

            $row = $dataReader->read();

        }


        return $affectedRows;

    }

编辑:在阅读了 The Dog 的评论后,我做了一些修改: 歌曲 table 中有 500 000 行,如果我将 batchSize 增加到 10000 大约需要 10 分钟(上面的代码需要 8 小时)。批量大小为 250 时,大约需要 50 分钟。我选择 250 是因为查询 运行 大约需要 1 秒,而在 10000 批处理大小(约束为 1 秒)下,查询 运行 需要 10 多秒。我想让它更快,但不确定还有什么要改变

$batchSize = 250;
        $lastSongID = 0;
        $rowIndex = 0;
        $affectedRows = 0;

        $sql = "SELECT max(song_id) FROM `firstdatabase`.song";
        $lastSongID = intval($db->createCommand($sql)->query()->read()['max(song_id)']);
        
        echo($lastSongID . ' songs in table.' . PHP_EOL);
        echo('Updating songs...' . PHP_EOL);

        while($rowIndex <= $lastSongID) {
            $startTime = microtime(true);

            
            $sql = "UPDATE `firstdatabase`.song
                    SET image = NULL
                    WHERE song_id in (
                        SELECT song_id
                        FROM (
                            SELECT song_id, name 
                            FROM `firstdatabase`.song 
                            WHERE song_id > " . $rowIndex . "
                            LIMIT " . $batchSize . "
                        ) s
                        INNER JOIN (
                            SELECT DISTINCT song_name 
                            FROM `seconddatabase`.playlist 
                        ) p ON s.name LIKE p.song_name
                        ORDER BY s.song_id ASC
                    )";

            $affectedRows += $db->createCommand($sql)->execute();


            $rowIndex += $batchSize;
            
            $endTime = microtime(true);
            $elapsedTime = round($endTime - $startTime, 2);
          
            
        }

这确实是 SQL 世界而不是 PHP 世界的问题,但这是我的建议:

不要在 while 循环中一次一行地执行此操作。制作一个更复杂的更新语句,可以在一次数据库命中中完成所有操作。数据库命令是 php 代码中最慢的部分,您想限制对数据库的调用次数。

当您确信可以通过一个 sql 命令完成操作时,或者即使您认为不可能,也可以将您的代码放入数据库中的存储过程中。将复杂的 sql 查询作为存储过程可以极大地帮助维护代码。

确保您的表上有索引。您需要确保您的查询命中这些索引以获得最佳性能。

这是单个查询的选项:

update `firstdatabase`.song
set image = null
where song_id in (
    select s.song_id 
    from `firstdatabase`.song s
    INNER JOIN `seconddatabase`.playlist p 
        ON s.name LIKE p.song_name"
);

显然我们无法访问您的数据库,因此您需要在必要时进行更改,但希望它能让您走上正轨。

编辑: 尝试用以下内容替换您的第二个代码集:

    $lastSongID = 0;
    $rowIndex = 0;
    $affectedRows = 0;
    
    $sql = "SELECT max(song_id) FROM `firstdatabase`.song";
    $lastSongID = intval($db->createCommand($sql)->query()->read()['max(song_id)']);
    
    echo($lastSongID . ' songs in table.' . PHP_EOL);
    echo('Updating songs...' . PHP_EOL);
    
    $startTime = microtime(true);
    $sql = "
        update `firstdatabase`.song
        set image = null
        where song_id in (
            select s.song_id 
            from `firstdatabase`.song s
            INNER JOIN `seconddatabase`.playlist p 
                ON s.name LIKE p.song_name"
        )";
    
    $affectedRows += $db->createCommand($sql)->execute();
        
    $endTime = microtime(true);
    $elapsedTime = round($endTime - $startTime, 2);

如果有效,请告诉我 运行 所需的时间,如果无效,是否是 SQL 的问题(同样我看不到表,所以我猜)。