【问题标题】:Mysql how to reduce rows by max datetime and group by non-uniqe idMysql如何按最大日期时间减少行并按非唯一ID分组
【发布时间】:2015-09-04 19:09:05
【问题描述】:

我有严重的脑胀气。

为了简单起见,我有 3 个表:OrdersStatusesXrefOrdersStatuses

我已经设置了一个 SQLFiddle (SQLFiddle Here),其中包含简化的架构和一些经过编辑的行数据。

我需要获得的是带有最新 XrefOrdersStatuses(与 Statuses 结合)的订单。我可以使用以下查询来做到这一点:

SELECT o.shopper_name_first, o.shopper_name_last, os.os_name, x.xos_order_id, x.xos_status_id, x.xos_datetime
FROM Orders AS o 
INNER JOIN XrefOrdersStatuses AS x ON x.xos_order_id = o.order_id
LEFT JOIN Statuses AS os ON os.os_id = x.xos_status_id
ORDER BY x.xos_order_id, x.xos_datetime DESC

结果如下:

| shopper_name_first | shopper_name_last |             os_name | xos_order_id | xos_status_id |           xos_datetime |
|--------------------|-------------------|---------------------|--------------|---------------|------------------------|
|              Sally |               Sue | Order Pre-Processed |        34049 |            31 | June, 18 2015 12:42:50 |
|              Sally |               Sue |          Order Paid |        34049 |            20 | June, 18 2015 12:36:30 |
|              Sally |               Sue |       Order Created |        34049 |            10 | June, 18 2015 12:34:56 |
|                Joe |            Schmoe | Order Pre-Processed |        34050 |            31 | June, 18 2015 12:54:50 |
|                Joe |            Schmoe |          Order Paid |        34050 |            20 | June, 18 2015 12:38:30 |
|                Joe |            Schmoe |       Order Created |        34050 |            10 | June, 18 2015 12:34:56 |
|              Peter |             Piper |          Order Paid |        34051 |            20 | June, 18 2015 12:37:30 |
|              Peter |             Piper |       Order Created |        34051 |            10 | June, 18 2015 12:34:56 |

在生产场景中,我选择(ing)更多/所有 Orders 表列,为了简单起见,将它们留在这里。注意重复的订单,但状态是一致的。

在我看来,我正在努力的目的不是找到最新的状态。此视图列出了订单,我想根据最大日期时间列值将每个订单与其各自的最新状态连接/匹配。一对多模式(一个订单有多个状态)。

所以我想做的是:

  • 选择订单
  • 使用 MAX(xos_datetime) GROUP BY xos_order_id 加入外部参照
  • 加入状态以获取定义

订单必须保留在查询中的左侧表(对于与其他表一起使用的其他连接)。在经历了许多其他 SO 问题和答案以及许多 Google 搜索之后,我还没有完全找到我需要的东西。

如果需要按最大日期时间获取 XrefOrdersStatuses 并按订单 ID 分组,这将是查询......

SELECT x.*
FROM XrefOrdersStatuses AS x
JOIN (
  SELECT xos_order_id, MAX(xos_datetime) AS maxdate
  FROM XrefOrdersStatuses
  GROUP BY xos_order_id
  ) AS x1 ON x1.xos_order_id = x.xos_order_id AND x1.maxdate = x.xos_datetime;

导致:

| xos_id | xos_order_id | xos_status_id |           xos_datetime |
|--------|--------------|---------------|------------------------|
| 118287 |        34051 |            20 | June, 18 2015 12:37:30 |
| 118289 |        34049 |            31 | June, 18 2015 12:42:50 |
| 118290 |        34050 |            31 | June, 18 2015 12:54:50 |

...耶!每个订单的最新状态!

唉,我需要 Orders 表作为左侧表(其他连接和搜索位置 - 即名称、活动/非活动、分配给用户等)。

我的麻烦是将 Orders 查询与 XrefOrdersStatuses 查询结合起来。我只是似乎不能让他们一起玩。我的尝试因排序、分组以及各种激烈的混乱而失败。

我觉得这让我很接近,但是让分组和排序正确是我发泄的地方:

SELECT o.shopper_name_first, o.shopper_name_last, os.os_name, x.xos_order_id, x.xos_status_id, x.xos_datetime
FROM Orders AS o 
JOIN (
  SELECT xz.*
  FROM XrefOrdersStatuses AS xz
  JOIN (
    SELECT xos_order_id, MAX(xos_datetime) AS maxdate
    FROM XrefOrdersStatuses
    GROUP BY xos_order_id
  ) AS x1 ON x1.xos_order_id = xz.xos_order_id AND x1.maxdate = xz.xos_datetime
) AS x
LEFT JOIN Statuses AS os ON os.os_id = x.xos_status_id
ORDER BY x.xos_order_id, x.xos_datetime DESC;

结果如下所示:

