如何使用Laravel查询生成器从子查询中选择?


102

我想通过下面的SQL使用Eloquent ORM来获得价值。

-SQL

 SELECT COUNT(*) FROM 
 (SELECT * FROM abc GROUP BY col1) AS a;

然后,我考虑了以下内容。

-代码

 $sql = Abc::from('abc AS a')->groupBy('col1')->toSql();
 $num = Abc::from(\DB::raw($sql))->count();
 print $num;

我正在寻找更好的解决方案。

请告诉我最简单的解决方案。

Answers:


131

除了@delmadord的答案和您的评论:

当前没有在FROM子句中创建子查询的方法,因此您需要手动使用raw语句,然后,如有必要,将合并所有绑定:

$sub = Abc::where(..)->groupBy(..); // Eloquent Builder instance

$count = DB::table( DB::raw("({$sub->toSql()}) as sub") )
    ->mergeBindings($sub->getQuery()) // you need to get underlying Query Builder
    ->count();

请注意,您需要以正确的顺序合并绑定。如果还有其他绑定子句,则必须将它们放在mergeBindings

$count = DB::table( DB::raw("({$sub->toSql()}) as sub") )

    // ->where(..) wrong

    ->mergeBindings($sub->getQuery()) // you need to get underlying Query Builder

    // ->where(..) correct

    ->count();

3
请注意,如果您将复杂查询作为子belongsToMany选择,则必须添加getQuery()两次=>$sub->getQuery()->getQuery()
JordiPuigdellívol,2015年

1
@Skyzer你没有读我写的东西。打电话时,什么也没逃脱toSql。阅读有关PDO php.net/manual/en/book.pdo.php的信息,并查看您的结果$query->toSql()
Jarek Tkaczyk

5
关于-> mergeBindings($ sub-> getQuery()),只需 -> mergeBindings($ sub)
Jimmy Ilenloa

1
@JimmyIlenloa如果$sub查询是Eloquent Builder,那么您仍然需要该->getQuery()部件,否则会出错,因为此方法是针对Query\Builder类进行类型化提示的。
Jarek Tkaczyk 2015年

1
@Kannan不。我猜这是PR的候选人,但最后这不是很常见的用例。也许这是不是有它存在到今天的原因..
亚雷克Tkaczyk

76

添加了Laravel v5.6.12(2018-03-14)fromSub()fromRaw()查询生成器(#23476)的方法

可接受的答案是正确的,但可以简化为:

DB::query()->fromSub(function ($query) {
    $query->from('abc')->groupBy('col1');
}, 'a')->count();

上面的代码片段产生以下SQL:

select count(*) as aggregate from (select * from `abc` group by `col1`) as `a`

15

@JarekTkaczyk的解决方案正是我想要的。我唯一想念的是在使用DB::table()查询时该怎么做 。在这种情况下,这就是我的做法:

$other = DB::table( DB::raw("({$sub->toSql()}) as sub") )->select(
    'something', 
    DB::raw('sum( qty ) as qty'), 
    'foo', 
    'bar'
);
$other->mergeBindings( $sub );
$other->groupBy('something');
$other->groupBy('foo');
$other->groupBy('bar');
print $other->toSql();
$other->get();

特别注意mergeBindings不使用getQuery()方法的制作方法


使用DB::raw()确实为我完成了工作
NinoŠkopac'19

7

从laravel 5.5开始,有一种专用的子查询方法,您可以像这样使用它:

Abc::selectSub(function($q) {
    $q->select('*')->groupBy('col1');
}, 'a')->count('a.*');

要么

Abc::selectSub(Abc::select('*')->groupBy('col1'), 'a')->count('a.*');

1
看来subSelect只能用于将子查询添加到SELECT,而不能用于FROM。
hagabaka

1
Call to undefined method subSelect()似乎subSelect不存在。
Maruf Alom

3
感谢您将此通知我,我错了名字了,应该是selectSub。我已经更新了我的回复。
Sasa Blagojevic

3

我喜欢做这样的事情:

Message::select('*')
->from(DB::raw("( SELECT * FROM `messages`
                  WHERE `to_id` = ".Auth::id()." AND `isseen` = 0
                  GROUP BY `from_id` asc) as `sub`"))
->count();

它不是很优雅,但是很简单。


谢谢,这对我有用,作为一个旁注,请小心选择内容,因为laravel添加了一些引号,并且我不得不使用-> select(\ DB :: raw('Your select'))摆脱它们。
Wak

2

我无法让您的代码执行所需的查询,AS仅是表的别名abc,而不是派生表的别名。Laravel查询生成器不隐式支持派生表别名,为此很可能需要DB :: raw。

我能想到的最直接的解决方案几乎与您的解决方案相同,但是会根据您的要求生成查询:

$sql = Abc::groupBy('col1')->toSql();
$count = DB::table(DB::raw("($sql) AS a"))->count();

产生的查询是

select count(*) as aggregate from (select * from `abc` group by `col1`) AS a;

谢谢您的回复。“ Abc :: from(???)和DB :: table(???)”的方法存在问题。$ sql = Abc :: where('id','=',$ id)-> groupBy('col1')-> toSql(); $ count = DB :: table(DB :: raw(“(($ sql)AS a”))-> count(); 上面的代码中发生SQL错误。-在哪里和参数分配!
2014年

2

此答案中描述的正确方法:https : //stackoverflow.com/a/52772444/2519714 当前最受欢迎的答案并不完全正确。

这种方式https://stackoverflow.com/a/24838367/2519714在某些情况下是不正确的:子选择具有where绑定,然后将表联接到子选择,然后将其他位置添加到所有查询。例如查询: select * from (select * from t1 where col1 = ?) join t2 on col1 = col2 and col3 = ? where t2.col4 = ? 要进行此查询,您将编写如下代码:

$subQuery = DB::query()->from('t1')->where('t1.col1', 'val1');
$query = DB::query()->from(DB::raw('('. $subQuery->toSql() . ') AS subquery'))
    ->mergeBindings($subQuery->getBindings());
$query->join('t2', function(JoinClause $join) {
    $join->on('subquery.col1', 't2.col2');
    $join->where('t2.col3', 'val3');
})->where('t2.col4', 'val4');

在执行此查询期间,他的方法$query->getBindings()将以不正确的顺序返回绑定,例如['val3', 'val1', 'val4']在这种情况下,它会['val1', 'val3', 'val4']针对上述原始sql 正确。

一种更正确的时间执行此操作:

$subQuery = DB::query()->from('t1')->where('t1.col1', 'val1');
$query = DB::query()->fromSub($subQuery, 'subquery');
$query->join('t2', function(JoinClause $join) {
    $join->on('subquery.col1', 't2.col2');
    $join->where('t2.col3', 'val3');
})->where('t2.col4', 'val4');

绑定也将自动正确地合并到新查询中。


非常感谢!它帮助很大!
Hasnat Babur
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.