0

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

$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')”

然后我准备我的查询字符串并将参数绑定到它。之所以使用,IGNORE是因为我在表上有一个复合唯一索引。不过,这似乎与问题无关,所以我省略了细节。

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

几乎一直有效,但时不时地我得到一个错误SQLSTATE[HY000]: General error: 2031

我做这个参数的方式是否与 Laravel 绑定了某种反模式?这个错误的根源可能是什么?

编辑: 因为在此方法中没有注入风险,并且此方法不可能扩展到具有注入风险的用例,所以我对其进行了修改以烘烤所有参数并跳过参数绑定。到目前为止,我还没有看到任何错误。

我仍然想知道可能导致这种行为的原因,以便将来更好地管理它。我会很感激任何见解。

4

1 回答 1

0

除了将参数烘焙到查询中之外,我认为您的查询没有什么大问题,这很容易受到 SQL 注入的影响。

据我所知,您的问题是INSERT ÌGNORE INTOLaravel 不支持开箱即用。您是否考虑过使用像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);
});

这样,您将只插入尚未出现的内容,但您也将保持在查询构建器的框架功能内。唯一的缺点是由于第二次往返数据库,它会稍微慢一些。

于 2019-08-18T12:19:19.330 回答