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
部分更改为->whereRaw(DB::raw('a.created_at < b.created_at'))
DEMO
Laravel Eloquent select all rows with max created_at
【讨论】:
以上是关于Laravel Eloquent/DB 选择每组前 1 行的主要内容,如果未能解决你的问题,请参考以下文章