DB::select()仅用于SELECT查询,返回stdClass对象数组,不支持INSERT/UPDATE/DELETE;参数绑定只支持?或命名占位符,字段值全为字符串,时间不转Carbon;语法错误抛PDOException。

DB::select() 适合查数据,但不返回模型实例
想直接拿原生 SQL 的结果?DB::select() 是最常用的入口,它底层调用 PDO::query,返回的是 stdClass 对象数组(不是 Eloquent 模型)。适合做报表、聚合统计或跨表复杂查询。
常见错误是误以为它能触发模型事件或自动转换类型——它不会。字段值全是字符串,时间字段也不会转成 Carbon 实例。
- 用
?占位符防注入:DB::select("SELECT * FROM users WHERE status = ? AND created_at > ?", ['active', '2024-01-01']) - 命名参数也支持:
DB::select("SELECT * FROM users WHERE id = :id", ['id' => 123]) - 如果 SQL 有语法错误,会抛出
PDOException,不是 Laravel 的QueryException—— 注意异常捕获层级
DB::statement() 和 DB::insert()/update()/delete() 的分工
DB::statement() 用于执行不返回结果集的语句,比如 CREATE TABLE、ALTER、带 INSERT ... ON DUPLICATE KEY UPDATE 的复合语句。它返回布尔值,不代表影响行数。
而 DB::insert()、DB::update()、DB::delete() 这三个函数才真正返回影响行数(int),且内部做了预处理和参数绑定,比裸写 DB::statement() 更安全。
-
DB::insert("INSERT INTO users (name, email) VALUES (?, ?)", ['John', 'john@example.com'])→ 返回true或false -
DB::update("UPDATE users SET status = ? WHERE id = ?", ['inactive', 456])→ 返回受影响行数(如1) -
DB::statement("DROP TABLE IF EXISTS temp_log")→ 不关心返回值,只确保执行
DB::unprepared() 是唯一能跑多语句的方案
MySQL 默认禁用多语句执行(mysqli.multi_query 关闭),所以用 DB::select() 或 DB::statement() 直接传入分号分隔的 SQL(如 "SELECT 1; SELECT 2;")会报错:SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active。
DB::unprepared() 绕过 PDO 的预处理限制,直接发送原始字符串到 MySQL,适用于初始化脚本、存储过程创建等场景。但它不支持参数绑定,必须手动过滤输入。
- 安全前提下可用:
DB::unprepared("SET @rank := 0; SELECT @rank := @rank + 1 as rank, name FROM users ORDER BY score DESC") - 绝不能拼接用户输入:
DB::unprepared("DROP TABLE " . $table_name)是高危操作 - 该方法在 PostgreSQL 或 Sqlite 下行为不一致,别跨数据库假设它可用
事务里混用原生 SQL 和 Eloquent 可能丢事务一致性
在 DB::transaction() 块里,如果你一边用 User::create(),一边用 DB::update(),看起来都在同一个事务里——但要注意:Eloquent 默认使用主连接,而你若在配置里定义了读写分离,DB::update() 可能走的是写连接,DB::select() 却默认走读连接,导致事务失效。
- 显式指定连接:
DB::connection('mysql')->update(...)确保和 Eloquent 同源 - 避免在事务中调用
DB::select()去查刚插入的数据——除非加DB::connection()->getPdo()->setAttribute(PDO::ATTR_EMULATE_PREPARES, true),否则可能读不到未提交变更 - 更稳妥的做法:事务内只用 Eloquent,或全部用原生 DB 类,不要混用抽象层级
原生 SQL 最容易被忽略的其实是连接复用和字符集隐式转换。比如 MySQL 连接默认 utf8mb4,但你在 DB::select() 里传入含 emoji 的参数,没设好 PDO 的 charset 选项,就可能存成乱码——这问题不会报错,只会静默损坏数据。











