【发布时间】:2021-02-24 16:48:07
【问题描述】:
我正在尝试更新我的数据库中的许多行(100 000+),但这需要一段时间(超过 10 分钟但仍未完成)。我想知道这是预期的行为还是我的代码有问题。为了防止数据库在执行更新时挂起,我被告知一次更新一行,不确定是否应该这样实现。
如果这些歌曲在我的播放列表中播放,我会将歌曲表中的图像更新为 null
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 的评论后,我做了一些更改: 歌曲表中有 500 000 行,如果我将 batchSize 增加到 10000 大约需要 10 分钟(上面的代码需要 8 小时)。在批量大小为 250 时,大约需要 50 分钟。我选择 250 是因为查询运行大约需要 1 秒,并且以 10000 批量大小运行需要 10 多秒(约束是 1 秒)。我想让它更快,但不知道还有什么要改变的
$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);
}
【问题讨论】:
-
可能是您可以避免循环并使用单个查询执行更新...尝试更新您的问题添加适当的数据样本和预期结果作为表格文本
-
'LIKE' 仅在您使用区分大小写的排序规则时才有意义,并且锁定后的啤酒表明您不是。
-
问题是您在 while 循环中运行 SQL。在 while 循环中执行数据库命令会使运行时间变成您所看到的大量时间。你必须把它从while循环中解脱出来。您可以在一次 DB 命中而不是在 while 循环中执行此操作。我将编辑我的回复。
-
考虑您的 SQL,在您的第二个代码示例中,对于第一个数据库中的每首歌曲,您正在运行查询以更新也在第二个数据库播放列表中的 250 首歌曲。您不需要在 while 循环中执行此操作。