Laravel Eloquent/DB 选择每组前 1 行

Posted

技术标签:

【中文标题】Laravel Eloquent/DB 选择每组前 1 行【英文标题】:Laravel Eloquent/DB select top 1 Row Per Group 【发布时间】:2020-12-16 14:58:53 【问题描述】:

我的控制器中有一个 SQL 查询,它从表中获取所有数据以按日期对其进行排序。表中有一些重复的 sku_parent 条目具有不同的 id,我想根据“日期”获取每个 sku_parent 的最新条目。这是我的数据库:

+----+-----------+------------+-----------+--------------+------------+---------------------+---------------------+
| id | warehouse | sku_parent | sku_child | case_balance |    date    |     created_at      |     updated_at      |
+----+-----------+------------+-----------+--------------+------------+---------------------+---------------------+
|  5 | SeaWest   |        120 | 120 - 25  |          400 | 2020-08-26 | 2020-08-26 19:02:20 | 2020-08-26 19:02:20 |
|  6 | SeaWest   |        121 | 121 - 25  |          784 | 2020-08-26 | 2020-08-26 19:02:42 | 2020-08-26 19:02:42 |
|  7 | SeaWest   |        121 | 121 - 25  |          734 | 2020-08-26 | 2020-08-26 19:03:46 | 2020-08-26 19:03:46 |
|  8 | SeaWest   |        120 | 120 - 25  |          350 | 2020-08-26 | 2020-08-26 19:03:46 | 2020-08-26 19:03:46 |
+----+-----------+------------+-----------+--------------+------------+---------------------+---------------------+

这是我到目前为止所做的:

    $data = DB::table('logs')
           ->where('sku_parent', $request->sku)
           ->where('id', \DB::raw("(select max(`id`) from logs)"))
           ->whereBetween('date', array($request->from_date, $request->to_date))
           ->get();

此代码仅从表中获取最大 id,不显示其他产品名称。我想让每个产品名称都具有最大 id。

【问题讨论】:

按作品大概是指只返回两行,因为它没有返回合理的数据。您的第一行显示 ID 为 8,case_balance 为 400,但 ID 为 8 的行的 case_balance 实际上是 350。您的问题仍然有效,即如何在 laravel 中获得每组的前 1 个(其中我不知道,我从未使用过它),但您绝对不应该尝试重新创建该查询。我以前explained with an example 为什么(除非你知道你在做什么)你应该避免这个 mysql 扩展 group by 子句 感谢您提及,我不会使用该查询,有什么方法可以获取我想要的数据吗? 您使用的是哪个版本的 MySQL? 使用 php my admin 7.4.7 我认为下面的查询对你有用:SELECT ID, sku_parent, sku_child, case_balance , date , created_at, updated_at FROM logs WHERE ID IN (SELECT MAX(ID) FROM logs GROUP BY sku_parent),不过正如我所说,我没有 Laravel 的经验,所以不能为你翻译。一般的原则是获取top 1 per group,所以可能有基于该搜索的答案? 【参考方案1】:

您可以先将它们全部按sku_parent 分组,而不是搜索某个项目的MAX id,并且只获取与havingRaw 重复值的最新记录。试试这样的:

$data = DB::table('logs')
       ->where('sku_parent', $request->sku)
       ->groupBy('sku_parent')
       ->havingRaw('count('sku_parent') > 1')
       ->whereBetween('date', array($request->from_date, $request->to_date))
       ->latest()
       ->get();

PS:还没有测试过,但应该会给出想要的结果。如果出现任何错误,或者不是您想要的,请联系我。

【讨论】:

语法错误,意外的 'sku_parent' (T_STRING),期待 ')'【参考方案2】:

要为每个组 (sku_parent) 选择最新的一行,您可以像这样进行自我加入

架构

CREATE TABLE logs
    (`id` int, `warehouse` varchar(7), `sku_parent` int, `sku_child` varchar(8), `case_balance` int, `date` datetime, `created_at` datetime, `updated_at` datetime)
;
    
INSERT INTO logs
    (`id`, `warehouse`, `sku_parent`, `sku_child`, `case_balance`, `date`, `created_at`, `updated_at`)
VALUES
    (5, 'SeaWest', 120, '120 - 25', 400, '2020-08-26 00:00:00', '2020-08-26 19:02:20', '2020-08-26 19:02:20'),
    (6, 'SeaWest', 121, '121 - 25', 784, '2020-08-26 00:00:00', '2020-08-26 19:02:42', '2020-08-26 19:02:42'),
    (7, 'SeaWest', 121, '121 - 25', 734, '2020-08-26 00:00:00', '2020-08-26 19:03:46', '2020-08-26 19:03:46'),
    (8, 'SeaWest', 120, '120 - 25', 350, '2020-08-26 00:00:00', '2020-08-26 19:03:46', '2020-08-26 19:03:46')
;

查询

select a.*
from logs a
left join logs b on a.sku_parent = b.sku_parent
    and a.id < b.id
where b.sku_parent is null
    and a.sku_parent = :sku_parent
    and a.date between :from_date and :to_date

查询生成器

DB::table('logs as a')
  ->select('a.*')
  ->leftJoin('logs as b', function ($join) 
        $join->on('a.sku_parent', '=', 'b.sku_parent')
             ->whereRaw(DB::raw('a.id < b.id'));
   )
  ->whereNull('b.sku_parent')
  ->where('a.sku_parent', $request->sku)
  ->whereBetween('a.date', array($request->from_date, $request->to_date))
  ->get();

如果您需要根据created_at 选择最新记录,请将leftJoin 部分更改为-&gt;whereRaw(DB::raw('a.created_at &lt; b.created_at'))

DEMO

Laravel Eloquent select all rows with max created_at

【讨论】:

以上是关于Laravel Eloquent/DB 选择每组前 1 行的主要内容,如果未能解决你的问题,请参考以下文章

选择每组前 N 个项目 ms-Access - MORE "Ns"

sql分组后取每组前三

每组前 3 名,包括 0

每组前 N 个(MSSQL)[重复]

mysql每组前N条

Laravel/Eloquent/DB 查询 - 当为空且不为空时加入