读操作用DB::select(),写操作用DB::insert/update/delete,DB::raw()仅嵌入构建器;均需参数绑定防注入,不支持命名绑定,返回原始数据或影响行数。

直接执行原生 SQL 语句在 Laravel 中不是“能不能”的问题,而是“用哪个方法、在哪种场景下更安全、更可控”的问题。核心结论:读操作优先用 DB::select(),写操作用 DB::insert()/DB::update()/DB::delete(),而需要嵌入构建器的片段(比如函数、计算字段、复杂条件)才用 DB::raw() 或 whereRaw()。
DB::select() 是 SELECT 原生查询的唯一推荐入口
它专为只读查询设计,返回 StdClass 对象数组,自动启用 PDO 预处理,强制参数绑定,天然防注入。
- 必须用问号占位符或命名绑定,不能拼接变量:
DB::select("SELECT * FROM users WHERE id = ?", [$id]) - 命名绑定需显式传入 PDO 选项才能生效:
DB::select("SELECT * FROM users WHERE name = :name", ['name' => 'Alice'], [PDO::ATTR_EMULATE_PREPARES => true]) - 不支持
DB::raw()包裹的字符串作为第一个参数——那会直接报错,因为DB::select()只接受纯字符串 SQL - 返回结果不走模型生命周期(
$casts、getAttributes()等全部失效),只是原始数据
DB::insert/update/delete 才是写操作的正确姿势
它们和 DB::select() 同属 DB 门面的原子方法,各自返回明确语义的结果(布尔值 / 影响行数),且都要求参数绑定,不接受字符串拼接。
-
DB::insert()不返回自增 ID;要取 ID 必须换用DB::table()->insertGetId()或手动调DB::getPdo()->lastInsertId() -
DB::update()和DB::delete()的 WHERE 条件若写错(比如漏条件、类型不匹配),可能误影响多行——务必先用DB::select()验证 WHERE 是否命中预期记录 - 批量插入不能靠多次
DB::insert()循环,性能差;应拼成单条INSERT INTO ... VALUES (),(),()再用DB::statement() - 所有方法都不支持命名绑定(
:name),只认问号占位符和顺序参数数组
DB::raw() 只能嵌入查询构建器,不能单独执行
DB::raw() 本质是标记一个字符串为“已信任的 SQL 片段”,绕过自动转义。它本身不执行任何 SQL,必须配合 select()、whereRaw()、orderByRaw() 等使用。
- 在
select()中:用DB::raw('COUNT(*) as total')计算字段,别名必须显式写出 - 在
whereRaw()中:可写whereRaw('created_at > DATE_SUB(NOW(), INTERVAL ? DAY)', [7]),参数绑定仍需手动传 - 误用典型:把整个 SQL 丢给
DB::raw("SELECT * FROM users"),然后试图执行——这不会运行,只会生成一个无意义的 RawExpression 对象 - 注意:
DB::raw()里的内容不校验语法,SQL 错了要到执行时才暴露,调试成本高
whereRaw() 是构建器里写复杂条件的开关,不是 SQL 注入后门
它解决的是 where() 表达不了的逻辑,比如数据库函数、多层 OR、JSON 字段提取等。但它的安全性完全取决于你是否绑定参数。
- 危险写法:
whereRaw("name = '" . $request->input('name') . "'")—— 直接引入 SQL 注入漏洞 - 安全写法:
whereRaw("name LIKE ? AND status IN (?)", ["%{$search}%", $statuses]) - MySQL 特有函数如
JSON_CONTAINS()、ST_Distance_Sphere()必须用whereRaw(),ORM 无法翻译 - 多个
whereRaw()连用时,注意 AND/OR 优先级;必要时用括号包裹整个条件片段
最常被忽略的一点:原生 SQL 查询默认不参与 Laravel 的查询日志上下文(DB::enableQueryLog() 能捕获,但 toSql() 对 DB::select() 无效)。如果要调试参数实际值,得靠 DB::listen() 或直接看数据库慢日志。写之前,先想清楚——这个逻辑真的不能用 Eloquent + fromRaw() 拆解?还是非得裸写整条 SQL?


















