如何在Laravel(PHP)中避免嵌套查询?含数据表及现有代码
优化游戏会话与游戏数据的查询效率
你当前的代码存在N+1查询问题:先查询所有会话(1次查询),然后为每个会话单独查询关联的游戏(N次查询,N是会话数量),这会导致数据库请求次数过多,性能随着会话数量增加而明显下降。下面提供两种更高效的解决方案:
方案一:使用Laravel Eloquent关联(推荐)
如果还没定义模型,先创建两个Eloquent模型并建立关联关系,这是Laravel中最符合规范的做法:
1. 定义模型
// app/Models/GameSession.php namespace App\Models; use Illuminate\Database\Eloquent\Model; class GameSession extends Model { protected $table = 'game_sessions'; protected $primaryKey = 'Id'; public $timestamps = false; // 若表没有created_at/updated_at字段需添加 // 关联关系:一个会话拥有多个游戏 public function games() { return $this->hasMany(Game::class, 'GamesSessionId', 'Id'); } }
// app/Models/Game.php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Game extends Model { protected $table = 'games'; protected $primaryKey = 'Id'; public $timestamps = false; // 关联关系:一个游戏属于一个会话(可选,按需添加) public function session() { return $this->belongsTo(GameSession::class, 'GamesSessionId', 'Id'); } }
2. 预加载关联数据
使用with()方法一次性加载所有会话及其关联的游戏,仅需2次查询(一次查会话,一次查游戏,Laravel自动处理关联匹配):
use App\Models\GameSession; $sessions = GameSession::select('Id as sessionId', 'SessionName') ->with(['games' => function($query) { $query->select('Id as GameId', 'GameName', 'GamesSessionId'); }]) ->get(); // 调整结果格式,和原有结构保持一致 $result = $sessions->map(function($session) { $session->Games = $session->games; unset($session->games); // 可选,移除原关联属性 return $session; }); return ['stat' => 'success', 'data' => $result];
方案二:使用JOIN查询(无需模型)
如果不想使用Eloquent模型,可以通过一次JOIN查询获取所有数据,然后在PHP中手动分组:
use Illuminate\Support\Facades\DB; // 一次查询获取所有会话及关联游戏 $results = DB::select(' SELECT gs.Id as sessionId, gs.SessionName, g.Id as GameId, g.GameName FROM game_sessions gs LEFT JOIN games g ON gs.Id = g.GamesSessionId '); // 手动将游戏分组到对应会话 $sessions = []; foreach($results as $row) { $sessionId = $row->sessionId; if (!isset($sessions[$sessionId])) { $sessions[$sessionId] = (object)[ 'sessionId' => $sessionId, 'SessionName' => $row->SessionName, 'Games' => [] ]; } if ($row->GameId) { // 处理没有关联游戏的会话 $sessions[$sessionId]->Games[] = (object)[ 'GameId' => $row->GameId, 'GameName' => $row->GameName ]; } } // 转为数组格式输出 $sessions = array_values($sessions); return ['stat' => 'success', 'data' => $sessions];
为什么这两种方案更高效?
- 方案一:仅需2次数据库查询(无论会话数量多少),Laravel会在内存中自动完成会话与游戏的关联匹配。
- 方案二:仅需1次数据库查询,后续分组逻辑在PHP内存中完成,大幅减少数据库交互次数,提升整体性能。
内容的提问来源于stack exchange,提问作者Joyner
相关产品推荐
相关产品推荐

