批量分配问题

时间:2015-12-10 20:14:03

标签: php laravel eloquent laravel-5.1

我有一个包含很多字段的表单,每个字段都必须作为不同的行添加到表中。

我的表格如下:

| Category | Device |       Value |
|----------|:-------|-------------|
| 2        |    1   |  some value |
| 3        |    1   | other value |
| 7        |    3   |         etc |

类别和设备实际上是CategoriesDevices表中的外键。它们也应该是唯一的,这意味着不能有Category: 2Device: 1两次。如果它们已存在,则应更新该值。

从表单中检索类别和值,它们如下所示:

{"2":"some value","3":"other value","5":"etc","6":"something","8":"can be empty"}

该设备也来自表格,但它将是相同的。

现在我需要输入数据库中的所有内容,我正在寻找一个简单的解决方案。

但它会做大约100个查询(每个输入一个),我相信必须有更好的解决方案。

如果来自表单的其中一个值为空,则应忽略它。

这是我目前正在使用的代码,也许您可​​以更好地理解:

public function postSpecs(Request $request)
    {
        $specs = $request->except(['_token', 'deviceid']);

        foreach($specs as $key=>$val)
        {
            if($val == '') continue;
            if(Spec::where('category', $key)->where('device', $request->deviceid)->exists())
            {
                $spec = Spec::where('category', $key)->where('device', $request->deviceid)->first();
                $spec->value = $val;
                $spec->save();
            }
            else 
            {
                $spec = new Spec;
                $spec->category = $key;
                $spec->device = $request->deviceid;
                $spec->value = $val;
                $spec->save();
            }
        }
    }

2 个答案:

答案 0 :(得分:6)

使用插入方法,如:

$model->insert([
    ['email' => 'taylor@example.com', 'votes' => 0],
    ['email' => 'dayle@example.com', 'votes' => 0]
]);

另请参阅:http://laravel.com/docs/5.1/queries#inserts

编辑:

已更新为您的代码:

public function postSpecs(Request $request)
{
    $specs = $request->except(['_token', 'deviceid']);

    $data = array();
    foreach($specs as $key=>$val)
    {
        if($val == '') continue;
        if(Spec::where('category', $key)->where('device', $request->deviceid)->exists())
        {
            $spec = Spec::where('category', $key)->where('device', $request->deviceid)->first();
            $spec->value = $val;
            $spec->save();
        }
        else
        {
            $data[]['category'] = $key;
            $data[]['device'] = $request->deviceid;
            $data[]['value'] = $val;
        }
    }

    Spec::insert($data);
}

虽然这并不完美,但它可以为您节省大量查询。否则你必须使用原始查询(未经测试!):

INSERT INTO spec (id,category,device,value) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE id=LAST_INSERTED_ID(id)

答案 1 :(得分:1)

关于on duplicate key update方法的更多信息,因为我觉得它应该是一个可能的解决方案,如果这种情况会发生很多......

首先,您需要创建唯一键。这将强制这些列是唯一的。无论您如何处理插入物,我强烈建议添加唯一键。它可能有助于在将来保持你的理智。

在迁移中,你会做...

$table->unique(['Category', 'Device']);

或者在普通的sql ...

alter table category_device add unique index unique_category_device (Category, Device);

现在要插入表格,您只需使用查询...

insert into category_device (Category, Device, Value) values ($category_id, $device_id, $value)
    on duplicate key udpate Value = VALUES(Value)

如果它不是重复条目,mysql将只插入新记录。如果它是重复的,mysql将使用您尝试插入的任何内容更新value列作为新值。这是非常友好的,因为在尝试执行插入操作之前,不需要检查是否存在重复项,因为如果存在重复,它将像一个更新语句一样简单。这里的缺点是laravel不支持on duplicate key update所以你需要实际编写SQL。我建议你仍然使用数据绑定,虽然这可以使它更容易。

$sql = "insert into category_device (`Category`, `Device`, `Value`) values (:category_id, :device_id, :value)
            on duplicate key udpate Value = VALUES(Value)";

$success = \DB::insert($sql, [
    'category_id' => $category_id,
    'device_id' => $device_id,
    'value' => $value
]);

另请注意,如果您有超过100个条目,您可以遍历它们并继续添加查询,它应该工作相同......

insert into category_device (`Category`, `Device`, `Value`) 
    values 
(1, 1, 'some value'), (1, 2, 'some other value'), (1, 3, 'third value')...
    on duplicate key udpate Value = VALUES(Value)";

如果您使用此方法,我可能不会担心数据绑定,只需将值插入查询即可。请确保事先正确转义。

如果要插入一百个项目,这将是200个查询(一个用于检查记录的存在,另一个用于插入/更新),这会将200个查询转换为1个查询,这显然是一个巨大的查询绩效提升。