MySQL에서 50K+ 레코드 가져오기에 일반 오류 발생: 1390 준비됨 문에 자리 표시자가 너무 많습니다.
다음 오류를 발견한 사람이 있습니까: 일반 오류: 1390 준비된 문에 자리 표시자가 너무 많습니다.
방금 SequetPro를 통해 50,000개가 넘는 레코드를 가져왔는데, 이제 이 레코드를 보기 위해 이동하면(Laravel 4) 일반 오류가 발생합니다. 1390 Prepared 문에 자리 표시자가 너무 많습니다.
AdminNotesController.php 파일의 아래 index() 메서드는 쿼리를 생성하고 보기를 렌더링하는 것입니다.
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
));
}
어떤 조언이든 감사하겠습니다.감사해요.
다음을 사용하여 이 문제를 해결했습니다.array_chunk
기능.
다음은 솔루션입니다.
foreach (array_chunk($data,1000) as $t)
{
DB::table('table_name')->insert($t);
}
MySQL 5.5와 동일한 동작을 하는 MariaDB 5.5에는 65,535(2^16-1) 자리 표시자가 제한되어 있습니다.
관련성이 있는지 확실하지 않습니다. MySQLi/MySQLND를 사용하여 PHP 5.5.12에서 테스트했습니다.
이 오류는 다음 조건이 모두 충족되는 경우에만 발생합니다.
- MySQL 클라이언트 라이브러리(libmysqlclient)가 아닌 MySQL Native Driver(mysqlnd)를 사용하고 있습니다.
- 준비를 에뮬레이트하고 있지 않습니다.
이러한 요인 중 하나를 변경해도 이 오류는 발생하지 않습니다.그러나 성능 또는 보안 문제에 대해 이 두 가지를 모두 수행하는 것이 좋습니다. 따라서 이 솔루션은 일회성 또는 일시적인 문제 이외에는 권장하지 않습니다.이 오류가 발생하지 않도록 다음과 같이 간단하게 수정할 수 있습니다.
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
제가 생각하기에 "분해자"는 플레이스홀더가 제한적이라는 것에 대해 옳습니다.레코드당 하나의 쿼리를 실행하지 않습니다.
열을 하나 더 추가하기 전까지는 정상적으로 작동한 쿼리가 있고 72k개의 자리 표시자가 있는데 이 오류가 발생합니다.그러나 72k는 8개의 열이 있는 9,000개의 행으로 구성됩니다.이 쿼리 1 레코드를 한 번에 실행하려면 며칠이 걸릴 수 있습니다. (AdWords 데이터를 DB로 가져오려고 하는데 한 번에 1 레코드를 실행하면 말 그대로 하루 분량의 데이터를 가져오는 데 24시간 이상이 걸릴 것입니다.저는 그것을 먼저 시도했습니다.
제가 추천하고 싶은 것은 일종의 해킹입니다.먼저 허용할 최대 자리 표시자 수(즉, 60k가 안전한지 여부)를 동적으로 결정합니다.이 숫자를 사용하여 열 수를 기준으로 한 번에 가져오거나 반환할 수 있는 전체 레코드 수를 결정합니다.쿼리할 데이터의 전체 배열을 만듭니다.array_chunk 및 for 각 루프를 사용하여 최소 수의 쿼리에서 원하는 모든 항목을 가져옵니다.다음과 같이:
$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.
Laravel 모델을 사용하여 sqlite 데이터베이스에서 mysql 데이터베이스로 11000개의 모든 레코드를 몇 초 안에 복사합니다.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();
}
array_chunk 함수를 사용하여 다음과 같이 수행할 수 있습니다.
foreach(array_chunk($data, 1000) as $key => $smallerArray) {
foreach ($smallerArray as $index => $value) {
$temp[$index] = $value
}
DB::table('table_name')->insert(temp);
}
위 문제에 대한 내 해결책:제 쪽에서는 이 오류가 발생했을 때 대량 삽입 청크 크기를 1000에서 800으로 줄여 수정했고 저에게 효과가 있었습니다.실제로 내 표에는 필드가 너무 많았고 대부분은 전체 페이지 텍스트처럼 크기에 대한 자세한 설명을 포함하고 있습니다.대량 삽입을 위해 이동했을 때 서비스가 충돌을 일으켰고 위의 오류를 통과했습니다.
플레이스홀더의 수는 쿼리당 65536개로 제한되어 있다고 생각합니다(적어도 이전 mysql 버전에서는).
이 코드 조각이 무엇을 생성하는지 정말 식별할 수 없습니다.하지만 거대한 질문이라면 문제가 있습니다.
레코드당 하나의 쿼리를 생성하여 해당 쿼리를 가져와 트랜잭션에 넣어야 합니다.
언급URL : https://stackoverflow.com/questions/18100782/import-of-50k-records-in-mysql-gives-general-error-1390-prepared-statement-con
'programing' 카테고리의 다른 글
파이썬에서 가져온 모듈의 main()을 호출할 수 있습니까? (0) | 2023.08.06 |
---|---|
마크다운 텍스트와 재구성된 텍스트 모두에서 동일한 README 사용 (0) | 2023.08.06 |
유니코드 문자를 CSS 콘텐츠 값에 배치 (0) | 2023.08.06 |
Oracle 12에 SQL의 로컬 수집 유형에 문제가 있습니까? (0) | 2023.08.01 |
jQuery를 사용하여 .prop을 변경해도 .change 이벤트가 트리거되지 않습니다. (0) | 2023.08.01 |