查询构建器 | 数据库操作 | Laravel 8 中文文档


本站和网页 https://laravelacademy.org/post/22012 的作者无关,不对其内容负责。快照谨为网络故障时之索引,不代表被搜索网站的即时页面。

查询构建器 | 数据库操作 | Laravel 8 中文文档
Laravel 学院
文档
Laravel 8.x 中文文档
Laravel 7.x 中文文档
Laravel 6.x 中文文档
Laravel 5.8 中文文档
Laravel 5.7 中文文档
Laravel 5.6 中文文档
Laravel 5.5 中文文档
Laravel 5.4 中文文档
Laravel 5.3 中文文档
Laravel 5.2 中文文档
Laravel 5.1 中文文档
Lumen 中文文档
全栈教程
PHP 全栈工程师指南
PHP 入门到实战
Laravel 入门到精通
Vue.js 入门到实战
玩转 PhpStorm 教程
Laravel 博客入门项目
Laravel 微信小程序项目
Laravel 前后端分离项目
Swoole 入门到实战
Eloquent 性能优化实战
Redis 高性能实战系列
Laravel 新版本特性
PHP 新特性与最佳实践
Golang
Go 入门教程
Go Web 编程
Gin 使用教程
微服务开发
内功修炼
数据结构与算法
网络协议
微服务从入门到实践
高性能 MySQL 实战
高性能 Redis 实战
Laravel 消息队列实战
Laravel 从学徒到工匠
PHP 设计模式系列
名企面试指南
资源库
Laravel 资源大全
Laravel 开源项目
Laravel 扩展包
Laravel 资源下载
更多
博客 & 新闻
问答 & 讨论
Leetcode 题解
学院君读书笔记系列
关于 Laravel 学院
Laravel 互助学习群
Golang 互助学习群
更多
Laravel 中文文档
Laravel 全栈教程
Laravel 学习路径
Go 入门教程
程序员内功修炼
博客
问答
搜索
注册
登录
Info
Content
章节导航
Laravel 8 中文文档
目录索引
序言
3篇文章
新版特性
升级指南
贡献指南
快速入门
5篇文章
安装
配置
目录结构
入门套件
部署应用到生产环境
核心架构
4篇文章
一次请求的生命周期
服务容器
服务提供者
门面
基础组件
13篇文章
路由
中间件
CSRF 防护
控制器
请求
响应
视图
Blade 模板引擎
URL 生成
Session
验证
错误处理
日志
安全系列
6篇文章
用户认证
用户授权
邮箱验证
加密
哈希
重置密码
进阶系列
16篇文章
Artisan 控制台
广播
缓存
集合
通过 Mix 编译前端资源
契约
事件
文件存储
辅助函数
HTTP 客户端
本地化
邮件
通知
扩展包开发
队列
任务调度
数据库操作
6篇文章
数据库入门
查询构建器
分页
迁移
数据填充
Redis
Eloquent ORM
6篇文章
Eloquent 模型入门
关联关系
Eloquent 集合
访问器和修改器
API 资源类
序列化
测试系列
6篇文章
测试入门
HTTP 测试
控制台测试
基于 Laravel Dusk 进行浏览器测试
数据库测试
模拟
官方扩展包
12篇文章
基于 Laravel Cashier 提供订阅支付解决方案
基于 Laravel Envoy 提供远程部署解决方案
基于 Laravel Fortify 提供纯后端用户认证解决方案
基于 Laravel Horizon 提供队列系统解决方案
基于 Laravel Homestead 搭建虚拟化本地集成开发环境
基于 Laravel Passport 提供 OAuth2 认证解决方案
基于 Laravel Sail 搭建 Docker 本地集成开发环境
基于 Laravel Sanctum 提供 SPA 认证解决方案
基于 Laravel Scout 提供全文搜索解决方案
基于 Laravel Socialite 提供第三方登录解决方案
基于 Laravel Telescope 提供本地调试解决方案
基于 Laravel Valet 在 Mac 系统搭建轻量级本地集成开发环境
图书
Laravel 8 中文文档
数据库操作
查询构建器
查询构建器
由 学院君 创建于2年前, 最后更新于 2年前
版本号 #2
20619 views
0 likes
0 collects
简介
数据库查询构建器提供了一个方便的流接口用于创建和执行数据库查询。查询构建器可以用于执行应用中绝大部分数据库操作,并且能够在 Laravel 支持的所有数据库系统上工作。
注:流接口是一种设计模式,更多关于流接口模式的设计和使用方式,可查看这篇教程:PHP 设计模式系列 —— 流接口模式。
Laravel 查询构建器使用 PDO 参数绑定来避免 SQL 注入攻击,不再需要过滤以绑定方式传递的字符串。
注:PDO 不支持绑定字段名称,因此,永远不要允许用户输出字段直接被查询语句引用,包括「order by」字段。如果你一定要让用户选择特定的字段进行操作,则必须要验证字段名称是否在白名单中。
获取结果集
从一张表中取出所有行
我们可以从 DB 门面的 table 方法开始,table 方法为给定表返回一个流式查询构建器实例,该实例允许你在查询上链接多个约束条件并返回最终查询结果。在本例中,我们使用 get 方法获取表中所有记录:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
class UserController extends Controller
/**
* 显示用户列表
* @return Response
*/
public function index()
$users = DB::table('users')->get();
return view('user.index', ['users' => $users]);
get 方法返回包含结果集的 Illuminate\Support\Collection,其中每一个结果都是 PHP 的 StdClass 对象实例:
你可以像访问对象的属性一样访问字段的值:
foreach ($users as $user) {
echo $user->name; // 输出用户名
从一张表中获取一行/一列
如果你只是想要从数据表中获取一行数据,可以使用 first 方法,该方法将会返回单个 StdClass 对象:
$user = DB::table('users')->where('name', '学院君')->first();
echo $user->name;
如果你不需要完整的一行,可以使用 value 方法从结果中获取单个值,该方法会直接返回指定列的值:
$email = DB::table('users')->where('name', '学院君')->value('email');
如果你想要通过 id 字段来获取单条记录,可以使用 find 方法:
$user = DB::table('users')->find(3);
获取字段值列表
如果想要获取包含单个字段值的数组,可以使用 pluck 方法,在本例中,我们获取角色标题数组:
$titles = DB::table('roles')->pluck('title');
foreach ($titles as $title) {
echo $title;
还可以在返回数组中为列值指定自定义键(该自定义键必须是该表的其它字段列名,否则会报错):
$roles = DB::table('roles')->pluck('title', 'name');
foreach ($roles as $name => $title) {
echo $title;
组块结果集
如果你需要处理成千上百条数据库记录,可以考虑使用 chunk 方法,该方法一次获取结果集的一小块,然后传递每一小块数据到闭包函数进行处理,该方法在编写处理大量数据库记录的 Artisan 命令的时候非常有用。例如,我们可以将处理全部 users 表数据分割成一次处理 100 条记录的小组块:
DB::table('users')->orderBy('id')->chunk(100, function($users) {
foreach ($users as $user) {
//
});
你可以通过从闭包函数中返回 false 来终止组块的运行:
DB::table('users')->orderBy('id')->chunk(100, function($users) {
// 处理结果集...
return false;
});
如果你要在组块结果集中更新数据库记录,组块结果可能会以意想不到的方式被更改。因此,在组块查询中更新记录时,最好使用 chunkById 方法。该方法会自动基于记录的主键对结果集进行分页:
DB::table('users')->where('active', false)
->chunkById(100, function ($users) {
foreach ($users as $user) {
DB::table('users')
->where('id', $user->id)
->update(['active' => true]);
});
注:在组块回调中更新或删除记录时,对主键或外键的任何更改都会影响组块查询,甚至会导致相应记录不被包含在组块结果集中。
聚合函数
查询构建器还提供了多个聚合方法,如count, max, min, avg 和 sum,你可以在构造查询之后调用这些方法:
$users = DB::table('users')->count();
$price = DB::table('orders')->max('price');
当然,你可以联合其它查询子句和聚合函数来构建查询:
$price = DB::table('orders')
->where('finalized', 1)
->avg('price');
判断记录是否存在
除了通过 count 方法来判断匹配查询条件的结果是否存在外,还可以使用 exists 或 doesntExist 方法:
return DB::table('orders')->where('finalized', 1)->exists();
return DB::table('orders')->where('finalized', 1)->doesntExist();
查询(Select)
指定查询子句
当然,我们并不总是想要获取数据表的所有列,使用 select 方法,你可以为查询指定自定义的 select 子句:
$users = DB::table('users')->select('name', 'email as user_email')->get();
distinct 方法允许你强制查询返回不重复的结果集:
$users = DB::table('users')->distinct()->get();
如果你已经有了一个查询构建器实例并且希望添加一个查询列到已存在的 select 子句,可以使用 addSelect 方法:
$query = DB::table('users')->select('name');
$users = $query->addSelect('age')->get();
原生表达式
有时候你希望在查询中使用原生表达式,这些表达式将会以字符串的形式注入到查询中,所以要格外小心避免 SQL 注入。想要创建一个原生表达式,可以使用 DB::raw 方法:
$users = DB::table('users')
->select(DB::raw('count(*) as user_count, status'))
->where('status', '<>', 1)
->groupBy('status')
->get();
注:原生语句会以字符串的形式注入查询,所以这里尤其要注意避免 SQL 注入攻击。
原生方法
除了使用 DB::raw 外,你还可以使用以下方法来插入原生表达式到查询的不同部分。
selectRaw
selectRaw 方法可用于替代 select(DB::raw(...)),该方法接收一个可选的绑定数组作为第二个参数:
$orders = DB::table('orders')
->selectRaw('price * ? as price_with_tax', [1.0825])
->get();
whereRaw / orWhereRaw
whereRaw 和 orWhereRaw 方法可用于注入原生 where 子句到查询,这两个方法接收一个可选的绑定数组作为第二个参数:
$orders = DB::table('orders')
->whereRaw('price > IF(state = "TX", ?, 100)', [200])
->get();
havingRaw / orHavingRaw
havingRaw 和 orHavingRaw 方法可用于设置原生字符串作为 having 子句的值:
$orders = DB::table('orders')
->select('department', DB::raw('SUM(price) as total_sales'))
->groupBy('department')
->havingRaw('SUM(price) > 2500')
->get();
orderByRaw
orderByRaw 方法可用于设置原生字符串作为 order by 子句的值:
$orders = DB::table('orders')
->orderByRaw('updated_at - created_at DESC')
->get();
groupByRaw
groupByRaw 方法可用于设置原生字符串作为 group by 子句的值:
$orders = DB::table('orders')
->select('city', 'state')
->groupByRaw('city, state')
->get();
连接(Join)
查询构建器还可以用于编写连接语句,关于 SQL 的几种连接类型,通过下图可以一目了然:
内连接(等值连接)
要实现一个简单的"内连接",你可以使用查询构建器实例上的 join 方法,传递给 join 方法的第一个参数是你需要连接到的表名,剩余的其它参数则是为连接指定的列约束,当然,正如你所看到的,你可以在单个查询中连接多张表:
$users = DB::table('users')
->join('contacts', 'users.id', '=', 'contacts.user_id')
->join('orders', 'users.id', '=', 'orders.user_id')
->select('users.*', 'contacts.phone', 'orders.price')
->get();
左连接/右连接
如果你是想要执行「左连接」或「右连接」而不是「内连接」,可以使用 leftJoin 或 rightJoin 方法。这些方法和 join 方法的用法一样:
$users = DB::table('users')
->leftJoin('posts', 'users.id', '=', 'posts.user_id')
->get();
$users = DB::table('users')
->rightJoin('posts', 'users.id', '=', 'posts.user_id')
->get();
交叉连接
要执行“交叉连接”可以使用 crossJoin 方法,传递你想要交叉连接的表名到该方法即可。交叉连接在第一张表和被连接表之间生成一个笛卡尔积:
$users = DB::table('sizes')
->crossJoin('colours')
->get();
高级连接语句
你还可以指定更多的高级连接子句,传递一个闭包到 join 方法作为第二个参数,该闭包将会接收一个 JoinClause 对象用于指定 join 子句约束:
DB::table('users')
->join('contacts', function ($join) {
$join->on('users.id', '=', 'contacts.user_id')->orOn(...);
})
->get();
如果你想要在连接中使用“where”风格的子句,可以在查询中使用 where 和orWhere 方法。这些方法会将列和值进行比较而不是列和列进行比较:
DB::table('users')
->join('contacts', function ($join) {
$join->on('users.id', '=', 'contacts.user_id')
->where('contacts.user_id', '>', 5);
})
->get();
子查询连接
你可以使用 joinSub、leftJoinSub 和 rightJoinSub 方法将查询和一个子查询进行连接,每个方法都接收三个参数 —— 子查询、表别名和定义关联字段的闭包:
$latestPosts = DB::table('posts')
->select('user_id', DB::raw('MAX(created_at) as last_post_created_at'))
->where('is_published', true)
->groupBy('user_id');
$users = DB::table('users')
->joinSub($latestPosts, 'latest_posts', function($join) {
$join->on('users.id', '=', 'latest_posts.user_id');
})->get();
联合(Union)
查询构建器还提供了“联合”两个查询的快捷方式,比如,你可以先创建一个查询,然后使用 union 方法将其和第二个查询进行联合:
$first = DB::table('users')
->whereNull('first_name');
$users = DB::table('users')
->whereNull('last_name')
->union($first)
->get();
注:unionAll 方法也是有效的,并且和 union 使用方式相同。
Where 子句
简单 Where 子句
使用查询构建器上的 where 方法可以添加 where 子句到查询中,调用where 最基本的方式需要传递三个参数,第一个参数是列名,第二个参数是任意一个数据库系统支持的操作符,第三个参数是该列要比较的值。
例如,下面是一个验证「votes」列的值是否等于 100 的查询:
$users = DB::table('users')->where('votes', '=', 100)->get();
为了方便,如果你只是简单比较列值和给定数值是否相等,可以将数值直接作为where 方法的第二个参数:
$users = DB::table('users')->where('votes', '=', 100)->get();
当然,你还可以使用其它操作符来编写 where 子句:
$users = DB::table('users')
->where('votes', '>=', 100)
->get();
$users = DB::table('users')
->where('votes', '<>', 100)
->get();
$users = DB::table('users')
->where('name', 'like', 'T%')
->get();
还可以传递条件数组到 where 函数:
$users = DB::table('users')->where([
['status', '=', '1'],
['subscribed', '<>', '1'],
])->get();
or 语句
你可以通过方法链将多个 where 约束链接到一起,也可以添加 or 子句到查询,orWhere 方法和 where 方法接收参数一样:
$users = DB::table('users')
->where('votes', '>', 100)
->orWhere('name', 'John')
->get();
如果你需要对 or 条件进行分组,可以传递一个闭包作为 orWhere 的第一个参数:
$users = DB::table('users')
->where('votes', '>', 100)
->orWhere(function($query) {
$query->where('name', 'Abigail')
->where('votes', '>', 50);
})
->get();
// SQL: select * from users where votes > 100 or (name = 'Abigail' and votes > 50)
更多 Where 子句
whereBetween/orWhereBetween
whereBetween 方法验证列值是否在给定值之间:
$users = DB::table('users')
->whereBetween('votes', [1, 100])
->get();
whereNotBetween/orWhereNotBetween
whereNotBetween 方法验证列值不在给定值之间:
$users = DB::table('users')
->whereNotBetween('votes', [1, 100])
->get();
whereIn/whereNotIn/orWhereIn/orWhereNotIn
whereIn 方法验证给定列的值是否在给定数组中:
$users = DB::table('users')
->whereIn('id', [1, 2, 3])
->get();
whereNotIn 方法验证给定列的值不在给定数组中:
$users = DB::table('users')
->whereNotIn('id', [1, 2, 3])
->get();
注:如果要添加非常大的整型数组绑定到查询,可以使用 whereIntegerInRaw 或 whereIntegerNotInRaw 方法来降低内存开销。
whereNull/whereNotNull/orWhereNull/orWhereNotNull
whereNull 方法验证给定列的值为 NULL:
$users = DB::table('users')
->whereNull('updated_at')
->get();
whereNotNull 方法验证给定列的值不是 NULL:
$users = DB::table('users')
->whereNotNull('updated_at')
->get();
whereDate/whereMonth/whereDay/whereYear/whereTime
whereDate 方法用于比较字段值和日期:
$users = DB::table('users')
->whereDate('created_at', '2019-12-31')
->get();
whereMonth 方法用于比较字段值和一年中的指定月份:
$users = DB::table('users')
->whereMonth('created_at', '12')
->get();
whereDay 方法用于比较字段值和一月中的指定日期:
$users = DB::table('users')
->whereDay('created_at', '31')
->get();
whereYear 方法用于比较字段值和指定年:
$users = DB::table('users')
->whereYear('created_at', '2019')
->get();
whereTime 方法用于比较字段值和指定时间:
$users = DB::table('users')
->whereTime('created_at', '=', '11:20:45')
->get();
whereColumn/orWhereColumn
whereColumn 方法用于验证两个字段是否相等:
$users = DB::table('users')
->whereColumn('first_name', 'last_name')
->get();
还可以传递一个比较运算符到该方法:
$users = DB::table('users')
->whereColumn('updated_at', '>', 'created_at')
->get();
还可以传递多条件数组到 whereColumn 方法,这些条件通过 and 操作符进行连接:
$users = DB::table('users')
->whereColumn([
['first_name', '=', 'last_name'],
['updated_at', '>', 'created_at']
])->get();
参数分组
有时候你需要创建更加高级的 where 子句,比如“where exists”或者嵌套的参数分组。Laravel 查询构建器也可以处理这些。作为开始,让我们看一个在括号中进行分组约束的例子:
DB::table('users')
->where('name', '=', 'John')
->orWhere(function ($query) {
$query->where('votes', '>', 100)
->where('title', '<>', 'Admin');
})
->get();
正如你所看到的,传递闭包到 orWhere 方法构造查询构建器来开始一个约束分组,该闭包将会获取一个用于设置括号中包含的约束的查询构建器实例。上述语句等价于下面的 SQL:
select * from users where name = '学院君' or (votes > 100 and title <> 'Admin')
注:无论何时都需要对 orWhere 调用进行分组以避免应用全局作用域时出现与预期不符 的行为。
where exists 子句
whereExists 方法允许你编写 where exists SQL 子句,whereExists 方法接收一个闭包参数,该闭包获取一个查询构建器实例从而允许你定义放置在“exists”子句中的查询:
DB::table('users')
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})
->get();
上述查询等价于下面的 SQL 语句:
select * from users
where exists (
select 1 from orders where orders.user_id = users.id
子查询 Where 子句
有时候你可能需要构造一个比较子查询结果与给定值的 where 子句,这可以通过传递一个闭包和值到 where 方法来实现。例如,下面的查询会获取所有类型为 Pro 的用户,这个类型是从 membership 表中获取的最新一条记录,如果对应结果 type 值不为 Pro,则返回 null:
use App\Models\User;
$users = User::where(function ($query) {
$query->select('type')
->from('membership')
->whereColumn('user_id', 'users.id')
->orderByDesc('start_date')
->limit(1);
}, 'Pro')->get();
JSON Where 子句
Laravel 还支持在提供 JSON 字段类型的数据库(目前是 MySQL 5.7、 PostgresSQL、SQL Server 2016 以及 SQLite 3.9.0)上使用操作符 -> 获取指定 JSON 字段值:
$users = DB::table('users')
->where('options->language', 'en')
->get();
$users = DB::table('users')
->where('preferences->dining->meal', 'salad')
->get();
可以使用 whereJsonContains 查询 JSON 数组(SQLite 不支持):
$users = DB::table('users')
->whereJsonContains('options->languages', 'en')
->get();
MySQL 和 PostgreSQL 支持在 whereJsonContains 中传入多个值:
$users = DB::table('users')
->whereJsonContains('options->languages', ['en', 'de'])
->get();
可以使用 whereJsonLength 通过长度来查询 JSON 数组:
$users = DB::table('users')
->whereJsonLength('options->languages', 0)
->get();
$users = DB::table('users')
->whereJsonLength('options->languages', '>', 1)
->get();
排序、分组、限定
orderBy
orderBy 方法允许你通过给定字段对结果集进行排序,orderBy 的第一个参数应该是你希望排序的字段,第二个参数控制着排序的方向 —— asc 或 desc:
$users = DB::table('users')
->orderBy('name', 'desc')
->get();
如果你需要基于多个字段进行排序,可以通过多次调用 orderBy 来实现:
$users = DB::table('users')
->orderBy('name', 'desc')
->orderBy('email', 'asc')
->get();
latest / oldest
latest 和 oldest 方法允许你通过日期对结果进行排序,默认情况下,结果集根据 created_at 字段进行排序,或者,你可以按照你想要排序的字段作为字段名传入:
$user = DB::table('users')
->latest()
->first();
inRandomOrder
inRandomOrder 方法可用于对查询结果集进行随机排序,比如,你可以用该方法获取一个随机用户:
$randomUser = DB::table('users')
->inRandomOrder()
->first();
reorder
reorder 方法可用于移除所有已存在的排序并且应用一个新的排序(可选):
$query = DB::table('users')->orderBy('name');
$unorderedUsers = $query->reorder()->get();
这里没有传递新的排序,如果想要应用的话可以这么设置:
$query = DB::table('users')->orderBy('name');
$usersOrderedByEmail = $query->reorder('email', 'desc')->get();
groupBy/having
groupBy 和 having 方法用于对结果集进行分组,having 方法和 where 方法的用法类似:
$users = DB::table('users')
->groupBy('account_id')
->having('account_id', '>', 100)
->get();
你可以传递多个参数到 groupBy 方法以便通过多个列进行分组:
$users = DB::table('users')
->groupBy('first_name', 'status')
->having('account_id', '>', 100)
->get();
关于 having 的更多高级用法,可查看 havingRaw 方法。
skip/take
想要限定查询返回的结果集的数目,或者在查询中跳过给定数目的结果,可以使用skip 和 take 方法:
$users = DB::table('users')->skip(10)->take(5)->get();
作为替代方法,还可以使用 limit 和 offset 方法:
$users = DB::table('users')
->offset(10)
->limit(5)
->get();
条件子句
有时候你可能想要某些条件为 true 的时候才将条件子句应用到查询。例如,你可能只想给定值在请求中存在的情况下才应用 where 语句,这可以通过 when 方法实现:
$role = $request->input('role');
$users = DB::table('users')
->when($role, function ($query) use ($role) {
return $query->where('role_id', $role);
})
->get();
when 方法只有在第一个参数为 true 的时候才执行给定闭包,如果第一个参数为 false,则闭包不执行。
你可以传递另一个闭包作为 when 方法的第三个参数,该闭包会在第一个参数为 false 的情况下执行。为了演示这个特性如何使用,我们来配置一个查询的默认排序:
$sortBy = null;
$users = DB::table('users')
->when($sortBy, function ($query) use ($sortBy) {
return $query->orderBy($sortBy);
}, function ($query) {
return $query->orderBy('name');
})
->get();
插入(Insert)
查询构建器还提供了 insert 方法用于插入记录到数据表。insert 方法接收数组形式的字段名和字段值进行插入操作:
DB::table('users')->insert(
['email' => 'john@example.com', 'votes' => 0]
);
你甚至可以一次性通过传入多个数组来插入多条记录,每个数组代表要插入数据表的记录:
DB::table('users')->insert([
['email' => 'taylor@example.com', 'votes' => 0],
['email' => 'dayle@example.com', 'votes' => 0]
]);
insertOrIgnore 方法会在插入记录到数据库时忽略重复记录错误:
DB::table('users')->insertOrIgnore([
['id' => 1, 'email' => 'taylor@example.com'],
['id' => 2, 'email' => 'dayle@example.com']
]);
自增 ID
如果数据表有自增 ID,使用 insertGetId 方法来插入记录并返回ID值:
$id = DB::table('users')->insertGetId(
['email' => 'john@example.com', 'votes' => 0]
);
注:当使用 PostgresSQL 时 insertGetId 方法默认自增列被命名为 id,如果你想要从其他“序列”获取ID,可以将序列名作为第二个参数传递到 insertGetId 方法。
更新(Update)
当然,除了插入记录到数据库,查询构建器还可以通过使用 update 方法更新已有记录。update 方法和 insert 方法一样,接收字段名和字段值的键值对数组,对应字段名就是要更新的列,你可以通过 where 子句来对 update 查询进行约束:
DB::table('users')
->where('id', 1)
->update(['votes' => 1]);
更新或插入
有时候你可能想要更新数据库中已存在的某条记录,如果对应记录不存在的话,则插入这条记录。在这种场景下,可以使用 updateOrInsert 方法。该方法接收两个参数:用于查询记录的条件数组和用于更新的列值对数组。
updateOrInsert 方法首先会尝试使用第一个参数的列值对匹配对应的数据库记录,如果记录存在,则通过第二个参数来更新它。如果记录不存在,则会合并这两个参数数组然后通过合并后的数组插入一条新纪录:
DB::table('users')
->updateOrInsert(
['email' => 'john@example.com', 'name' => 'John'],
['votes' => '2']
);
更新 JSON 字段
更新 JSON 字段的时候,需要使用 -> 语法访问 JSON 对象上相应的值,该操作只能用于支持 JSON 字段类型的数据库:
DB::table('users')
->where('id', 1)
->update(['options->enabled' => true]);
增加/减少
查询构建器还为增减给定字段名对应数值提供方便。相较于编写 update 语句,这是一条捷径,提供了更好的体验和测试接口。
这两个方法都至少接收一个参数:需要修改的列。第二个参数是可选的,用于控制列值增加/减少的数目。
DB::table('users')->increment('votes');
DB::table('users')->increment('votes', 5);
DB::table('users')->decrement('votes');
DB::table('users')->decrement('votes', 5);
在操作过程中你还可以指定额外的列进行更新:
DB::table('users')->increment('votes', 1, ['name' => 'John']);
注:使用 increment 和 decrement 方法时不会触发模型事件。
删除(Delete)
当然,查询构建器还可以通过 delete 方法从表中删除记录,你可以在调用 delete 方法前通过添加 where 子句来添加约束条件:
DB::table('users')->delete();
DB::table('users')->where('votes', '>', 100)->delete();
如果你希望清除整张表,也就是删除所有列并将自增 ID 置为 0,可以使用 truncate 方法:
DB::table('users')->truncate();
悲观锁 & 乐观锁
悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁、表锁、读锁、写锁等,都是在做操作之前先上锁。
乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于 write_condition 机制的其实都是提供的乐观锁。
下面我们看下悲观锁和乐观锁在 Laravel 中的使用:
悲观锁使用
Laravel 查询构建器提供了一些方法帮助你在 select 语句中实现“悲观锁”。可以在查询中使用 sharedLock 方法从而在运行语句时带一把”共享锁“。共享锁可以避免被选择的行被修改直到事务提交:
DB::table('users')->where('votes', '>', 100)->sharedLock()->get();
上面这个查询等价于下面这条 SQL 语句:
select * from `users` where `votes` > '100' lock in share mode
此外你还可以使用 lockForUpdate 方法。“for update”锁避免选择行被其它共享锁修改或删除:
DB::table('users')->where('votes', '>', 100)->lockForUpdate()->get();
上面这个查询等价于下面这条 SQL 语句:
select * from `users` where `votes` > '100' for update
for update 与 lock in share mode 都是用于确保被选中的记录值不能被其它事务更新(上锁),两者的区别在于 lock in share mode 不会阻塞其它事务读取被锁定行记录的值,而 for update 会阻塞其他锁定性读对锁定行的读取(非锁定性读仍然可以读取这些记录,lock in share mode 和 for update 都是锁定性读)。
这么说比较抽象,我们举个计数器的例子:在一条语句中读取一个值,然后在另一条语句中更新这个值。使用 lock in share mode 的话可以允许两个事务读取相同的初始化值,所以执行两个事务之后最终计数器的值+1;而如果使用 for update 的话,会锁定第二个事务对记录值的读取直到第一个事务执行完成,这样计数器的最终结果就是+2了。
乐观锁使用
乐观锁,大多是基于数据版本 ( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。
读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
总结
两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行重试,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。
调试
你可以在构建查询的时候使用 dd 或 dump 方法来打印查询绑定和 SQL 语句。dd 方法会显示调试信息并停止执行请求,dump 方法也会显示调试信息但允许请求继续执行:
DB::table('users')->where('votes', '>', 100)->dd();
DB::table('users')->where('votes', '>', 100)->dump();
实例教程
查询构建器入门:数据库增删改查
查询构建器进阶:构建复杂的查询语句
联合查询
调试
乐观锁
悲观锁
限定
排序
JSON
子查询
分组
where
Laravel
连接查询
聚合函数
组块
增删改查
DB
查询构建器
数据库
文档
8.x
点赞
取消点赞
收藏
取消收藏
赞赏
分享到以下平台:
<< 上一篇:
数据库入门
>> 下一篇:
分页
无评论
登录后即可添加评论
升级为学院君订阅用户(新年优惠🎁)
内容导航
简介
获取结果集
组块结果集
聚合函数
查询(Select)
原生表达式
原生方法
连接(Join)
联合(Union)
Where 子句
简单 Where 子句
or 语句
更多 Where 子句
参数分组
where exists 子句
子查询 Where 子句
JSON Where 子句
排序、分组、限定
条件子句
插入(Insert)
更新(Update)
更新 JSON 字段
增加/减少
删除(Delete)
悲观锁 & 乐观锁
悲观锁使用
乐观锁使用
总结
调试
实例教程
相关推荐
查询构建器
Laravel 5.6 中文文档
数据库操作
查询构建器
Laravel 5.7 中文文档
数据库操作
查询构建器
Laravel 5.8 中文文档
数据库操作
查询构建器
Laravel 6 中文文档
数据库操作
查询构建器
Laravel 7 中文文档
数据库操作
回到顶部
2022 基于 Laravel 6 构建
关于学院
订阅服务
友情链接
站点地图
本站 CDN 加速服务由又拍云赞助