在Laravel查询/子查询构建器中添加左连接

时间:2016-10-06 19:43:48

标签: php mysql eloquent laravel-query-builder

我尝试在此查询中添加左连接,我一直用它来获取nPerGroup的相关记录。我在SQL中创建了查询,但不知道如何将其转换为Laravel查询构建器代码。

我想添加left join的原因是出于性能目的。当我从get-go中获取所有列时,查询需要太多的负载才能完成(在400k行上大约6秒),而left join只需要缩短半秒。

我尝试在left join之后添加mergeBindings,但是,我无法弄清楚如何在第一个选择中指定我需要的列。无论我尝试了什么,第一个选择始终保持为select *

这是我需要更改的Laravel范围代码:

public function scopeNPerGroup($query, $group, $n = 10, $columns)
{

    // queried table
    $table = ($this->getTable());

    // initialize MySQL variables inline
    $query->from(DB::raw("(SELECT @rank:=0, @group:=0) as vars, {$table}"));

    // if no columns already selected, let's select *
    if (! $query->getQuery()->columns && empty($columns)) {
        $query->select("{$table}.*");
    }
    elseif (!empty($columns)) {
        foreach ($columns as $column) {
            $query->addSelect($column);
        }
    }

    // make sure column aliases are unique
    $groupAlias = 'group_'.md5(time());
    $rankAlias  = 'rank_'.md5(time());

    // apply mysql variables
    $query->addSelect(DB::raw(
        "@rank := IF(@group = {$group}, @rank+1, 1) as {$rankAlias}, @group := {$group} as {$groupAlias}"
    ));

    // make sure first order clause is the group order
    $query->getQuery()->orders = (array) $query->getQuery()->orders;
    array_unshift($query->getQuery()->orders, ['column' => $group, 'direction' => 'asc']);

    // prepare subquery
    $subQuery = $query->toSql();

    // prepare new main base Query\Builder
    $newBase = $this->newQuery()
        ->from(DB::raw("({$subQuery}) as {$table}"))
        ->mergeBindings($query->getQuery())
        ->where($rankAlias, '<=', $n)
        ->getQuery();

    // replace underlying builder to get rid of previous clauses
    $query->setQuery($newBase);
}

这是以上代码生成的SQL:

SELECT * 
FROM   (SELECT `positions`.`id`, 
               `positions`.`keyword_id`, 
               `positions`.`position`, 
               @rank := IF(@group = keyword_id, @rank + 1, 1) AS 
               rank_fa9d7a6f55c38becc0b28f348651a856, 
               @group := keyword_id                           AS 
                      group_fa9d7a6f55c38becc0b28f348651a856 
        FROM   (SELECT @rank := 0, 
                       @group := 0) AS vars, 
               positions 
        ORDER  BY `keyword_id` ASC, 
                  `created_at` DESC) AS positions 
WHERE  `rank_fa9d7a6f55c38becc0b28f348651a856` <= '2' 
       AND `positions`.`keyword_id` IN ('1', '2', ...) 

这就是我需要它来生成的SQL :(只要它完成同样的事情就可以有所不同,这就是获取额外的positions.url列。)

SELECT `positionsA`.`id`, `positionsA`.`keyword_id`, `positionsA`.`position`, `positions`.`url`
FROM   (SELECT `positions`.`id`, 
               `positions`.`keyword_id`, 
               `positions`.`position`, 
               @rank := IF(@group = keyword_id, @rank + 1, 1) AS 
               rank_e2d9373d3bb35d6aabe9ffc57ff29c1c, 
               @group := keyword_id                           AS 
                      group_e2d9373d3bb35d6aabe9ffc57ff29c1c 
        FROM   (SELECT @rank := 0, 
                       @group := 0) AS vars, 
               positions 
        ORDER  BY `keyword_id` ASC, 
                  `created_at` DESC) AS positionsA 
LEFT JOIN `positions` on `positionsA`.`id` = `positions`.`id`
WHERE  `rank_e2d9373d3bb35d6aabe9ffc57ff29c1c` <= '2' 
       AND `positionsA`.`keyword_id` IN ('1', '2', ...) 

1 个答案:

答案 0 :(得分:0)

我认为你需要这样的事情:

$newBase = $this->newQuery()
    ->from(DB::raw("({$subQuery}) as {$table}A"))
    ->leftJoin('positions', 'positionsA.id', '=', 'positions.id')
    ->mergeBindings($query->getQuery())
    ->where($rankAlias, '<=', $n)
    ->getQuery();