【问题标题】:Last order timestamp per product每个产品的最后订单时间戳
【发布时间】:2013-08-15 07:07:53
【问题描述】:

我想查找指定product_id最后一次付款(或NULL,如果不适用)。下面是我正在使用的表的表示(简化版)。

+----------+
|Products  |
|----------+
|product_id|
+----------+
+---------------+
|Orders         |
+---------------+
|order_id       |
|order_timestamp|
|order_status   |
+---------------+
+-----------------+
|ProductsOrdersMap|
+-----------------+
|product_id       |
|order_id         |
+-----------------+

在 JOIN、MAX、GROUP BY、LEFT JOIN、多个 INNER JOIN 以得到每个组的最大 n 之后,我仍然无法得到正确的结果。大多数情况下,具有多个订单的产品会返回多行。到目前为止,我得到的最好结果是(我正在搜索特定产品):

product_id  order_id  order_timestamp      order_status
8           NULL      NULL                 NULL
9           NULL      NULL                 NULL
10          NULL      NULL                 NULL
12          NULL      NULL                 NULL
13          NULL      NULL                 NULL
14          11        2013-08-13 07:22:01  finished
15          11        2013-08-13 07:22:01  finished
15          12        2013-08-14 00:00:00  finished
32          11        2013-08-13 07:22:01  finished
83          9         2013-08-13 07:04:02  finished
83          10        2013-08-13 07:11:42  finished

编辑:在 PP 之后。 anwser,我最终得到了以下查询:

SELECT p.product_id, o.order_id, MAX(order_timestamp) AS order_timestamp, order_status
FROM Products p LEFT JOIN (ProductsOrdersMap m, Orders o)
  ON (p.product_id = m.product_id AND m.order_id = o.order_id)
WHERE p.product_id IN (8,9,10,12,13,14,15,32,83)
GROUP BY p.product_id

返回

product_id  order_id  order_timestamp      order_status
8           NULL      NULL                 NULL
9           NULL      NULL                 NULL
10          NULL      NULL                 NULL
12          NULL      NULL                 NULL
13          NULL      NULL                 NULL
14          11        2013-08-13 07:22:01  finished
15          11        2013-08-13 07:22:01  finished
32          11        2013-08-13 07:22:01  finished
83          9         2013-08-13 07:04:02  finished

乍一看,它似乎是正确的,但只有产品 ID 和时间戳是正确的。比较上面的两个查询,你可以看到,对于产品 15 和 83,order_id 是错误的(order_status 也可能是错误的)。

