小心 Laravel update 数据的坑

通常我们更新数据有以下几种方法:

方法 1

$model = Model::find($id);
$model->field1 = $value1;
$model->field2 = $value2;
$model->save();

方法 2

Model::find($id)
    ->update([
        'field1' => $value1,
        'field2' => $value2,
    ]);

方法 3

Model::query()
    ->where('id', $id)
    ->update([
        'field1' => $value1,
        'field2' => $value2,
    ]);

三种方法都可以更新数据,如果是你,你会选择哪种方法?或者说哪个方法是性能最好的?


以下为更新内容,揭晓答案:

肯定不是标题党哦,本来是想个大家一个选择的过程,一起讨论。
答案是选择方法3,方法3 仅生成一条 SQL 如下:

update table set field1=value1, field2=value2 where id = $id;

方法1,方法2,都是形成两条SQL,会多一次查询语句:

select * from table where id = $id limit 1;
update table set field1=value1, field2=value2 where id = $id;

@Imuyu 提到 find 可能返回空,也是个值得注意的地方,直接update 没有 id 也就没有更新内容了,也会减少一次判断。

这里主要想分享下,多去关注 Elequent 的具体SQL内容,用的时候很爽,但如果没搞清楚底层实现,也会造成很多优化的麻烦。

不住念,无名无相
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 21
panda-sir

标题党?

3年前 评论
adays

说好的坑呢? 正好过来假装准备来填坑来着

3年前 评论

可能就是find会返回空吧!

3年前 评论
nfangxu

方法三不会触发相关的更新事件 如果有用到的话 找都没法找 不可取 方法一/二 虽然会多一次查询 但是也不会影响太多 性能上是可以接收的

3年前 评论
扣丁禅师 (楼主) 3年前

方案一中的 find 并不仅仅是为了更新数据用的,实际的开发过程中,会有逻辑判断,然后再决定是否需要更新

3年前 评论

这个,,,也是坑吗,,,前面两种,先查询出模型,那肯定会有一条 select,,,

3年前 评论

@largezhou 如果你直接用方法3,那就当我没说。如果你用了方法2,那就值得思考下了。

抛开Observe不谈,单纯更新数据,我想应该大多数人都是方法2的写法吧。

3年前 评论
largezhou 3年前

真没这么用过方法2。。。

3年前 评论

方法1和方法3用的多

3年前 评论

有点不同见解,我最常用的是方法1。

先说方法3,Model::query()->where() 其实这个和 Model::where() 是等价的,可省略,这个其实也没啥好说的,文档也说了Eloquent ORM支持所有数据库方法,从某些方面来说算是"最优"选项。

方法1和方法2其实是一样的,就是写法不同,是遵循ORM思想的,操作数据就是操作对象,所以那个查询在数据库方面来说是没必要的,但是对于ORM思想来说是有必要的,而且有一点和楼主说的不同,方法1和方法2在保存的时候会对比程序中查询的数据库的值来就行对比,如果要修改的值与对象的值相同则不执行update数据表的操作,而且对象还有一个好处,是默认引用的,你把对象传入其他的方法修改之后在原位置数据也会改变(此处我可能表述有误,欢迎有更了解底层原理的人来指正)。

所以方法1和方法2执行的SQL是有可能只有SELECT而没有UPDATE的。

以前经常会听到前辈们优化数据库的一个办法是先查询后修改,至于为什么从没有深入探究,恰巧上几天在社区看到一篇文章《浅入浅出MySQL》表锁 行锁 并发插入,恍然大悟,是因为写锁和读锁的优先级问题,所以我觉得ORM这种先查询后修改的方式对于高频率修改的数据库来说对程序更加健壮,至于那个查询引发的效率问题,应该可以忽略不计了。


最后,方法1和方法2确实有坑,可参考博客:Laravel 修改数据与原数据一样 导致没有执行sql,就是用了ORM后,又通过数据库方法修改数据库,导致ORM内的数据和数据表的数据不一致导致更新丢失字段。

3年前 评论

方法2非链式操作还是经常用的

$post = Model::find($id);
$post->update([
        'field1' => $value1,
        'field2' => $value2,
    ]);
3年前 评论
da_house

如果有需要抛事件的时候 会选择方法1 ,不需要抛事件的时候 选择方法3

3年前 评论

这个要看具体使用地方了

3年前 评论

今天update用的方法三,但是采用了如下写法导致where子句没有生效,把我整表更新了,无语

Model::query()
    ->update([
        'field1' => $value1,
        'field2' => $value2,
    ])
    ->where('id', $id);
1年前 评论
扣丁禅师 (楼主) 1年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!