Вопрос или проблема
Я выполняю пакетную вставку в коде ниже, извлекая данные из одной базы данных и вставляя их в другой источник. Данные вставляются полностью, за исключением последней строки. Вместо этого дублируется предпоследняя строка.
Журнал ошибок также показывает фактическое количество строк данных.
try {
// Подключение к исходной базе данных
$sourceDB = new PDO("mysql:host=$hostSource;dbname=$dbSource", $userSource, $passSource);
$sourceDB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Подключение к целевой базе данных
$targetDB = new PDO("mysql:host=$hostDest;dbname=$dbDest", $userDest, $passDest);
$targetDB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Шаг 1: Извлечение данных из исходной базы данных
$query = "SELECT * FROM chapter_contributor_info";
$sourceData = $sourceDB->query($query)->fetchAll(PDO::FETCH_ASSOC);
// Шаг 2: Преобразование данных (строчные буквы в email и т.д.)
foreach ($sourceData as &$row) {
$row['email'] = strtolower($row['email']);
}
// Шаг 3: Очистка существующих данных в целевой таблице
$targetDB->exec("DELETE FROM chapter_contributor_info");
// Шаг 4: Вставка данных пакетами
$batchSize = 50; // Настройте размер пакета по мере необходимости
$dataCount = count($sourceData);
$targetDB->beginTransaction(); // Начать транзакцию для повышения производительности
$insertedCount = 0; // Счетчик успешно вставленных строк
$failedInserts = []; // Массив для хранения неудачных вставок
for ($i = 0; $i < $dataCount; $i += $batchSize) {
// Избежание повторения последней строки в пакете
$batchData = array_slice($sourceData, $i, $batchSize);
// Обеспечение отсутствия повторения в последней строке
if ($i + $batchSize > $dataCount) {
$batchData = array_unique($batchData, SORT_REGULAR);
}
// Формирование запроса для пакетной вставки
$values = [];
$params = [];
foreach ($batchData as $row) {
$values[] = "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
$params = array_merge($params, [
$row['bookcode'], $row['chapter_doi'], $row['chapter'], $row['chapter_title'],
$row['firstname'], $row['lastname'], $row['contributor_type'], $row['email'],
$row['affiliation'], $row['country'], $row['bookxml_year']
]);
}
// Подготовка запроса для пакетной вставки
$sql = "INSERT INTO chapter_contributor_info
(bookcode, chapter_doi, chapter, chapter_title, firstname, lastname, contributor_type, email, affiliation, country, bookxml_year)
VALUES " . implode(", ", $values);
try {
$stmt = $targetDB->prepare($sql);
$stmt->execute($params);
$insertedCount += $stmt->rowCount(); // Подсчет вставленных строк
} catch (PDOException $e) {
// Запись сообщения об ошибке и проблемных данных
$failedInserts[] = [
'data' => $batchData,
'error' => $e->getMessage()
];
}
}
$targetDB->commit(); // Завершение транзакции
echo "Данные успешно извлечены, преобразованы и вставлены в chapter_contributor_info в пакетах. Всего вставленных строк: $insertedCount.";
// Запись неудачных вставок
if (!empty($failedInserts)) {
file_put_contents('failed_inserts.log', print_r($failedInserts, true), FILE_APPEND);
echo "Некоторые строки не удалось вставить. Проверьте failed_inserts.log для получения подробностей.";
}
} catch (PDOException $e) {
// Откат транзакции в случае ошибки
if ($targetDB->inTransaction()) {
$targetDB->rollBack();
}
echo "Ошибка: " . $e->getMessage();
}
// Закрытие соединений
$sourceDB = null;
$targetDB = null;
Ответ или решение
Проблема, с которой вы сталкиваетесь, связана с тем, как вы обрабатываете вставку данных при выполнении пакетного добавления в вашу целевую базу данных в PHP. Вместо того чтобы корректно вставлять последнюю строку, повторяется предпоследняя. Эта ошибка может возникнуть по нескольким причинам, и я предложу вам возможные решения для её устранения.
Возможные причины проблемы:
-
Некорректное управление пакетами: В вашем коде есть условие, которое нацелено на избегание повтора последней строки. Однако оно реализовано некорректно и может приводить к неожиданному поведению во время сборки массива
batchData
. -
Логика при формировании SQL запроса: Вы используете
array_unique
в условии, которое срабатывает только для последнего пакета. Это может привести к потере линейности данных, когда последняя запись не считается уникальной.
Решение:
-
Удаление условия проверки на уникальность: Вместо применения
array_unique
, просто позвольте PHP собрать текущий массив на основе индексации. -
Корректный расчет индексирования при пакетной вставке: Убедитесь, что размер последнего батча обрабатывается правильно. Например, вы можете доработать цикл, чтобы он продолжал работать до
$dataCount
и корректно обрабатывал последний пакет данных. -
Изменение методов обработки ошибок: Убедитесь, что любая ошибка, возникающая во время вставки, правильно обрабатывается и фиксируется, чтобы вы могли выяснить, в каком пакете дубликаты.
Вот переработанный фрагмент вашего кода с учетом этих замечаний:
try {
// Подключение к исходной базе данных
$sourceDB = new PDO("mysql:host=$hostSource;dbname=$dbSource", $userSource, $passSource);
$sourceDB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Подключение к целевой базе данных
$targetDB = new PDO("mysql:host=$hostDest;dbname=$dbDest", $userDest, $passDest);
$targetDB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Шаг 1: Извлечение данных из исходной базы данных
$query = "SELECT * FROM chapter_contributor_info";
$sourceData = $sourceDB->query($query)->fetchAll(PDO::FETCH_ASSOC);
// Шаг 2: Трансформация данных (например, перевод email в нижний регистр)
foreach ($sourceData as &$row) {
$row['email'] = strtolower($row['email']);
}
// Шаг 3: Очистка существующих данных в целевой таблице
$targetDB->exec("DELETE FROM chapter_contributor_info");
// Шаг 4: Вставка данных пакетами
$batchSize = 50; // Настройте размер партии по необходимости
$dataCount = count($sourceData);
$targetDB->beginTransaction(); // Начало транзакции для повышения производительности
$insertedCount = 0; // Счетчик успешно вставленных строк
$failedInserts = []; // Массив для хранения неудачных вставок
for ($i = 0; $i < $dataCount; $i += $batchSize) {
$batchData = array_slice($sourceData, $i, $batchSize); // Формирование пакета данных
// Построение запроса для пакетной вставки
$values = [];
$params = [];
foreach ($batchData as $row) {
$values[] = "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
$params = array_merge($params, [
$row['bookcode'], $row['chapter_doi'], $row['chapter'], $row['chapter_title'],
$row['firstname'], $row['lastname'], $row['contributor_type'], $row['email'],
$row['affiliation'], $row['country'], $row['bookxml_year']
]);
}
// Подготовка запроса на пакетное добавление
$sql = "INSERT INTO chapter_contributor_info
(bookcode, chapter_doi, chapter, chapter_title, firstname, lastname, contributor_type, email, affiliation, country, bookxml_year)
VALUES " . implode(", ", $values);
try {
$stmt = $targetDB->prepare($sql);
$stmt->execute($params);
$insertedCount += $stmt->rowCount(); // Учет вставленных строк
} catch (PDOException $e) {
// Фиксация ошибки и проблемных данных
$failedInserts[] = [
'data' => $batchData,
'error' => $e->getMessage()
];
}
}
$targetDB->commit(); // Фиксация транзакции
echo "Данные успешно извлечены, трансформированы и вставлены в chapter_contributor_info пакетами. Всего вставленных строк: $insertedCount.";
// Фиксация неудачных вставок
if (!empty($failedInserts)) {
file_put_contents('failed_inserts.log', print_r($failedInserts, true), FILE_APPEND);
echo "Некоторые строки не удалось вставить. Проверьте 'failed_inserts.log' для подробностей.";
}
} catch (PDOException $e) {
// Откат транзакции в случае ошибки
if ($targetDB->inTransaction()) {
$targetDB->rollBack();
}
echo "Ошибка: " . $e->getMessage();
}
// Закрытие соединений
$sourceDB = null;
$targetDB = null;
Объяснение кода:
- Мы убрали условия для
array_unique
, так как они не необходимы. - Мы продолжаем вставлять данные пакетом, хорошо контролируя начало и конец каждого пакета.
- Логируем ошибки для дальнейшей отладки.
Эти изменения должны помочь вам успешно вставить все данные, включая последнюю строку, и избежать дублирования. Не забывайте проверять журнал ошибок для выявления возможных проблем.