查询构建器 - 将orderBy和groupBy

时间:2018-01-17 13:35:24

标签: php mysql laravel eloquent laravel-eloquent

我有一个Laravel应用程序,我有一个应该按如下方式运行的查询。

company   date      period
apple | 2017-08-23 | live
apple | 2017-09-04 | live
apple | 2014-03-04 | history
enron | 1987-09-09 | history
tesla | 2017-07-04 | live
tesla | 2017-06-03 | live

它需要按日期降序对每个公司的条目进行排序,条件为period ='live',从而返回每个公司的最后一个“实时”条目。在上面的例子中,这应该返回

company   date 
apple | 2017-09-04
tesla | 2017-07-04

我试图使用查询构建器完成此操作,如下所示:

return $query->where('period', '=', 'live')
        ->groupBy('company')
        ->orderBy('date','desc');

然而,这首先对结果进行分组,然后对它们进行排序,这会产生不可预测的结果。根据这个问题/答案,答案在于子查询:

ORDER BY date and time BEFORE GROUP BY name in mysql

但是,我无法将这种方法纳入我的问题。您是否可以向我解释如何有效地解决此问题,无论是使用子查询还是使用不同的方法,而不是诉诸原始查询?

3 个答案:

答案 0 :(得分:1)

您当前所需输出的策略是正确的,并且您不需要子查询。以下原始MySQL查询应该足够了:

SELECT company, MAX(date)
FROM companies
GROUP BY company

我们可以尝试以下Laravel代码:

return $query->where('period', '=', 'live')
    ->groupBy('company')
    ->orderBy('date','desc')
    ->get(['company', DB::raw('MAX(date) AS max_date')]);

与您在问题中所说的相反,如果您使用ORDER BY查询并且表中的基础数据没有更改,那么查询结果应该是完全可重现的。

修改

与您的问题相反,您的评论表明您希望每个公司的每个最大日期都有匹配的整行。如果是这样,那么你真的想要以下原始MySQL查询:

SELECT c1.*
FROM companies c1
INNER JOIN
(
    SELECT company, MAX(date) AS max_date
    FROM companies
    GROUP BY company
) c2
    ON c1.company = c2.company AND
       c1.date = c2.max_date

我们可以尝试编写以下Laravel代码来处理这个问题:

DB::table('companies')
    ->select('*')
    ->join(DB::raw('(SELECT company, MAX(date) AS max_date
                     FROM companies GROUP BY company) c2'), function($join)
        {
            $join->on('companies.company', '=', 'c2.company');
            $join->on('companies.date', '=', 'c2.max_date');
        })
    ->orderBy('companies.date', 'DESC')
    ->get();

答案 1 :(得分:1)

另一个解决方案可能是简单groupBy()已排序的查询结果(https://laravel.com/docs/5.5/collections#method-groupby) 它完全取决于结果数据集的大小

答案 2 :(得分:0)

https://laravel.com/docs/5.5/queries#retrieving-results看看这个链接。你应该使用DB :: table('company') - > where('period','live') - > groupBy('your condition') - > orderBy('your condition') - > get( ); 它将返回StdClass对象。如果你想要一个数组在get()之后添加toArray(); 希望它有效,让我知道。