我有一个整数数组,需要作为一批行插入。每行都需要一些其他数据。
$ids = [1,2]
$thing = 1
$now = Carbon::now(); // This is just a timestamp.
$binding_values = trim(str_repeat("({$thing}, ?, '{$now}'),", count($ids)), ',');
字符串$binding_values
看起来像这样:
“(1, ?, '2019-01-01 00:00:00'), (1, ?, '2019-01-01 00:00:00')”
然后我准备查询字符串并将参数绑定(bind)到它。使用 IGNORE
是因为我在表上有一个复合唯一索引。不过,它似乎与问题无关,因此我省略了详细信息。
DB::insert("
INSERT IGNORE INTO table (thing, id, created_at)
VALUES {$binding_values}
", $ids);
这几乎一直有效,但时不时地我会收到错误SQLSTATE[HY000]:一般错误:2031
。
我执行此参数的方式是否与 Laravel 绑定(bind)了某种反模式?此错误的根源可能是什么?
因为该方法不存在注入(inject)风险,并且该方法不可能扩展到有注入(inject)风险的用例,所以我对其进行了修改以烘焙所有参数并跳过参数绑定(bind)。到目前为止我还没有看到任何错误。
我仍然想知道是什么可能导致此行为,以便我将来可以更好地管理它。如果您有任何见解,我将不胜感激。
最佳答案
除了将参数烘焙到查询中之外,我没有发现您的查询有什么大问题,这很容易受到 SQL 注入(inject)的影响。
据我所知,您的问题是您需要 INSERT ÌGNORE INTO
,Laravel 不支持开箱即用。您是否考虑过使用像staudenmeir/laravel-upsert这样的第三方包?
另一种方法是将查询包装在事务中并首先选择现有条目,这样您就有机会不再插入它们:
$ids = [1, 2];
$thing = 1;
$time = now();
DB::transaction(function () use ($ids, $thing, $time) {
$existing = DB::table('some_table')->whereIn('id', $ids)->pluck('id')->toArray();
$toInsert = array_diff($ids, $existing);
$dataToInsert = array_map(function ($id) use ($thing, $time) {
return [
'id' => $id,
'thing' => $thing,
'created_at' => $time
];
}, $toInsert);
DB::table('some_table')->insert($dataToInsert);
});
这样,您将只插入尚不存在的内容,但您也将保留在查询生成器的框架功能内。唯一的缺点是,由于第二次往返数据库,速度会稍微慢一些。
关于mysql - Laravel 参数绑定(bind)导致偶尔出现 MySQL 一般错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57542386/