简体   繁体   中英

PHP Eloquent flattening nested `whereHas` selectors?

I got tasked with optimizing some old php project for a new company I work at. Some api call takes almost 20 seconds to do, which is way too long, and I tracked the problem down to some sql generated in the project. It has several nested exists ', and was (I believe) generated by this code:

return Workstation::whereHas('productionSteps', function ($qp) {
    $qp->whereHas('subSteps', function ($qs) {
        $qs->whereHas('position', function ($qpr) {
            $qpr->where('ProjectId', $this->id);
        });
    });
})->get();

the resulting query is:

SELECT *
from `workstations`
where EXISTS (
    select * 
    from `productionsteps` 
    where `productionsteps`.`UsedWorkStationId` = `workstations`.`Id` 
    and exists (
        select * 
        from `substeps` 
        where `substeps`.`ParentStepId` = `productionsteps`.`Id` 
        and exists (
                select * 
            from `positions` 
            where `substeps`.`PositionId` = `positions`.`Id` 
            and `ProjectId` = "some id"
        )
    )
);

So I optimized the query to only use one exists

SELECT *
from `workstations`
where EXISTS (
    select * 
    from `productionsteps`, `substeps`, `positions`
    where `productionsteps`.`UsedWorkStationId` = `workstations`.`Id` 
    AND `substeps`.`ParentStepId` = `productionsteps`.`Id` 
    and `substeps`.`PositionId` = `positions`.`Id` 
    and `ProjectId` = "some id"
);

However, I'm unsure as how to update the php code.

So how do I do this in php?

This would be my best guess, judging from the documentation:

return Workstation::whereExists(function($query) {
    $query
        ->join('productionsteps', 'UsedWorkStationId', '=', 'workstations.Id')
        ->join('substeps', 'ParentStepId', '=', 'productionsteps.Id')
        ->join('positions', 'PositionId', 'positions.Id')
        ->where('ProjectId', $this->id);
})->get();

I don't know the eloquent way to do that; Since there are many tables you're working with though I'd use a broad select instead:

DB::select("
SELECT *
from `workstations`
where EXISTS (
    select * 
    from `productionsteps`, `substeps`, `positions`
    where `productionsteps`.`UsedWorkStationId` = `workstations`.`Id` 
    AND `substeps`.`ParentStepId` = `productionsteps`.`Id` 
    and `substeps`.`PositionId` = `positions`.`Id` 
    and `ProjectId` = ?
);
", ['some id']); // array is for prepared parameters binding

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM