Импорт записей 50K + в MySQL дает общую ошибку: 1390 Подготовленный оператор содержит слишком много заполнителей

Кто-нибудь сталкивался с этой ошибкой: Общая ошибка: 1390 Подготовленный оператор содержит слишком много заполнителей

Я только что сделал импорт через SequelPro из более чем 50 000 записей, и теперь, когда я перехожу к просмотру этих записей в моем представлении (Laravel 4), я получаю общую ошибку: 1390 Подготовленный оператор содержит слишком много заполнителей.

Метод index() в моем файле AdminNotesController.php является тем, что генерирует запрос и отображает представление.

public function index()
{
    $created_at_value = Input::get('created_at_value');
    $note_types_value = Input::get('note_types_value');
    $contact_names_value = Input::get('contact_names_value');
    $user_names_value = Input::get('user_names_value');
    $account_managers_value = Input::get('account_managers_value');

    if (is_null($created_at_value)) $created_at_value = DB::table('notes')->lists('created_at');
    if (is_null($note_types_value)) $note_types_value = DB::table('note_types')->lists('type');
    if (is_null($contact_names_value)) $contact_names_value = DB::table('contacts')->select(DB::raw('CONCAT(first_name," ",last_name) as cname'))->lists('cname');
    if (is_null($user_names_value)) $user_names_value = DB::table('users')->select(DB::raw('CONCAT(first_name," ",last_name) as uname'))->lists('uname');

    // In the view, there is a dropdown box, that allows the user to select the amount of records to show per page. Retrieve that value or set a default.
    $perPage = Input::get('perPage', 10);

    // This code retrieves the order from the session that has been selected by the user by clicking on a table column title. The value is placed in the session via the getOrder() method and is used later in the Eloquent query and joins.
    $order = Session::get('account.order', 'company_name.asc');
    $order = explode('.', $order);

    $notes_query = Note::leftJoin('note_types', 'note_types.id', '=', 'notes.note_type_id')
        ->leftJoin('users', 'users.id', '=', 'notes.user_id')
        ->leftJoin('contacts', 'contacts.id', '=', 'notes.contact_id')
        ->orderBy($order[0], $order[1])
        ->select(array('notes.*', DB::raw('notes.id as nid')));

    if (!empty($created_at_value)) $notes_query = $notes_query->whereIn('notes.created_at', $created_at_value);

    $notes = $notes_query->whereIn('note_types.type', $note_types_value)
        ->whereIn(DB::raw('CONCAT(contacts.first_name," ",contacts.last_name)'), $contact_names_value)
        ->whereIn(DB::raw('CONCAT(users.first_name," ",users.last_name)'), $user_names_value)
        ->paginate($perPage)->appends(array('created_at_value' => Input::get('created_at_value'), 'note_types_value' => Input::get('note_types_value'), 'contact_names_value' => Input::get('contact_names_value'), 'user_names_value' => Input::get('user_names_value')));

    $notes_trash = Note::onlyTrashed()
        ->leftJoin('note_types', 'note_types.id', '=', 'notes.note_type_id')
        ->leftJoin('users', 'users.id', '=', 'notes.user_id')
        ->leftJoin('contacts', 'contacts.id', '=', 'notes.contact_id')
        ->orderBy($order[0], $order[1])
        ->select(array('notes.*', DB::raw('notes.id as nid')))
        ->get();

    $this->layout->content = View::make('admin.notes.index', array(
        'notes'             => $notes,
        'created_at'        => DB::table('notes')->lists('created_at', 'created_at'),
        'note_types'        => DB::table('note_types')->lists('type', 'type'),
        'contacts'          => DB::table('contacts')->select(DB::raw('CONCAT(first_name," ",last_name) as cname'))->lists('cname', 'cname'),
        'accounts'          => Account::lists('company_name', 'company_name'),
        'users'             => DB::table('users')->select(DB::raw('CONCAT(first_name," ",last_name) as uname'))->lists('uname', 'uname'),
        'notes_trash'       => $notes_trash,
        'perPage'           => $perPage
    ));
}

Любые советы будут оценены. Спасибо.

Ответ 1

В MariaDB 5.5 есть лимит 65,535 (2 ^ 16-1), который должен иметь идентичное поведение как MySQL 5.5.

Не уверен, если это уместно, я тестировал его на PHP 5.5.12 с использованием MySQLi/MySQLND.

Ответ 2

В то время как я думаю, что The Disintegrator правильно относится к тому, чтобы заполнители были ограничены. Я бы не запускал 1 запрос на запись.

У меня есть запрос, который работал нормально, пока я не добавил еще один столбец, и теперь у меня есть 72k-заполнители, и я получаю эту ошибку. Однако, что 72k состоит из 9000 строк с 8 столбцами. Запуск этого запроса по 1 записи за один раз займет несколько дней. (Я пытаюсь импортировать данные AdWords в БД и буквально занимает более 24 часов, чтобы импортировать данные за день, если я сделал это по 1 записи за раз. Я пробовал это в первую очередь.)

Я бы порекомендовал что-то вроде взлома. Сначала либо динамически определите максимальное количество заполнителей, которые вы хотите разрешить, - то есть 60k - в безопасности. Используйте этот номер, чтобы определить, на основе количества столбцов, количества полных записей, которые вы можете импортировать/вернуть сразу. Создайте полный массив данных для запроса. Используйте array_chunk и цикл foreach, чтобы захватить все, что вы хотите, в минимальном количестве запросов. Вот так:

$maxRecords = 1000;
$sql = 'SELECT * FROM ...';
$qMarks = array_fill(0, $maxInsert, '(?, ...)');
$tmp = $sql . $implode(', ', $qMarks);
foreach (array_chunk($data, $maxRecords) AS $junk=>$dataArray) {
  if (count($dataArray) < $maxRecords)) { break; }

  // Do your PDO stuff here using $tmp as you SQL statement with all those placeholders - the ?s
}

// Now insert all the leftovers with basically the same code as above except accounting for
// the fact that you have fewer than $maxRecords now.

Ответ 3

Эта ошибка возникает только при выполнении обоих следующих условий:

  • Вы используете MySQL Native Driver (mysqlnd), а не клиентскую библиотеку MySQL (libmysqlclient)
  • Подготавливается не эмуляция.

Если вы измените один из этих факторов, эта ошибка не произойдет. Однако имейте в виду, что выполнение обоих из них рекомендуется либо для повышения производительности, либо для обеспечения безопасности, поэтому я бы не рекомендовал это решение ни для чего, кроме более чем одной временной или временной проблемы, с которой вы сталкиваетесь. Чтобы предотвратить эту ошибку, исправление выполняется так же просто, как:

$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

Ответ 4

Решил эту проблему с помощью функции array_chunk.

Вот решение ниже:

foreach (array_chunk($data,1000) as $t)  
{
     DB::table('table_name')->insert($t); 
}

Ответ 5

Используя модель Laravel, скопируйте все 11000 записей из базы данных sqlite в базу данных mysql за несколько секунд. Блок данных массива до 500 записей:

public function handle(): void
{
    $smodel = new Src_model();
    $smodel->setTable($this->argument('fromtable'));
    $smodel->setConnection('default'); // sqlite database
    $src = $smodel::all()->toArray();

    $dmodel = new Dst_model();
    $dmodel->setTable($this->argument('totable'));
    $dmodel->timestamps = false;
    $stack = $dmodel->getFields();
    $fields = array_shift($stack);

    $condb = DB::connection('mysql');
    $condb->beginTransaction();

    $dmodel::query()->truncate();
    $dmodel->fillable($stack);
    $srcarr=array_chunk($src,500);
    $isOK=true;
    foreach($srcarr as $item) {
        if (!$dmodel->query()->insert($item)) $isOK=false;
    }
    if ($isOK) {
        $this->notify("Przenieśliśmy tabelę z tabeli : {$this->argument('fromtable')} do tabeli: {$this->argument('totable')}", 'Będzie świeża jak nigdy!');
        $condb->commit();
    }
    else $condb->rollBack();

}

Ответ 6

Мое исправление для вышеуказанной проблемы: со своей стороны, когда я получил эту ошибку, я исправил ее, уменьшив размер фрагмента массовой вставки с 1000 до 800, и это сработало для меня. На самом деле в моей таблице было слишком много полей, и большинство из них содержит подробные описания размера, как полный текст страницы. когда я иду туда массовую вставку службы вызвал сбой и из-за вышеуказанной ошибки.

Ответ 7

Я думаю, что число заполнителей ограничено 65536 на запрос (по крайней мере, в более старых версиях mysql).

Я действительно не могу понять, что генерирует этот кусок кода. Но если это гигантский запрос, там ваша проблема.

Вы должны сгенерировать один запрос на запись для импорта и поместить их в транзакцию.