При выполнении пакетной вставки в PHP вместо последней строки дублируется предпоследняя строка, и фактическая последняя строка не вставляется.

Вопрос или проблема

Я выполняю пакетную вставку в коде ниже, извлекая данные из одной базы данных и вставляя их в другой источник. Данные вставляются полностью, за исключением последней строки. Вместо этого дублируется предпоследняя строка.

Журнал ошибок также показывает фактическое количество строк данных.

   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. Вместо того чтобы корректно вставлять последнюю строку, повторяется предпоследняя. Эта ошибка может возникнуть по нескольким причинам, и я предложу вам возможные решения для её устранения.

Возможные причины проблемы:

  1. Некорректное управление пакетами: В вашем коде есть условие, которое нацелено на избегание повтора последней строки. Однако оно реализовано некорректно и может приводить к неожиданному поведению во время сборки массива batchData.

  2. Логика при формировании SQL запроса: Вы используете array_unique в условии, которое срабатывает только для последнего пакета. Это может привести к потере линейности данных, когда последняя запись не считается уникальной.

Решение:

  1. Удаление условия проверки на уникальность: Вместо применения array_unique, просто позвольте PHP собрать текущий массив на основе индексации.

  2. Корректный расчет индексирования при пакетной вставке: Убедитесь, что размер последнего батча обрабатывается правильно. Например, вы можете доработать цикл, чтобы он продолжал работать до $dataCount и корректно обрабатывал последний пакет данных.

  3. Изменение методов обработки ошибок: Убедитесь, что любая ошибка, возникающая во время вставки, правильно обрабатывается и фиксируется, чтобы вы могли выяснить, в каком пакете дубликаты.

Вот переработанный фрагмент вашего кода с учетом этих замечаний:

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, так как они не необходимы.
  • Мы продолжаем вставлять данные пакетом, хорошо контролируя начало и конец каждого пакета.
  • Логируем ошибки для дальнейшей отладки.

Эти изменения должны помочь вам успешно вставить все данные, включая последнюю строку, и избежать дублирования. Не забывайте проверять журнал ошибок для выявления возможных проблем.

Оцените материал
Добавить комментарий

Капча загружается...