有没有人遇到过这个错误:常规错误:1390准备好的语句包含太多占位符
我刚刚通过SequelPro导入超过50,000条记录,现在当我在我的视图中查看这些记录时(Laravel 4)我得到一般错误:1390准备好的语句包含太多占位符。
我的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
));
}
任何建议都将不胜感激。感谢。
答案 0 :(得分:12)
在 MariaDB 5.5 中, 65,535 (2 ^ 16-1)占位符有限制,应该与具有相同的行为> MySQL 5.5 。
不确定是否相关,我使用MySQLi / MySQLND在PHP 5.5.12上进行了测试。
答案 1 :(得分:8)
虽然我认为@The Disintegrator对于占位符有限是正确的。我不会为每条记录运行1个查询。
我有一个查询工作正常,直到我添加了一个列,现在我有72k占位符,我得到这个错误。但是,72k由9000行和8列组成。一次运行此查询1条记录需要数天时间。 (我正在尝试将AdWords数据导入到数据库中,如果我一次只创建1条记录,那么导入数天的数据会花费超过24小时。我先尝试过。)
我建议的是黑客攻击。首先要么动态确定你想要允许的最大占位符数 - 即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.
答案 2 :(得分:8)
只有满足以下条件的 时,才会发生此错误 :
如果更改这些因素中的任何一个,则不会发生此错误。但是请记住,建议不要出于性能或安全问题而执行这两项操作,因此我不建议您使用此解决方案,只需要更多的一次性或临时性问题。为防止发生此错误,修复程序非常简单:
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
答案 3 :(得分:1)
您可以使用array_chunk函数来做到这一点,像这样:
foreach(array_chunk($data, 1000) as $key => $smallerArray) {
foreach ($smallerArray as $index => $value) {
$temp[$index] = $value
}
DB::table('table_name')->insert(temp);
}
答案 4 :(得分:0)
使用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();
}
答案 5 :(得分:0)
上述问题的我的解决方法: 就我而言,当我遇到此错误时,我通过将批量插入块的大小从1000减小到800来解决了这个问题,它对我来说很有效。 实际上,我的表中有太多字段,并且大多数字段包含详细的大小说明,例如完整的页面文本。当我去那里进行批量插入时,该服务导致崩溃并通过上述错误。
答案 6 :(得分:0)
使用array_chunk函数解决了此问题。
这是下面的解决方案:
foreach (array_chunk($data,1000) as $t)
{
DB::table('table_name')->insert($t);
}
答案 7 :(得分:-2)
我认为占位符的数量限制为每个查询65536(至少在较旧的mysql版本中)。
我真的无法辨别出这段代码产生了什么。但如果这是一个巨大的查询,那就是你的问题。
您应该为每个记录生成一个查询以进行导入并将其放入事务中。