あなたが参加している値を含むテーブルから削除しているので、内部の結合は最新のコメントに基づいて冗長です。本質的には、cdr_2017
で比較される値の数は、内部結合によって変更されないため、b.id_cdr = a.id_cdr
を2回処理する必要があります。削除される値の数だけが変更されます。
増分遅さの原因は、手動でSELECT cdr_id FROM cdr_2017 LIMIT 10000 OFFSET x
と同じ機能を実行しているためです。
つまり、削除するID値を決定するために、クエリでフルテーブルスキャンcdr_2017
を実行する必要があります。値を削除すると、SQLオプティマイザはcdr_2017
テーブルをさらに移動して値を取得する必要があります。 cdr_id
は、あなたが選択した値をフィルタリングするcdr_2017
から取得した最後のインデックスを使用することがある問題を解決するための増分主キー、であると仮定すると、
DELETE FROM IN(1,2,3,...10000)
DELETE FROM IN(1,2,3,...20000)
...
DELETE FROM IN(1,2,3,...1000000)
に結果の
。 クエリの両側でインデックス値を利用しているため、結合されたレコードの検証にフルテーブルスキャンが不要になるため、これははるかに高速になります。
$sql = " SELECT COUNT(a.cdr_id) FROM historisation.cdr_".$annee." a
INNER JOIN transatel.cdr_transatel_v2 b ON a.id_cdr = b.id_cdr ";
$t = $db_transatel->selectAll($sql);
//The number of lines I have to delete
$i = $t[0][0];
//set starting index
$previous = 0;
do {
if ($i < $limit) {
$limit = $i;
}
$selectFromHistoryAndDelete = 'DELETE d
FROM transatel.cdr_transatel_v2 AS d
JOIN (
SELECT @previous := cdr_id AS cdr_id
FROM historisation.cdr_2017
WHERE cdr_id > ' . $previous . '
ORDER BY cdr_id
LIMIT 10000
) AS a
ON a.cdr_id = d.cdr_id';
$db_transatel->exec($selectFromHistoryAndDelete, $params);
//retrieve last id selected in cdr_2017 to use in next iteration
$v = $db_transatel->selectAll('SELECT @previous'); //prefer fetchColumn
$previous = $v[0][0];
$i = $i - $limit;
} while ($i > 0);
//optionally reclaim table-space
$db_transatel->exec('OPTIMIZE TABLE transatel.cdr_transatel_v2', $params);
また、また、パフォーマンスを向上させる必要がある制限条項によって順序を削除するにはcdr_id > $previous AND cdr_id < $last
を使用するようにリファクタリングできます。
この操作中、MyISAMデータベースエンジンによってcdr_transatel_v2のテーブルロックが実行されることに注意してください。 MySQLが並行セッションとクエリを処理する方法のため、この方法ではバッチ削除による利益はあまりなく、InnoDBとトランザクションにのみ適用されます。特に、Apache mod_phpとは対照的に、FastCGIでPHPを使用する場合。cdr_transatel_v2以外のクエリは実行され、cdr_transatel_v2の書き込み操作は依然としてキューに入れられます。 mod_phpを使用している場合は、制限時間を1,000
レコードに減らしてキュー時間を短縮します。詳細について はhttps://dev.mysql.com/doc/refman/5.7/en/internal-locking.html#internal-table-level-locking
代替アプローチを参照してください。
削除する必要があるレコードの数が多いことを考慮すると、削除されたレコードが削除されたレコードを超えると、DELETE
の代わりにINSERT
を使用して操作を逆転させると効果的です。
#ensure the storage table doesn't exist already
DROP TABLE IF EXISTS cdr_transatel_temp;
#duplicate the structure of the original table
CREATE TABLE transatel.cdr_transatel_temp
LIKE transatel.cdr_transatel_v2;
#copy the records that are not to be deleted from the original table
INSERT transatel.cdr_transatel_temp
SELECT *
FROM transatel.cdr_transatel_v2 AS d
LEFT JOIN historisation.cdr_2017 AS b
ON b.cdr_id = d.cdr_id
WHERE b.cdr_id IS NULL;
#replace the original table with the storage table
RENAME TABLE transatel.cdr_transatel_v2 to transatel.backup,
transatel.cdr_transatel_temp to cdr_transatel_v2;
#remove the original table
DROP TABLE transatel.backup;
ロールバックセグメント。 DBはどこかの行を削除してロールバックを処理できると思います。各削除の後にコミットを試みてください。 – StanislavL
悲しいことに、私はすでにそれについて考えていますが、テーブルにあるテーブルはMyIASMにあり、ロールバックとコミットをサポートしていません。テーブルが大きい場合、 – McKenneii
INが遅すぎます。 JOINを使用して削除する行を見つける方がはるかに高速です。 – vladatr