MySQL 抓取数据组中的最后一行,但特定于列值

huangapple go评论100阅读模式
英文:

MySQL grab last row of a group of data but specific to a column value

问题

我们正在使用Laravel开发一个系统,这意味着我们正在使用Laravel ORM。我们有一个类似于这样的表格:

  1. table: approvals
  2. +----+-------------------------+--------+----------+----------+------+----------+
  3. | id | app_model | app_id | group_id | revision | year | status |
  4. +----+-------------------------+--------+----------+----------+------+----------+
  5. | 1 | App\Models\AApplication | 4 | 500 | (NULL) | 2021 | APPROVED |
  6. | 2 | App\Models\AApplication | 95 | 501 | (NULL) | 2022 | APPROVED |
  7. | 3 | App.Models\BApplication | 22 | 90 | 1 | 2019 | APPROVED |
  8. | 4 | App.Models\BApplication | 37 | 90 | 2 | 2020 | APPROVED |
  9. | 5 | App\Models\AApplication | 125 | 501 | 1 | 2023 | APPROVED |
  10. | 6 | App.Models\BApplication | 89 | 90 | 3 | 2024 | DRAFT |
  11. +----+-------------------------+--------+----------+----------+------+----------+

我们正在使用Laravel的morphTo()关系来获取关联表格数据:

  1. public function app() {
  2. return $this->morphTo(__FUNCTION__, 'app_model', 'app_id');
  3. }

我尝试呈现一些数据,以便提供有关此表格中数据维度的一些上下文。我们在这个approvals表中保留了不同应用表的批准。一个机构可以根据每个申请获取修订批准多次。批准可以最初是'草稿',后来变成'批准'。

👉 我们需要获取组(group_id)中状态为'批准'的最后一次修订行。

  1. 批准UI表格
  2. +-------+----------+--------+
  3. | GROUP | REVISION | ACTION |
  4. +-------+----------+--------+
  5. | 500 | - | [查看] |
  6. | 501 | 1 | [查看] |
  7. | 90 | 👁 2 | [查看] |
  8. +-------+----------+--------+

我们已经在Laravel ORM中制定了以下MySQL查询来实现这一目标:

  1. // $userId = 获取当前用户的ID;
  2. $query = DB::table('approvals')
  3. ->with(['app'])
  4. ->whereNotIn('id', function ($query) {
  5. $query->from('approvals as t1')
  6. ->crossJoin('approvals as t2')
  7. ->whereColumn('t1.id', '<', 't2.id')
  8. ->whereColumn('t1.group_id', '=', 't2.group_id')
  9. ->whereColumn('t1.app_model', '=', 't2.app_model')
  10. ->select('t1.id');
  11. });
  12. // 仅显示属于该特定用户的批准。
  13. $query = $query->whereRelation('app', 'created_by', '=', $userId);
  14. $query = $query->orderBy('approvals.id', 'DESC');
  15. $query = $query->where('approvals.status', 'APPROVED'); // &lt;-------- 🚩 此处存在问题
  16. return $query;

如果我们从查询中注释掉'APPROVED'条件,我们可以获取每个组的最后一行。问题是,组ID 90 的最后一行不是'批准',但它会出现在表格中,这不是我们想要的。

👉 我们需要组的最后一行,但如果已批准,则应显示前一个最后一行。

在MySQL中,我们如何实现这一点呢?

英文:

We're developing a system using Laravel, which means we are using Laravel ORM. We have a table similar to this:

  1. table: approvals
  2. +----+-------------------------+--------+----------+----------+------+----------+
  3. | id | app_model | app_id | group_id | revision | year | status |
  4. +----+-------------------------+--------+----------+----------+------+----------+
  5. | 1 | App\Models\AApplication | 4 | 500 | (NULL) | 2021 | APPROVED |
  6. | 2 | App\Models\AApplication | 95 | 501 | (NULL) | 2022 | APPROVED |
  7. | 3 | App\Models\BApplication | 22 | 90 | 1 | 2019 | APPROVED |
  8. | 4 | App\Models\BApplication | 37 | 90 | 2 | 2020 | APPROVED |
  9. | 5 | App\Models\AApplication | 125 | 501 | 1 | 2023 | APPROVED |
  10. | 6 | App\Models\BApplication | 89 | 90 | 3 | 2024 | DRAFT |
  11. +----+-------------------------+--------+----------+----------+------+----------+