| shopper_name_first | shopper_name_last |             os_name | xos_order_id | xos_status_id |           xos_datetime |
|--------------------|-------------------|---------------------|--------------|---------------|------------------------|
|                Joe |            Schmoe | Order Pre-Processed |        34049 |            31 | June, 18 2015 12:42:50 |
|              Peter |             Piper | Order Pre-Processed |        34049 |            31 | June, 18 2015 12:42:50 |
|              Sally |               Sue | Order Pre-Processed |        34049 |            31 | June, 18 2015 12:42:50 |
|                Joe |            Schmoe | Order Pre-Processed |        34050 |            31 | June, 18 2015 12:54:50 |
|              Peter |             Piper | Order Pre-Processed |        34050 |            31 | June, 18 2015 12:54:50 |
|              Sally |               Sue | Order Pre-Processed |        34050 |            31 | June, 18 2015 12:54:50 |
|              Peter |             Piper |          Order Paid |        34051 |            20 | June, 18 2015 12:37:30 |
|              Sally |               Sue |          Order Paid |        34051 |            20 | June, 18 2015 12:37:30 |
|                Joe |            Schmoe |          Order Paid |        34051 |            20 | June, 18 2015 12:37:30 |

我尝试了不同的变化,尝试向前、向后、向左、向右、向内、向外、向上、向下、热、冷、湿、干......你明白了。

我需要的是它看起来像这样:

| shopper_name_first | shopper_name_last |             os_name | xos_order_id | xos_status_id |           xos_datetime |
|--------------------|-------------------|---------------------|--------------|---------------|------------------------|
|              Sally |               Sue | Order Pre-Processed |        34049 |            31 | June, 18 2015 12:42:50 |
|                Joe |            Schmoe | Order Pre-Processed |        34050 |            31 | June, 18 2015 12:54:50 |
|              Peter |             Piper |          Order Paid |        34051 |            20 | June, 18 2015 12:37:30 |

如果您返回第一个查询...我需要这些结果,但会减少,以便只保留每个订单的最新状态。事实上,我实际上是在 PHP 中这样做的......因此我正在重新访问查询以从 PHP 中删除看似不必要的步骤。

也许我的解决方案是 XrefOrdersStatuses 查询(上面的第二个),但使用 RIGHT JOIN 来获取 Order 和 Status 表?

有人想吗?很抱歉让这个问题这么长(几乎是 TL;我自己的 DR),但我希望我已经适当地注释了这个问题。

顺便说一句——我是 SO 的长期潜伏者(从这里发现的问题中收集了无数的问题和提示!)但这是我第一次陷入困境并且无法弄清楚如何获得我需要什么。


编辑/回答:按照我需要的方式设置查询,发现我的性能问题与列和索引有关。有点像因为腹痛去医院,结果被告知您患有克罗恩病,然后发现您的肾脏长了一个肿瘤——最后一切都好起来了。

# Query for answer to user Linoff with mods
SELECT o.shopper_name_first, o.shopper_name_last, s.os_name, x.*
FROM Orders o 
RIGHT JOIN XrefOrdersStatuses x ON x.xos_order_id = o.order_id 
RIGHT JOIN
(
  SELECT xos_order_id, MAX(xos_datetime) AS maxdate
  FROM XrefOrdersStatuses
  GROUP BY xos_order_id
) xmax ON xmax.xos_order_id = x.xos_order_id AND xmax.maxdate = x.xos_datetime
LEFT JOIN Statuses s ON s.os_id = x.xos_status_id
ORDER BY o.order_datetime DESC;

【问题讨论】:

    标签: mysql datetime join group-by


    【解决方案1】:

    这是你要找的吗?

    SELECT <choose your columns here>
    FROM Orders o LEFT JOIN
         XrefOrdersStatuses x
         ON x.xos_order_id = o.order_id LEFT JOIN
         (SELECT xos_order_id, MAX(xos_datetime) AS maxdate
          FROM XrefOrdersStatuses
          GROUP BY xos_order_id
         ) xmax
         ON xmax.xos_order_id = x.xos_order_id AND
            xmax.maxdate = x.xos_datetime;
    

    LEFT JOIN 仅在您有没有状态记录的订单时才需要。

    【讨论】:

    • 它有效,但它不起作用....我已经尝试了几种不同的方法。不过,建立一个走同一条路的人,然后回想一下 RIGHT JOINs……答案可能就在眼前。在 IDE 中进行测试....
    • 如果我们将外部参照连接修改为 RIGHT JOIN,将状态修改为 LEFT JOIN,并且 ORDER BY Orders.order_datetime 它似乎可以正常工作。 sqlfiddle.com/#!2/9ae7cc/4/0 但是当我在控制台中复制时,我无法在生产数据上获得类似的输出。我的查询性能很差。我开始怀疑,让查询为我需要的数据提供服务的目标是否是最好的。
    • @Rodney 。 . .如果你有正确的外键关系,我不明白你为什么需要混合左右连接。事实上,除了某些订单可能没有状态的可能性之外,您根本不需要外连接。
    • 我没有在这个数据库中使用外键,因为 MyISAM 引擎不支持它,我在大多数表上使用全文搜索和频繁的简单选择查询。不过,可能是时候考虑在可能的情况下更改为 InnoDB(自 2011 年以来未重新访问数据库模式)。在我的开发服务器中,我可以运行与您发布的内容非常相似的基本查询(使用我想要的列),但我得到的结果是最旧的日期时间位于集合的前面。与 SQLFiddle 输出的不同。我正在努力调和原因。
    • @Rodney 。 . .这假定日期时间值正确存储在 datetime 列中。如果您将其存储为字符串,那么您将获得字符串的最大值。再一次,你应该修复你的数据。
    猜你喜欢
    • 1970-01-01
    • 2012-09-26
    • 1970-01-01
    • 2022-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-27
    相关资源
    最近更新 更多