You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:20:02