We're grabbing the relational table data using Laravel's morphTo() relation:

  1. public function app() {
  2. return $this-&gt;morphTo(__FUNCTION__, &#39;app_model&#39;, &#39;app_id&#39;);
  3. }

I tried to present some data so that I can provide some context about the dimensions of the data in this table. We're keeping approvals of different application tables in this approvals table. A single institute can get revised approval multiple times based on each of the application. The approval can be 'DRAFT' initially, later be 'APPROVED'.

👉 We need to grab the last revised row of the group (group_id) where the status is 'APPROVED'.

  1. Approvals UI Grid
  2. +-------+----------+--------+
  3. | GROUP | REVISION | ACTION |
  4. +-------+----------+--------+
  5. | 500 | - | [view] |
  6. | 501 | 1 | [view] |
  7. | 90 | &#128073; 2 | [view] |
  8. +-------+----------+--------+

We've made the following MySQL query in Laravel ORM to achieve that:

  1. // $userId = get current user id;
  2. $query = DB::table(&#39;approvals&#39;)
  3. -&gt;with([&#39;app&#39;])
  4. -&gt;whereNotIn(&#39;id&#39;, function ($query) {
  5. $query-&gt;from(&#39;approvals as t1&#39;)
  6. -&gt;crossJoin(&#39;approvals as t2&#39;)
  7. -&gt;whereColumn(&#39;t1.id&#39;, &#39;&lt;&#39;, &#39;t2.id&#39;)
  8. -&gt;whereColumn(&#39;t1.group_id&#39;, &#39;=&#39;, &#39;t2.group_id&#39;)
  9. -&gt;whereColumn(&#39;t1.app_model&#39;, &#39;=&#39;, &#39;t2.app_model&#39;)
  10. -&gt;select(&#39;t1.id&#39;);
  11. });
  12. // Only show approvals of _that_ particular user.
  13. $query = $query-&gt;whereRelation(&#39;app&#39;, &#39;created_by&#39;, &#39;=&#39;, $userId);
  14. $query = $query-&gt;orderBy(&#39;approvals.id&#39;, &#39;DESC&#39;);
  15. $query = $query-&gt;where(&#39;approvals.status&#39;, &#39;APPROVED&#39;); // &lt;-------- &#128997; ISSUE IS HERE
  16. return $query;

If we comment out the 'APPROVED' condition from the query, we can get the last row of each group. Issue is that, the last row of group_id 90 is not 'APPROVED', but that comes into the grid which is not what we want.

👉 We need the last row of the group, but if approved, otherwise the previous-last row should come.

How can we achive this in MySQL?

答案1

得分: 1

要获取每个group_id的最新的APPROVED行。对于每个group_id,您要找到具有最大id且状态为APPROVED的行。

以下是我翻译好的代码部分:

  1. $query = DB::table('approvals')
  2. ->with(['app'])
  3. ->whereNotIn('id', function ($query) {
  4. $query->from('approvals as t1')
  5. ->join('approvals as t2', function ($join) {
  6. $join->on('t1.group_id', '=', 't2.group_id')
  7. ->on('t1.app_model', '=', 't2.app_model')
  8. ->on('t1.id', '<', 't2.id')
  9. ->where('t2.status', 'APPROVED');
  10. })
  11. ->select('t1.id');
  12. });
  13. // 只显示特定用户的批准。
  14. $query = $query->whereRelation('app', 'created_by', '=', $userId);
  15. $query = $query->orderBy('approvals.id', 'DESC');
  16. $query = $query->where('approvals.status', 'APPROVED');
  17. return $query;

希望这有助于您的需求。

英文:

You want to get the latest APPROVED row per group_id. For each group_id, you want to find the row with the maximum id where the status is APPROVED.

If that is the case, heres what i would do:

  1. $query = DB::table(&#39;approvals&#39;)
  2. -&gt;with([&#39;app&#39;])
  3. -&gt;whereNotIn(&#39;id&#39;, function ($query) {
  4. $query-&gt;from(&#39;approvals as t1&#39;)
  5. -&gt;join(&#39;approvals as t2&#39;, function ($join) {
  6. $join-&gt;on(&#39;t1.group_id&#39;, &#39;=&#39;, &#39;t2.group_id&#39;)
  7. -&gt;on(&#39;t1.app_model&#39;, &#39;=&#39;, &#39;t2.app_model&#39;)
  8. -&gt;on(&#39;t1.id&#39;, &#39;&lt;&#39;, &#39;t2.id&#39;)
  9. -&gt;where(&#39;t2.status&#39;, &#39;APPROVED&#39;);
  10. })
  11. -&gt;select(&#39;t1.id&#39;);
  12. });
  13. // Only show approvals of _that_ particular user.
  14. $query = $query-&gt;whereRelation(&#39;app&#39;, &#39;created_by&#39;, &#39;=&#39;, $userId);
  15. $query = $query-&gt;orderBy(&#39;approvals.id&#39;, &#39;DESC&#39;);
  16. $query = $query-&gt;where(&#39;approvals.status&#39;, &#39;APPROVED&#39;);
  17. return $query;

Edit: Explanation

The subquery in general returns all id where there's a row with the same group_id and app_model, as well as a greater id and finally the status of Approved. This essentially eliminates all but the latest APPROVED row for each group. The outer query then selects these latest APPROVED rows.

huangapple
  • 本文由 发表于 2023年7月31日 20:49:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76803803.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定