【问题标题】:How can I join a table on itself to get the first or the last for each unique item?我怎样才能加入一个表本身以获得每个独特项目的第一个或最后一个?
【发布时间】:2016-12-03 16:22:48
【问题描述】:

我有以下 MySQL 查询,我已经研究了一段时间。我试图在in 语句中为每个list_id 获取一个唯一记录。我自己加入表格并检查lists.ordered 表格以确定我是否需要获取第一个或最后一个订单项目。

SELECT lists.ordered, 
       cnt1.* 
FROM   cnt_lists AS cnt1 
       LEFT JOIN lists 
              ON cnt1.list_id = lists.id 
       LEFT JOIN cnt_lists AS cnt2 
              ON ( cnt1.list_id = cnt2.list_id 
                   AND ( ( lists.ordered = 1 
                           AND cnt1.id < cnt2.id ) 
                          OR ( lists.ordered = 0 
                               AND cnt1.id > cnt2.id ) ) ) 
WHERE  cnt2.id IS NULL 
       AND cnt1.list_id IN ( '3176', '3295', '3296' ) 
       AND cnt1.listable_type = 'App\\Models\\Movie';

抓取最后一件商品效果很好,但是当我尝试抓取第一个订单商品时,它实际上抓取了第二件商品。

大家有什么建议吗?

样本数据:

id     list_id   listable_id   listable_type      order   created_at            updated_at
2226   3296      7             App\Models\Movie   1       2016-12-02 12:56:29   2016-12-02 15:07:42
2224   3296      1             App\Models\Movie   2       2016-11-29 00:06:57   2016-12-02 15:07:42
2227   3296      9             App\Models\Movie   3       2016-12-02 12:56:35   2016-12-02 12:56:35
2228   3296      54            App\Models\Movie   4       2016-12-02 12:56:39   2016-12-02 12:56:39
2229   3296      40            App\Models\Movie   5       2016-12-02 12:56:43   2016-12-02 12:56:43
2230   3296      65            App\Models\Movie   6       2016-12-02 12:56:47   2016-12-02 12:56:47

在上面的示例中,如果lists.ordered = 0 它将抓取行2230,因为它是最高的order。如果lists.ordered = 1 它将抢占行2226,因为它是最低的order。最高的工作正常,但最低的是抓住2224,这是第二低的。

【问题讨论】:

  • 您能发布一些来自cnt_listscnt 的示例表数据吗?
  • @WEBjuju 我已经添加了示例表数据。
  • 是的,就是样本表数据。还请提供来自cnt的一些数据。
  • @WEBjuju 该数据来自cnt_lists 表。
  • 这个作业/面试问题是在不使用order by 或子查询或最大连接技巧的情况下找到第一个/最后一个吗?因为否则尝试使用连接来解决是一种非常愚蠢的方法。

标签: php mysql join


【解决方案1】:

不要尝试在单个 SQL 中执行此操作。相反,编写应用程序代码或存储过程以在“第一个”和“最后一个”之间进行选择。

可能比这更简单:

SELECT ... FROM ...
    ORDER BY ordered ASC   -- ASC gives #1; DESC gives #6 in the example
    LIMIT 1;

如果输出多于一行,可能需要Groupwise max问题。

好的,在一个语句中有一种混乱的方法:

( SELECT 0 AS which, ... ORDER BY ordered ASC  LIMIT 1 )
UNION ALL
( SELECT 1 AS which, ... ORDER BY ordered DESC LIMIT 1 )
HAVING which = (however you pick first vs last);

【讨论】:

  • 由于 n+1 问题,这不会被认为是不好的做法吗?这意味着如果我要查找 150 个不同的列表,则需要 150 个单独的查询。这就是我尝试将其优化为一个查询的原因。
  • 这就是您需要 Groupwise 最大链接的地方。
  • 感谢您的回答。我一直在摆弄 Groupwise 最大链接建议,但我正在努力如何同时完成最大和最小顺序。我已经设置了一个小提琴来解决这个问题,但我看不到让它工作。 sqlfiddle.com/#!9/027b22/2 我可以返回每个list_id 的最大和最小行,然后我可以确定在 php 级别使用哪一个。谢谢!
  • “在最大值和最小值之间挣扎” -- 输入我的UNION 建议。
  • 感谢您的提琴。但是......输出应该是什么?每个list 条目对应一行?一个包含 6 行的有序列表,基于哪个 list 项目被挑选?
【解决方案2】:

您想查找按order 列排序的第一行/最后一行,但您在id 列上进行了排序?在这种情况下,试试这个:

选择列表。有序,
cnt1.*
FROM cnt_lists AS cnt1
左连接列表
ON cnt1.list_id = list.id
左连接 cnt_lists 作为 cnt2
开启 (cnt1.list_id = cnt2.list_id
AND ( ( list.ordered = 1
cnt1.order cnt2.order )
或 (lists.ordered = 0
cnt1.order > cnt2.order ) ) )
其中 cnt2.id 为 NULL
AND cnt1.list_id IN ('3176', '3295', '3296')
和 cnt1.listable_type = 'App\Models\Movie';

【讨论】:

  • 我用你的小提琴演奏了一点,它似乎奏效了。 [链接]sqlfiddle.com/#!9/027b22/10 [/链接]
  • 很好,谢谢!我把头发拉到这个上面。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-01-08
  • 1970-01-01
  • 2013-08-07
  • 1970-01-01
  • 2013-01-16
  • 2012-03-17
  • 1970-01-01
相关资源
最近更新 更多