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

Laravel链式一对一关系:如何高效查询完整航班前后链?

高效获取完整航班链的方案

你当前的实现会触发N+1查询问题,每循环一次就发起一次数据库请求,航班链越长性能越差。下面是几种更高效的实现方式:

1. 数据库递归CTE(推荐,性能最优)

如果你的数据库支持递归公共表表达式(MySQL 8.0+/PostgreSQL/SQL Server等),可以用一次查询直接拉取整条航班链,这是效率最高的方案。

前置航班链查询示例

use Illuminate\Support\Facades\DB;

$flightId = $flight->id;

$flightsBefore = DB::withRecursive('flight_chain', function ($query) use ($flightId) {
    // 起始节点:当前航班的上一个航班
    $query->select('*')
          ->from('flights')
          ->where('id', $flight->linked_schedule_before_id)
          ->unionAll(function ($query) {
              // 递归关联:查询当前节点的上一个航班
              $query->select('f.*')
                    ->from('flights as f')
                    ->join('flight_chain as fc', 'f.id', '=', 'fc.linked_schedule_before_id');
          });
})->select('*')->from('flight_chain')->get();

封装成模型方法

可以在Flight模型里封装一个方法,方便复用:

public function getFlightsBefore()
{
    return DB::withRecursive('flight_chain', function ($query) {
        $query->select('*')
              ->from('flights')
              ->where('id', $this->linked_schedule_before_id)
              ->unionAll(function ($query) {
                  $query->select('f.*')
                        ->from('flights as f')
                        ->join('flight_chain as fc', 'f.id', '=', 'fc.linked_schedule_before_id');
              });
    })->select('*')->from('flight_chain')->get();
}

调用时直接用$flight->getFlightsBefore()即可。

2. 预加载+递归(兼容旧版数据库)

如果你的数据库不支持CTE,可以通过预加载减少查询次数,把N次查询压缩为2次:

// 先递归收集所有前置航班ID
$beforeIds = [];
$current = $flight->linked_schedule_before;
while ($current) {
    $beforeIds[] = $current->id;
    $current = $current->linked_schedule_before;
}

// 批量查询所有前置航班,再按链的顺序排序
$flightsBefore = Flight::whereIn('id', $beforeIds)
    ->get()
    ->sortBy(function ($item) use ($beforeIds) {
        return array_search($item->id, $beforeIds);
    });

3. 模型递归方法(辅助复用)

可以在Flight模型里添加辅助方法,简化链的获取,但注意需要配合预加载避免N+1查询:

public function allLinkedBefore()
{
    return $this->linked_schedule_before 
        ? $this->linked_schedule_before->allLinkedBefore()->prepend($this->linked_schedule_before) 
        : collect();
}

使用时需提前预加载递归关系:Flight::with('linked_schedule_before.allLinkedBefore')->find($flightId),但性能仍不如CTE或批量查询方案。


内容的提问来源于stack exchange,提问作者Matthew Dyson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 22:12:28