Laravel参数绑定偶尔导致MySQL一般错误

wbrvyc0a  于 2022-12-14  发布在  Mysql
关注(0)|答案(1)|浏览(112)

我有一个整数数组,需要作为一批行插入。每一行都需要一些其他数据。

$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年1月1日00:00:00'),(1,?,'2019年1月1日00:00:00')”
然后我准备查询字符串并将参数绑定到它。使用IGNORE是因为我在表上有一个复合唯一索引。但它似乎与问题无关,所以我省略了细节。

DB::insert("
    INSERT IGNORE INTO table (thing, id, created_at)
    VALUES {$binding_values}
", $ids);

这几乎总是有效的,但是我偶尔会得到一个错误SQLSTATE[HY000]: General error: 2031
我做这个参数的方式是否绑定了某种与Laravel的反模式?这个错误的来源可能是什么?
因为在这个方法中没有注入的风险,而且这个方法也不可能被扩展到有注入风险的用例,所以我修改了它,把所有的参数都烘焙进去,跳过参数绑定。到目前为止,我还没有看到任何错误。
我仍然想知道是什么原因导致了这种行为,这样我就可以在将来更好地处理它。我将感激任何见解。

ct3nt3jp

ct3nt3jp1#

我看不出您的查询有什么大问题,除了将参数烘焙到查询中,这容易受到SQL注入的攻击。
据我所知,您的问题是您需要INSERT ÌGNORE INTO,而Laravel不支持它。您是否考虑过使用staudenmeir/laravel-upsert这样的第三方软件包?
另一种方法是将查询 Package 在事务中,并首先选择现有条目,这样就不必再次插入它们:

$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);
});

这样您只会插入尚未出现的内容,但您仍会保持在查询生成器的框架功能范围内。唯一的缺点是,由于需要第二次往返数据库,因此速度会稍慢。

相关问题