laravel之DB门面&查询构造器 操作数据库

门面DB

DB::select('select * from users where id = :id', ['id' => 1]);
DB::insert('insert into users (id, name) values (?, ?)', [1, '学院君']);
DB::update('update users set votes = 100 where name = ?', ['学院君']);
DB::delete('delete from users');

有些数据库语句不返回任何值

DB::statement('drop table users');

监听

App\Providers AppServiceProvider
public function boot()
{
   DB::listen(function ($query) {
   // $query->sql
   // $query->bindings
   // $query->time
   });
}

数据库事务
使用 transaction 方法时不需要手动回滚或提交:如果事务闭包中抛出异常,事务将会自动回滚;如果闭包执行成功,事务将会自动提交:

DB::transaction(function () {
  DB::table('users')->update(['votes' => 1]);
  DB::table('posts')->delete();
});

处理死锁
transaction 第二个参数,用于定义死锁发生时事务的最大重试次数

DB::transaction(function () {
  DB::table('users')->update(['votes' => 1]); 
  DB::table('posts')->delete();
}, 5);

手动使用事务

DB::beginTransaction();
DB::rollBack();
DB::commit();

 

通过查询构建器实现高级功能

流接口模式(Fluent Interface)
https://laravelacademy.org/post/2828.html

DB::table('users')->get();

一行

DB::table('users')->where('name', 'John')->first();

单个值

DB::table('users')->where('name', 'John')->value('email');

单个列值的数组

DB::table('roles')->pluck('title');

id为键,name为val的一维数组

DB::table('roles')->pluck('name','id');

组块结果集

DB::table('users')->orderBy('id')->chunk(100, function($users) {
  foreach ($users as $user) {
  //
  }
});

聚合函数 count, max, min, avg 和 sum

DB::table('users')->count();
DB::table('users')->max('age');

查询(Select)

DB::table('users')->select('name', 'email as user_email')->get();

不重复的结果集

DB::table('users')->distinct()->get();

如果你已经有了一个查询构建器实例并且希望添加一个查询列到已存在的 select 子句,可以使用 addSelect 方法:

$query = DB::table('users')->select('name');
$users = $query->addSelect('age')->get();

原生表达式

DB::raw
$users = DB::table('users')
->select(DB::raw('count(*) as user_count, status'))
->where('status', '<>', 1)
->groupBy('status')
->get();

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();

连接(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

$users = DB::table('users')
->leftJoin('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();

联合(Union)

$first = DB::table('users')
->whereNull('first_name');

$users = DB::table('users')
->whereNull('last_name')
->union($first)
->get();

 

Where子句

where('votes',100)
where('votes', '=', 100)
where('votes', '<>', 100)
where('name', 'like', 'T%')

数组

where([
['status', '=', '1'],
['subscribed', '<>', '1'],
])

orWhere(‘name’, ‘John’)

#验证列值是否在给定值之间

whereBetween('votes', [1, 100])
whereNotBetween('votes', [1, 100]

whereIn('id', [1, 2, 3])
whereNotIn('id', [1, 2, 3])

whereNull('updated_at')
whereNotNull('updated_at')

whereDate('created_at', '2016-10-10')
whereMonth('created_at', '10')
whereDay('created_at', '10')
whereYear('created_at', '2017')

验证两个字段是否相等

whereColumn('first_name', 'last_name')

可以传递一个比较运算符到该方法

whereColumn('updated_at', '>', 'created_at')
DB::table('users')
->whereColumn([
['first_name', '=', 'last_name'],
['updated_at', '>', 'created_at']
])->get();

参数分组

DB::table('users')
->where('name', '=', 'John')
->orWhere(function ($query) {
$query->where('votes', '>', 100)
->where('title', '<>', 'Admin');
})
->get();

select * from users where name = 'John' or (votes > 100 and title <> 'Admin')

 

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
)

JSON Where子句
Laravel 还支持在提供 JSON 字段类型的数据库(目前是 MySQL 5.7 和 PostgresSQL)上使用操作符 -> 获取指定 JSON 字段值:

$users = DB::table('users')
->where('options->language', 'en')
->get();

$users = DB::table('users')
->where('preferences->dining->meal', 'salad')
->get();

排序、分组、限定

orderBy('name', 'desc')

latest / oldest
默认是 created_at 字段进行排序,或者,你可以按照你想要排序的字段作为字段名传入:

DB::table('users')->latest()->first();

inRandomOrder , 获取一个随机用户

DB::table('users')
->inRandomOrder()
->first();

groupBy / having

$users = DB::table('users')
->groupBy('account_id')
->having('account_id', '>', 100)
->get();

skip / take

DB::table('users')->skip(10)->take(5)->get();

offset / limit

DB::table('users')
->offset(10)
->limit(5)
->get();

条件字句 when
when 方法只有在第一个参数为 true 的时候才执行给定闭包,如果第一个参数为 false,则闭包不执行。

$role = $request->input('role');
$users = DB::table('users')
->when($role, function ($query) use ($role) {
return $query->where('role_id', $role);
})
->get();

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)

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]
]);

自增ID insertGetId

$id = DB::table('users')->insertGetId(
['email' => 'john@example.com', 'votes' => 0]
);

更新(Update)

DB::table('users')
->where('id', 1)
->update(['votes' => 1]);

更新 JSON 字段

DB::table('users')
->where('id', 1)
->update(['options->enabled' => true]);

增加/减少

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']);

删除(Delete)

DB::table('users')->where('votes', '>', 100)->delete();

悲观锁 & 乐观锁
悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁、表锁、读锁、写锁等,都是在做操作之前先上锁。

乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于 write_condition 机制的其实都是提供的乐观锁。

DB::table(‘users’)->where(‘votes’, ‘>’, 100)->sharedLock()->get();
select * from `users` where `votes` > ‘100’ lock in share mode

此外你还可以使用 lockForUpdate 方法。“for update”锁避免选择行被其它共享锁修改或删除:
DB::table(‘users’)->where(‘votes’, ‘>’, 100)->lockForUpdate()->get();
select * from `users` where `votes` > ‘100’ for update