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 的问题(同样我看不到表,所以我猜)。
我正在尝试更新数据库中的许多行(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 的问题(同样我看不到表,所以我猜)。