数据库:查询生成器
- Introduction
- 运行数据库查询
- 选择语句
- 原始表达式
- Joins
- Unions
- 基本 Where 子句
- 高级 Where 子句
- 排序、分组、限制和偏移
- 条件条款
- 插入语句
- 更新报表
- 删除语句
- 悲观锁
- Debugging
Introduction
Laravel 的数据库查询构建器为创建和运行数据库查询提供了一个方便、流畅的界面。它可用于在您的应用程序中执行大多数数据库操作,并与 Laravel 支持的所有数据库系统完美配合。
Laravel 查询生成器使用 PDO 参数绑定来保护您的应用程序免受 SQL 注入攻击。无需清理或清理作为查询绑定传递给查询构建器的字符串。
Warning
PDO 不支持绑定列名。因此,您永远不应允许用户输入来指示您的查询引用的列名,包括“排序依据”列。
运行数据库查询
从表中检索所有行
您可以使用table
提供的方法DB
门面开始查询。这table
方法返回给定表的流畅查询生成器实例,允许您将更多约束链接到查询,然后最终使用get
方法:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Show a list of all of the application's users.
*/
public function index(): View
{
$users = DB::table('users')->get();
return view('user.index', ['users' => $users]);
}
}
这get
方法返回一个Illuminate\Support\Collection
包含查询结果的实例,其中每个结果都是 PHP 的一个实例stdClass
目的。您可以通过访问作为对象属性的列来访问每一列的值:
use Illuminate\Support\Facades\DB;
$users = DB::table('users')->get();
foreach ($users as $user) {
echo $user->name;
}
Note
Laravel 集合提供了多种非常强大的方法来映射和减少数据。有关 Laravel 集合的更多信息,请查看收集文件.
从表中检索单个行/列
如果您只需要从数据库表中检索一行,您可以使用DB
门面的first
方法。此方法将返回一个stdClass
目的:
$user = DB::table('users')->where('name', 'John')->first();
return $user->email;
如果不需要整行,可以使用value
方法。此方法将直接返回列的值:
$email = DB::table('users')->where('name', 'John')->value('email');
通过其检索单行id
列值,使用find
方法:
$user = DB::table('users')->find(3);
检索列值列表
如果你想找回一个Illuminate\Support\Collection
实例包含单个列的值,您可以使用pluck
方法。在此示例中,我们将检索用户职务的集合:
use Illuminate\Support\Facades\DB;
$titles = DB::table('users')->pluck('title');
foreach ($titles as $title) {
echo $title;
}
您可以通过向pluck
方法:
$titles = DB::table('users')->pluck('title', 'name');
foreach ($titles as $name => $title) {
echo $title;
}
分块结果
如果您需要处理数以千计的数据库记录,请考虑使用chunk
提供的方法DB
正面。此方法一次检索一小块结果,并将每个块馈送到闭包中进行处理。例如,让我们检索整个users
一次以 100 条记录为一组的表:
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
DB::table('users')->orderBy('id')->chunk(100, function (Collection $users) {
foreach ($users as $user) {
// ...
}
});
您可以通过返回来停止处理更多块false
从关闭:
DB::table('users')->orderBy('id')->chunk(100, function (Collection $users) {
// Process the records...
return false;
});
如果您在对结果进行分块时更新数据库记录,则分块结果可能会以意想不到的方式发生变化。如果您计划在分块时更新检索到的记录,最好始终使用chunkById
方法代替。此方法将根据记录的主键自动对结果进行分页:
DB::table('users')->where('active', false)
->chunkById(100, function (Collection $users) {
foreach ($users as $user) {
DB::table('users')
->where('id', $user->id)
->update(['active' => true]);
}
});
Warning
在块回调中更新或删除记录时,对主键或外键的任何更改都可能影响块查询。这可能会导致记录不包含在分块结果中。
懒惰地流式传输结果
这lazy
方法类似于这chunk
方法 从某种意义上说,它以块的形式执行查询。但是,不是将每个块传递给回调,而是lazy()
方法返回一个LazyCollection
,它允许您将结果作为单个流进行交互:
use Illuminate\Support\Facades\DB;
DB::table('users')->orderBy('id')->lazy()->each(function (object $user) {
// ...
});
再一次,如果您计划在遍历记录时更新检索到的记录,最好使用lazyById
或者lazyByIdDesc
方法代替。这些方法将根据记录的主键自动对结果进行分页:
DB::table('users')->where('active', false)
->lazyById()->each(function (object $user) {
DB::table('users')
->where('id', $user->id)
->update(['active' => true]);
});
Warning
在迭代记录时更新或删除记录时,对主键或外键的任何更改都可能影响块查询。这可能会导致记录未包含在结果中。
Aggregates
查询构建器还提供了多种方法来检索聚合值,例如count
,max
,min
,avg
, 和sum
.您可以在构建查询后调用这些方法中的任何一个:
use Illuminate\Support\Facades\DB;
$users = DB::table('users')->count();
$price = DB::table('orders')->max('price');
当然,您可以将这些方法与其他子句结合使用以微调聚合值的计算方式:
$price = DB::table('orders')
->where('finalized', 1)
->avg('price');
确定记录是否存在
而不是使用count
方法来确定是否存在任何符合查询约束的记录,您可以使用exists
和doesntExist
方法:
if (DB::table('orders')->where('finalized', 1)->exists()) {
// ...
}
if (DB::table('orders')->where('finalized', 1)->doesntExist()) {
// ...
}
选择语句
指定 Select 子句
您可能并不总是希望从数据库表中选择所有列。使用select
方法,您可以为查询指定一个自定义的“select”子句:
use Illuminate\Support\Facades\DB;
$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();
原始表达式
有时您可能需要在查询中插入任意字符串。要创建原始字符串表达式,您可以使用raw
提供的方法DB
正面:
$users = DB::table('users')
->select(DB::raw('count(*) as user_count, status'))
->where('status', '<>', 1)
->groupBy('status')
->get();
Warning
原始语句将作为字符串注入到查询中,因此您应该格外小心,以免产生 SQL 注入漏洞。
原始方法
而不是使用DB::raw
方法,您还可以使用以下方法将原始表达式插入查询的各个部分。请记住,Laravel 不能保证任何使用原始表达式的查询都不受 SQL 注入漏洞的影响。
selectRaw
这selectRaw
方法可以用来代替addSelect(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();
Joins
内部连接子句
查询生成器也可用于将连接子句添加到您的查询中。要执行基本的“内部连接”,您可以使用join
查询生成器实例上的方法。第一个参数传递给join
method 是您需要连接的表的名称,而其余参数指定连接的列约束。您甚至可以在一个查询中连接多个表:
use Illuminate\Support\Facades\DB;
$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
执行“交叉连接”的方法。交叉连接在第一个表和连接表之间生成笛卡尔积:
$sizes = DB::table('sizes')
->crossJoin('colors')
->get();
高级连接子句
您还可以指定更高级的连接子句。首先,将闭包作为第二个参数传递给join
方法。关闭将收到一个Illuminate\Database\Query\JoinClause
允许您在“join”子句上指定约束的实例:
DB::table('users')
->join('contacts', function (JoinClause $join) {
$join->on('users.id', '=', 'contacts.user_id')->orOn(/* ... */);
})
->get();
如果您想在联接中使用“where”子句,您可以使用where
和orWhere
提供的方法JoinClause
实例。这些方法不是比较两列,而是将列与值进行比较:
DB::table('users')
->join('contacts', function (JoinClause $join) {
$join->on('users.id', '=', 'contacts.user_id')
->where('contacts.user_id', '>', 5);
})
->get();
子查询连接
您可以使用joinSub
,leftJoinSub
, 和rightJoinSub
将查询连接到子查询的方法。这些方法中的每一个都接收三个参数:子查询、其表别名和定义相关列的闭包。在此示例中,我们将检索用户集合,其中每个用户记录还包含created_at
用户最近发布的博客文章的时间戳:
$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 (JoinClause $join) {
$join->on('users.id', '=', 'latest_posts.user_id');
})->get();
Unions
查询生成器还提供了一种将两个或多个查询“合并”在一起的便捷方法。例如,您可以创建一个初始查询并使用union
将其与更多查询合并的方法:
use Illuminate\Support\Facades\DB;
$first = DB::table('users')
->whereNull('first_name');
$users = DB::table('users')
->whereNull('last_name')
->union($first)
->get();
除了union
方法,查询构建器提供了一个unionAll
方法。使用组合的查询unionAll
方法不会删除重复的结果。这unionAll
方法具有与union
方法。
基本 Where 子句
Where 子句
您可以使用查询生成器的where
向查询添加“where”子句的方法。最基本的调用where
方法需要三个参数。第一个参数是列的名称。第二个参数是一个运算符,它可以是数据库支持的任何运算符。第三个参数是要与列值进行比较的值。
例如,以下查询检索用户的值votes
列等于100
和价值age
列大于35
:
$users = DB::table('users')
->where('votes', '=', 100)
->where('age', '>', 35)
->get();
为了方便起见,如果你想验证一个列是=
给定值,您可以将该值作为第二个参数传递给where
方法。 Laravel 会假设你想使用=
操作员:
$users = DB::table('users')->where('votes', 100)->get();
如前所述,您可以使用数据库系统支持的任何运算符:
$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
功能。数组的每个元素都应该是一个数组,其中包含通常传递给where
方法:
$users = DB::table('users')->where([
['status', '=', '1'],
['subscribed', '<>', '1'],
])->get();
Warning
PDO 不支持绑定列名。因此,您永远不应允许用户输入来指示您的查询引用的列名,包括“排序依据”列。
或 Where 子句
将对查询构建器的调用链接在一起时where
方法,“where”子句将使用and
操作员。但是,您可以使用orWhere
使用的方法将子句连接到查询or
操作员。这orWhere
方法接受与where
方法:
$users = DB::table('users')
->where('votes', '>', 100)
->orWhere('name', 'John')
->get();
如果您需要在括号内对“或”条件进行分组,您可以将闭包作为第一个参数传递给orWhere
方法:
$users = DB::table('users')
->where('votes', '>', 100)
->orWhere(function(Builder $query) {
$query->where('name', 'Abigail')
->where('votes', '>', 50);
})
->get();
上面的示例将生成以下 SQL:
select * from users where votes > 100 or (name = 'Abigail' and votes > 50)
Warning
你应该总是分组orWhere
调用以避免在应用全局范围时发生意外行为。
Where Not 子句
这whereNot
和orWhereNot
方法可用于否定给定的一组查询约束。例如,以下查询排除了正在清仓或价格低于 10 的产品:
$products = DB::table('products')
->whereNot(function (Builder $query) {
$query->where('clearance', true)
->orWhere('price', '<', 10);
})
->get();
JSON Where 子句
Laravel 还支持在提供 JSON 列类型支持的数据库上查询 JSON 列类型。目前,这包括 MySQL 5.7+、PostgreSQL、SQL Server 2016 和 SQLite 3.39.0(带有JSON1 扩展).要查询 JSON 列,请使用->
操作员:
$users = DB::table('users')
->where('preferences->dining->meal', 'salad')
->get();
你可以使用whereJsonContains
查询 JSON 数组。低于 3.38.0 的 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();
附加的 Where 子句
WhereBetween / 或 WhereBetween
这whereBetween
方法验证列的值是否介于两个值之间:
$users = DB::table('users')
->whereBetween('votes', [1, 100])
->get();
whereNotBetween / orWhereNotBetween
这whereNotBetween
方法验证列的值是否位于两个值之外:
$users = DB::table('users')
->whereNotBetween('votes', [1, 100])
->get();
whereBetweenColumns / whereNotBetweenColumns / orWhereBetweenColumns / orWhereNotBetweenColumns
这whereBetweenColumns
方法验证列的值是否介于同一表行中两列的两个值之间:
$patients = DB::table('patients')
->whereBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight'])
->get();
这whereNotBetweenColumns
方法验证列的值是否位于同一表行中两列的两个值之外:
$patients = DB::table('patients')
->whereNotBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight'])
->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();
您还可以提供一个查询对象作为whereIn
方法的第二个参数:
$activeUsers = DB::table('users')->select('id')->where('is_active', 1);
$users = DB::table('comments')
->whereIn('user_id', $activeUsers)
->get();
上面的示例将生成以下 SQL:
select * from comments where user_id in (
select id
from users
where is_active = 1
)
Warning
如果您要向查询中添加大量整数绑定,则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', '2016-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', '2016')
->get();
这whereTime
方法可用于将列的值与特定时间进行比较:
$users = DB::table('users')
->whereTime('created_at', '=', '11:20:45')
->get();
whereColumn / 或 WhereColumn
这whereColumn
方法可用于验证两列是否相等:
$users = DB::table('users')
->whereColumn('first_name', 'last_name')
->get();
您还可以将比较运算符传递给whereColumn
方法:
$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”子句进行分组,以实现查询所需的逻辑分组。事实上,您通常应该始终将呼叫分组到orWhere
括号中的方法以避免意外的查询行为。为此,您可以将闭包传递给where
方法:
$users = DB::table('users')
->where('name', '=', 'John')
->where(function (Builder $query) {
$query->where('votes', '>', 100)
->orWhere('title', '=', 'Admin');
})
->get();
如您所见,将闭包传递给where
方法指示查询构建器开始一个约束组。闭包将接收一个查询生成器实例,您可以使用它来设置应包含在括号组中的约束。上面的示例将生成以下 SQL:
select * from users where name = 'John' and (votes > 100 or title = 'Admin')
Warning
你应该总是分组orWhere
调用以避免在应用全局范围时发生意外行为。
高级 Where 子句
Where Exists 子句
这whereExists
方法允许您编写“where exists”SQL 子句。这whereExists
方法接受一个闭包,该闭包将接收一个查询生成器实例,允许您定义应放置在“exists”子句中的查询:
$users = DB::table('users')
->whereExists(function (Builder $query) {
$query->select(DB::raw(1))
->from('orders')
->whereColumn('orders.user_id', 'users.id');
})
->get();
或者,您可以向whereExists
方法而不是闭包:
$orders = DB::table('orders')
->select(DB::raw(1))
->whereColumn('orders.user_id', 'users.id');
$users = DB::table('users')
->whereExists($orders)
->get();
上面的两个例子都会产生下面的 SQL:
select * from users
where exists (
select 1
from orders
where orders.user_id = users.id
)
子查询 Where 子句
有时您可能需要构造一个“where”子句,将子查询的结果与给定值进行比较。您可以通过将闭包和值传递给where
方法。例如,以下查询将检索最近拥有给定类型“会员资格”的所有用户;
use App\Models\User;
use Illuminate\Database\Query\Builder;
$users = User::where(function (Builder $query) {
$query->select('type')
->from('membership')
->whereColumn('membership.user_id', 'users.id')
->orderByDesc('membership.start_date')
->limit(1);
}, 'Pro')->get();
或者,您可能需要构造一个“where”子句,将列与子查询的结果进行比较。您可以通过将列、运算符和闭包传递给where
方法。例如,以下查询将检索金额低于平均值的所有收入记录;
use App\Models\Income;
use Illuminate\Database\Query\Builder;
$incomes = Income::where('amount', '<', function (Builder $query) {
$query->selectRaw('avg(i.amount)')->from('incomes as i');
})->get();
全文 Where 子句
Warning
MySQL 和 PostgreSQL 当前支持全文 where 子句。
这whereFullText
和orWhereFullText
方法可用于将全文“where”子句添加到具有以下内容的列的查询中全文索引.这些方法会被 Laravel 转化为适合底层数据库系统的 SQL。例如,一个MATCH AGAINST
将为使用 MySQL 的应用程序生成子句:
$users = DB::table('users')
->whereFullText('bio', 'web developer')
->get();
排序、分组、限制和偏移
Ordering
这orderBy
方法
这orderBy
方法允许您按给定列对查询结果进行排序。接受的第一个论点orderBy
method 应该是你希望排序的列,而第二个参数决定了排序的方向,可以是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
方法可用于对查询结果进行随机排序。例如,您可以使用此方法来获取随机用户:
$randomUser = DB::table('users')
->inRandomOrder()
->first();
删除现有订单
这reorder
方法删除了之前应用于查询的所有“order by”子句:
$query = DB::table('users')->orderBy('name');
$unorderedUsers = $query->reorder()->get();
您可以在调用时传递列和方向reorder
方法以删除所有现有的“order by”子句并将全新的顺序应用于查询:
$query = DB::table('users')->orderBy('name');
$usersOrderedByEmail = $query->reorder('email', 'desc')->get();
Grouping
这groupBy
&having
方法
如您所料,groupBy
和having
方法可用于对查询结果进行分组。这having
方法的签名类似于where
方法:
$users = DB::table('users')
->groupBy('account_id')
->having('account_id', '>', 100)
->get();
您可以使用havingBetween
过滤给定范围内结果的方法:
$report = DB::table('orders')
->selectRaw('count(id) as number_of_orders, customer_id')
->groupBy('customer_id')
->havingBetween('number_of_orders', [5, 15])
->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
方法。这些方法在功能上等同于take
和skip
方法分别为:
$users = DB::table('users')
->offset(10)
->limit(5)
->get();
条件条款
有时您可能希望某些查询子句应用于基于另一个条件的查询。例如,您可能只想应用一个where
传入 HTTP 请求中是否存在给定输入值的语句。您可以使用when
方法:
$role = $request->string('role');
$users = DB::table('users')
->when($role, function (Builder $query, string $role) {
$query->where('role_id', $role);
})
->get();
这when
方法仅在第一个参数是时执行给定的闭包true
.如果第一个参数是false
,闭包将不会被执行。所以,在上面的例子中,给的闭包when
只有在role
字段出现在传入请求中并评估为true
.
您可以将另一个闭包作为第三个参数传递给when
方法。只有当第一个参数的计算结果为false
.为了说明如何使用此功能,我们将使用它来配置查询的默认排序:
$sortByVotes = $request->boolean('sort_by_votes');
$users = DB::table('users')
->when($sortByVotes, function (Builder $query, bool $sortByVotes) {
$query->orderBy('votes');
}, function (Builder $query) {
$query->orderBy('name');
})
->get();
插入语句
查询构建器还提供了一个insert
可用于将记录插入数据库表的方法。这insert
方法接受列名和值的数组:
DB::table('users')->insert([
'email' => 'kayla@example.com',
'votes' => 0
]);
您可以通过传递数组数组来一次插入多条记录。每个数组代表一条应该插入到表中的记录:
DB::table('users')->insert([
['email' => 'picard@example.com', 'votes' => 0],
['email' => 'janeway@example.com', 'votes' => 0],
]);
这insertOrIgnore
方法将在将记录插入数据库时忽略错误。使用此方法时,您应该注意重复记录错误将被忽略,并且根据数据库引擎的不同,其他类型的错误也可能被忽略。例如,insertOrIgnore
将要绕过MySQL的严格模式:
DB::table('users')->insertOrIgnore([
['id' => 1, 'email' => 'sisko@example.com'],
['id' => 2, 'email' => 'archer@example.com'],
]);
这insertUsing
方法将新记录插入表中,同时使用子查询来确定应插入的数据:
DB::table('pruned_users')->insertUsing([
'id', 'name', 'email', 'email_verified_at'
], DB::table('users')->select(
'id', 'name', 'email', 'email_verified_at'
)->where('updated_at', '<=', now()->subMonth()));
自动递增 ID
如果表有一个自动递增的 id,使用insertGetId
插入记录然后检索 ID 的方法:
$id = DB::table('users')->insertGetId(
['email' => 'john@example.com', 'votes' => 0]
);
Warning
使用 PostgreSQL 时insertGetId
方法期望自动递增的列被命名id
.如果您想从不同的“序列”中检索 ID,您可以将列名作为第二个参数传递给insertGetId
方法。
Upserts
这upsert
方法将插入不存在的记录,并使用您可能指定的新值更新已存在的记录。该方法的第一个参数包含要插入或更新的值,而第二个参数列出了在关联表中唯一标识记录的列。该方法的第三个也是最后一个参数是一个列数组,如果数据库中已存在匹配记录,则应更新该列:
DB::table('flights')->upsert(
[
['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
],
['departure', 'destination'],
['price']
);
在上面的示例中,Laravel 将尝试插入两条记录。如果已经存在相同的记录departure
和destination
列值,Laravel 将更新该记录的price
柱子。
Warning
除 SQL Server 外的所有数据库都需要第二个参数中的列upsert
具有“主要”或“唯一”索引的方法。此外,MySQL 数据库驱动程序忽略了upsert
方法并始终使用表的“主要”和“唯一”索引来检测现有记录。
更新报表
除了将记录插入数据库外,查询构建器还可以使用update
方法。这update
方法,比如insert
方法,接受一个列和值对的数组,指示要更新的列。这update
方法返回受影响的行数。你可以限制update
查询使用where
条款:
$affected = DB::table('users')
->where('id', 1)
->update(['votes' => 1]);
更新或插入
有时您可能希望更新数据库中的现有记录,或者在不存在匹配记录时创建它。在这种情况下,updateOrInsert
可以使用方法。这updateOrInsert
方法接受两个参数:用于查找记录的条件数组,以及指示要更新的列的列和值对数组。
这updateOrInsert
方法将尝试使用第一个参数的列和值对来定位匹配的数据库记录。如果记录存在,它将用第二个参数中的值更新。如果找不到该记录,将插入一个新记录,其中包含两个参数的合并属性:
DB::table('users')
->updateOrInsert(
['email' => 'john@example.com', 'name' => 'John'],
['votes' => '2']
);
更新 JSON 列
更新 JSON 列时,您应该使用->
更新 JSON 对象中适当键的语法。 MySQL 5.7+ 和 PostgreSQL 9.5+ 支持此操作:
$affected = 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']);
此外,您可以使用incrementEach
和decrementEach
方法:
DB::table('users')->incrementEach([
'votes' => 5,
'balance' => 100,
]);
删除语句
查询构建器的delete
方法可用于从表中删除记录。这delete
方法返回受影响的行数。你可以约束delete
在调用之前添加“where”子句的语句delete
方法:
$deleted = DB::table('users')->delete();
$deleted = DB::table('users')->where('votes', '>', 100)->delete();
如果您希望截断整个表,这将从表中删除所有记录并将自动递增 ID 重置为零,您可以使用truncate
方法:
DB::table('users')->truncate();
表截断和 PostgreSQL
截断 PostgreSQL 数据库时,CASCADE
行为将被应用。这意味着其他表中的所有外键相关记录也将被删除。
悲观锁
查询生成器还包括一些函数来帮助您在执行查询时实现“悲观锁定”select
声明。要执行带有“共享锁”的语句,您可以调用sharedLock
方法。共享锁可防止所选行在提交事务之前被修改:
DB::table('users')
->where('votes', '>', 100)
->sharedLock()
->get();
或者,您可以使用lockForUpdate
方法。 “for update”锁防止所选记录被修改或被另一个共享锁选中:
DB::table('users')
->where('votes', '>', 100)
->lockForUpdate()
->get();
Debugging
您可以使用dd
和dump
构建查询以转储当前查询绑定和 SQL 的方法。这dd
方法将显示调试信息,然后停止执行请求。这dump
方法将显示调试信息但允许请求继续执行:
DB::table('users')->where('votes', '>', 100)->dd();
DB::table('users')->where('votes', '>', 100)->dump();