【问题讨论】:

    标签: mysql join left-join greatest-n-per-group


    【解决方案1】:

    此查询应返回指定的结果集(这只是桌面检查,未测试)

    返回所有 product_id

    SELECT p.product_id
         , m.order_d
         , m.order_timestamp
         , m.order_status
      FROM products p
      LEFT
      JOIN ( SELECT kl.product_id
                  , MAX(ko.order_timestamp) AS latest_timestamp
               FROM orderproductsmap kl
               JOIN orders ko
                 ON ko.order_id = kl.order_id
              GROUP
                 BY kl.product_id
           ) l
        ON l.product_id = p.product_id
      LEFT
      JOIN ( SELECT ml.product_id
                  , mo.order_id
                  , mo.order_timestamp
                  , mo.order_status
               FROM orderproductsmap ml
               JOIN orders mo
                 ON mo.order_id = ml.order_id
           ) m
        ON m.product_id = l.product_id
       AND m.order_timestamp = l.latest_timestamp
     GROUP
        BY p.product_id
    

    内联视图“l”为我们提供每个“product_id”的最新“order_timestamp”。这与内联视图“m”相结合,以获取具有最新时间戳的订单的整行。

    如果碰巧有多个订单具有相同的最新“order_timestamp”(即order_timestamp 不能保证对于给定的product_id 是唯一的),那么最外面的GROUP BY 确保只有一个返回那些订单行。

    如果只需要返回特定的 product_id 值,请在最外层查询中添加 WHERE 子句。为了提高性能,可以在内联视图中重复相同的谓词。

    要仅返回 SPECIFIC product_id,我们添加三个 WHERE 子句:

    SELECT p.product_id
         , m.order_d
         , m.order_timestamp
         , m.order_status
      FROM products p
      LEFT
      JOIN ( SELECT kl.product_id
                  , MAX(ko.order_timestamp) AS latest_timestamp
               FROM orderproductsmap kl
               JOIN orders ko
                 ON ko.order_id = kl.order_id
              WHERE kl.product_id IN (8,9,10,12,13,14,15,32,83)
              GROUP
                 BY kl.product_id
           ) l
        ON l.product_id = p.product_id
      LEFT
      JOIN ( SELECT ml.product_id
                  , mo.order_id
                  , mo.order_timestamp
                  , mo.order_status
               FROM orderproductsmap ml
               JOIN orders mo
                 ON mo.order_id = ml.order_id
              WHERE ml.product_id IN (8,9,10,12,13,14,15,32,83)
           ) m
        ON m.product_id = l.product_id
       AND m.order_timestamp = l.latest_timestamp
     WHERE p.product_id IN (8,9,10,12,13,14,15,32,83)
     GROUP
        BY p.product_id
    

    只有最外层查询的 WHERE 子句是必需的。添加另外两个只是为了通过限制每个派生表的大小来提高性能。

    【讨论】:

    • 我测试了第一个查询,它有效!但它应该是SELECT p.product_id 而不是SELECT k.product_id
    • 很好,是的,应该是“p.”而不是“k.
    • 感谢您的第二次查询,以及优化警告。我接受你的回答。
    【解决方案2】:

    要退回所有产品,即使是那些没有订单的产品,LEFT JOIN 绝对是最好的选择。上面@PP 的答案使用“旧式”内连接,相当于:

    SELECT
        P.product_id
        ,MAX(order_timestamp)
    FROM Products P
    INNER JOIN ProductsOrdersMap M ON P.product_id = M.product_id
    INNER JOIN Orders O ON O.order_id = M.order_id
    GROUP BY
        P.product_id
    

    从这个语法开始,到达LEFT JOIN 要容易得多 - 只需将INNER 替换为LEFT

    SELECT
        P.product_id
        ,MAX(order_timestamp)
    FROM Products P
    LEFT JOIN ProductsOrdersMap M ON P.product_id = M.product_id
    LEFT JOIN Orders O ON O.order_id = M.order_id
    GROUP BY
        P.product_id
    

    附录:Renato 需要的不仅仅是将另一个答案改写为LEFT JOIN,因为order_idorder_status 必须与最大时间戳一起出现。最简单的方法是从产品 ID 和订单 ID 列表开始,其中订单的最大时间戳为 order_id

    SELECT
      p2.product_id,
      o2.order_id
    FROM Products p2
    INNER JOIN ProductsOrdersMap m ON p2.product_id = m.product_id
    INNER JOIN Orders o2 ON m.order_id = o2.order_id
    WHERE (o2.order_id, o2.order_timestamp) IN (
      SELECT order_id, MAX(order_timestamp)
      FROM Orders
      GROUP BY order_id)
    

    然后,不要使用ProductsOrdersMap 将产品解析为订单,而是使用上述查询的结果:

    SELECT
      p.product_id,
      o.order_id,
      o.TS,
      o.order_status
    FROM Products p
    LEFT JOIN (
      SELECT
        p2.product_id,
        o2.order_id
      FROM Products p2
      INNER JOIN ProductsOrdersMap m ON p2.product_id = m.product_id
      INNER JOIN Orders o2 ON m.order_id = o2.order_id
      WHERE (o2.order_id, o2.order_timestamp) IN (
        SELECT order_id, MAX(order_timestamp)
        FROM Orders
        GROUP BY order_id)
      ) MaxTS ON p.product_id = MaxTS.product_id
    LEFT JOIN Orders o ON MaxTS.order_id = o.order_id
    

    【讨论】:

    • 编辑后检查我的问题。我得到了和你一样的结果,但是它返回了错误的值。
    • 好的,我想我知道你在追求什么。我上面所做的只是将另一个答案更改为 LEFT JOINS,我可以从您的编辑中看到,这还不足以获得您想要的结果。我会尽快发布更新,并在准备就绪时通知您。
    • 是的,在你回答之前我做了同样的事情(我的编辑):D 然而,我失败得很惨......谢谢。
    • Renato - 我已经更新了我的答案。让我知道这是否更接近您想要的。离开加入时很难排除行,而且我今天没有 MySQL 访问权限,所以我无法测试它。如果由于与ProductsOrderMap 的左连接而最终导致额外的行,我们也可以将该表折叠到子查询中。
    • Ed Gibbs - 我想MaxTS.TS 是指MaxTS.order_timestamp。这就是你所担心的:我最终得到了重复的行。
    【解决方案3】:
    
        SELECT
            P.product_id
            ,MAX(order_timestamp)
        FROM
            Products P
            ,Orders O
            ,ProductsOrdersMap M
        WHERE
            P.product_id = M.product_id
            AND O.order_id = M.order_id
        GROUP BY
            P.product_id
    

    【讨论】:

    • 看起来代码是 o.o 工作的,而不是一个单一的连接......我想我想多了。
    • 如何退回所有产品,即使是没有订单的产品? LEFT JOIN 我会知道,但用这种方式,我不知道。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-24
    • 1970-01-01
    • 1970-01-01
    • 2022-11-27
    相关资源
    最近更新 更多