每个产品的最后订单时间戳

时间:2013-08-13 15:17:23

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

我想找到针对哪个指定NULL上次付款(或product_id,如果n / a)。下面是我正在使用的表格(简化版)的表示。

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

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

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也可能是错误的。)

3 个答案:

答案 0 :(得分:2)

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

返回所有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”会为每个“order_timestamp”获取最新的“product_id”。这与内联视图“m”相结合,以获取具有最新时间戳的订单的整行。

如果恰好有多个订单使用相同的最新“order_timestamp”(即order_timestamp不能保证对于给定的product_id是唯一的),那么最外面的GROUP BY 1}}确保只返回其中一个订单行。

如果只需要返回特定的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子句。添加另外两个只是为了通过限制每个派生表的大小来提高性能。

答案 1 :(得分:1)


    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

答案 2 :(得分:1)

要退回所有产品,即使是那些没有订单的产品,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