我尝试在此查询中添加左连接,我一直用它来获取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', ...)
答案 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();