Laravel 5.6迁移中DB::unprepared查询执行失败求助
这种在数据库工具里能正常执行,但迁移中毫无反应还没错误的情况,我之前也碰到过,大概率是几个容易忽略的细节问题,咱们一步步来排查:
1. 优先检查SQL逻辑的括号优先级
你原查询里的子WHERE条件有个致命的逻辑问题:OR条件没有加括号,导致整个过滤逻辑和你预期的不一样。
原条件是:
auditable_type = 'App\Protocol' AND event = 'updated_status' AND new_values LIKE '%pending irb specialist review%' OR auditable_type = 'App\Protocol' AND event = 'updated_status' AND new_values LIKE '%pending faculty sponsor approval%'
因为SQL里AND的优先级比OR高,实际执行逻辑会变成:(A AND B AND C) OR (D AND E AND F),这种写法很容易因为数据分布导致子查询返回空结果——这就是你UPDATE没效果的核心原因。
修正后的子查询WHERE条件应该把OR的两个LIKE用括号包起来:
auditable_type = 'App\Protocol' AND event = 'updated_status' AND ( new_values LIKE '%pending irb specialist review%' OR new_values LIKE '%pending faculty sponsor approval%' )
2. 避免转义字符带来的坑
你在PHP字符串里用了\'App\\Protocol\'这种转义写法,很容易因为转义错误导致实际传入数据库的字符串和你预期的不一致。建议用PHP的单引号HEREDOC语法来写SQL,这样内部的单引号、反斜杠都不需要额外转义,SQL可读性也更高:
DB::unprepared(<<<'SQL' UPDATE protocols AS P INNER JOIN ( SELECT auditable_id, updated_at FROM logs WHERE auditable_type = 'App\Protocol' AND event = 'updated_status' AND (new_values LIKE '%pending irb specialist review%' OR new_values LIKE '%pending faculty sponsor approval%') ) AS L ON L.auditable_id = P.id SET P.submitted_on = L.updated_at WHERE P.id = L.auditable_id SQL);
3. 调试方法:确认子查询是否有数据
迁移里的查询没报错但没效果,大概率是子查询没返回任何匹配的记录。你可以先在迁移里单独执行子查询,看看结果:
$matchingLogs = DB::select(<<<'SQL' SELECT auditable_id, updated_at FROM logs WHERE auditable_type = 'App\Protocol' AND event = 'updated_status' AND (new_values LIKE '%pending irb specialist review%' OR new_values LIKE '%pending faculty sponsor approval%') SQL); dd($matchingLogs); // 打印结果,看看有没有数据
如果这里是空数组,那就是过滤条件的问题;如果有数据,再执行UPDATE就应该能生效了。
另外,你也可以用PDO的exec方法来执行查询,它会返回受影响的行数,方便你确认是否真的有数据被更新:
$sql = <<<'SQL' UPDATE protocols AS P INNER JOIN ( SELECT auditable_id, updated_at FROM logs WHERE auditable_type = 'App\Protocol' AND event = 'updated_status' AND (new_values LIKE '%pending irb specialist review%' OR new_values LIKE '%pending faculty sponsor approval%') ) AS L ON L.auditable_id = P.id SET P.submitted_on = L.updated_at WHERE P.id = L.auditable_id SQL; $affectedRows = DB::getPdo()->exec($sql); dd("受影响的行数:{$affectedRows}");
4. 迁移事务的影响(可选)
Laravel的迁移默认是在事务中执行的,如果你的查询因为某些原因(比如锁表、隔离级别)导致事务默默提交但没生效,可以尝试在迁移类里关闭事务:
class UpdateProtocolSubmittedOn extends Migration { public $transactions = false; // 关闭事务 public function up() { // 你的查询代码 } // ... }
按照上面的步骤排查,应该就能解决问题了,我之前遇到类似的情况,就是括号优先级的问题导致子查询空结果,修正后就正常了。
内容的提问来源于stack exchange,提问作者Chapman

