虽然 Laravel 的查询构建器和 Eloquent ORM 提供了丰富的数据库操作方法,但有时你仍然需要直接执行原生 SQL 语句,比如使用数据库特定的函数、复杂查询或优化性能。
Laravel 的 DB Facade 提供了多种方法来安全地执行原生 SQL,并支持参数绑定以防止 SQL 注入。
JSON_EXTRACT、PostgreSQL 的 tsvector)。INSERT ... SELECT)。首先,导入 Illuminate\Support\Facades\DB 门面:
use Illuminate\Support\Facades\DB;
然后,可以通过 DB:: 调用各种方法执行 SQL。
DB::select() 方法用于执行 SELECT 查询,返回一个数组,每个元素是 stdClass 对象:
$users = DB::select('SELECT * FROM users WHERE active = ?', [1]);
foreach ($users as $user) {
echo $user->name;
}
你也可以使用命名绑定:
$users = DB::select('SELECT * FROM users WHERE age > :age', ['age' => 18]);
如果不需要参数,可以省略第二个参数:
$users = DB::select('SELECT * FROM users');
DB::insert() 执行 INSERT 语句,返回布尔值表示是否成功:
$inserted = DB::insert('INSERT INTO users (name, email) VALUES (?, ?)', ['John', 'john@example.com']);
如果需要获取自增 ID,可以使用 DB::getPdo()->lastInsertId():
DB::insert('INSERT INTO users (name, email) VALUES (?, ?)', ['John', 'john@example.com']);
$id = DB::getPdo()->lastInsertId();
DB::update() 执行更新,返回受影响的行数:
$affected = DB::update('UPDATE users SET age = ? WHERE id = ?', [30, 1]);
DB::delete() 执行删除,返回受影响的行数:
$deleted = DB::delete('DELETE FROM users WHERE active = 0');
DB::statement() 用于执行不返回结果集的 SQL,如 CREATE TABLE、ALTER、DROP 等:
DB::statement('DROP TABLE temp_users');
也可用于执行存储过程:
DB::statement('CALL refresh_user_stats(?)', [1]);
上述方法都支持参数绑定,可以有效防止 SQL 注入。请务必使用问号占位符或命名占位符,切勿直接拼接用户输入:
// ❌ 危险!不要这样做
DB::select("SELECT * FROM users WHERE name = '$name'");
// ✅ 正确:使用参数绑定
DB::select('SELECT * FROM users WHERE name = ?', [$name]);
DB::raw() 或直接拼接字符串会绕过参数绑定,除非你完全确定内容安全,否则强烈推荐使用绑定。
当需要确保多个 SQL 操作原子性时,可以使用事务。Laravel 提供了简洁的事务方法:
DB::transaction(function () {
DB::update('UPDATE accounts SET balance = balance - 100 WHERE id = 1');
DB::update('UPDATE accounts SET balance = balance + 100 WHERE id = 2');
});
如果闭包内抛出异常,事务会自动回滚;否则自动提交。
DB::beginTransaction();
try {
DB::update('UPDATE accounts SET balance = balance - 100 WHERE id = 1');
DB::update('UPDATE accounts SET balance = balance + 100 WHERE id = 2');
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
throw $e;
}
调试时可以监听所有执行的 SQL:
DB::enableQueryLog();
DB::select('SELECT * FROM users');
DB::update('UPDATE users SET age = ? WHERE id = ?', [30, 1]);
$queries = DB::getQueryLog();
foreach ($queries as $query) {
echo $query['query'] . ' 绑定的参数: ' . implode(', ', $query['bindings']) . "\n";
}
在服务提供者中注册监听器,可以持久记录所有查询:
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
public function boot()
{
DB::listen(function ($query) {
Log::info($query->sql, $query->bindings);
});
}
批量插入(使用原生 SQL 提升性能)
$chunks = collect($data)->chunk(1000);
foreach ($chunks as $chunk) {
$values = implode(',', array_fill(0, count($chunk), '(?, ?)'));
$params = [];
foreach ($chunk as $item) {
$params[] = $item['name'];
$params[] = $item['email'];
}
DB::insert("INSERT INTO users (name, email) VALUES {$values}", $params);
}
调用存储过程
DB::statement('CALL update_user_rank(?, ?)', [$userId, $newRank]);
执行复杂查询并返回结果
$reports = DB::select('
SELECT department, COUNT(*) as employee_count, AVG(salary) as avg_salary
FROM employees
WHERE hire_date >= ?
GROUP BY department
HAVING employee_count > ?
', ['2023-01-01', 5]);
| 特性 | 查询构建器 | 原生 SQL |
|---|---|---|
| 可读性 | 高,链式方法 | 取决于 SQL 复杂度 |
| SQL 注入防护 | 自动参数绑定 | 需手动绑定 |
| 数据库无关性 | 高,自动转换语法 | 低,需适配不同数据库 |
| 性能 | 中等(额外解析) | 直接执行,可能更优 |
| 适用场景 | 常规 CRUD | 复杂查询、存储过程、批量操作 |
empty($result) 判断是否有数据。
$allowedTables = ['users', 'orders']; if (in_array($table, $allowedTables)) { DB::select("SELECT * FROM `$table`"); }。
DB::listen 事件,每个查询对象包含 time 属性(以毫秒为单位)。或者使用 DB::enableQueryLog() 后查看日志中的时间。
Laravel 允许你通过 DB Facade 灵活执行原生 SQL,同时提供了参数绑定、事务和日志功能来保障安全性和可调试性。
掌握这些方法,可以在需要高性能或复杂数据库操作时游刃有余。但请记住,保持代码清晰和可维护性同样重要,不要滥用原生 SQL。
下一章我们将学习 Eloquent ORM 的基础,体验 Laravel 中最优雅的数据库交互方式。