查询生成器 - 结合 orderBy 和 groupBy

Query builder - combining orderBy and groupBy

我有一个 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' 因此 return 每个公司的最后 "live" 个条目。在上面的示例中,这应该 return

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

我尝试使用查询生成器来完成此操作,例如:

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

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

ORDER BY date and time BEFORE GROUP BY name in mysql

但是,我无法将这种方法应用到我的问题中。您能否向我解释如何有效地解决此问题,无论是使用子查询还是使用其他方法,但又不求助于原始查询?

您当前针对所需输出的策略是正确的,不需要子查询。以下原始 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 进行查询并且 table 中的基础数据没有改变,那么查询结果应该是完全可重现的。

编辑:

与您的问题相反,您的评论表明您想要每个公司的每个最大日期的整个匹配行。如果是这样,那么您真的需要以下原始 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();

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

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