Mysql 与连接重复的结果

Mysql duplicate result with join

我有一个 table order_status_history 在其中为每个订单状态更改存储一行,我想获得最后一个条目确定的订单状态。

涉及的表格(简化):

orders
| id     | 
| 1      |  

order_products
| id     | order_id | productid | quantity| 
| 1      | 1        | 1         | 1       | 
| 2      | 1        | 1         | 2       | 
| 3      | 1        | 1         | 2       | 
| 4      | 1        | 1         | 10      |  

order_status_history
| id     | order_id | order_status_id | updatedat              | 
| 1      | 1        | 1               | 2017-05-18 18:45:50    | 
| 2      | 1        | 2               | 2017-05-19 18:45:50    | 
| 3      | 1        | 3               | 2017-05-20 18:45:50    | 
| 4      | 1        | 2               | 2017-05-21 18:45:50    | 
| 5      | 1        | 3               | 2017-05-22 18:45:50    | 

我真正需要的是:

如果最后一个订单状态为“3”(因为“4”表示已取消或无论如何不再可用),对于每个订单 ID 仅获取最后一个条目(基于 updatedat 列,最后一个表示最新)

这是我试过的查询,但在本例中 returns 2 行相同的订单 ID 和状态 3:

SELECT o.id, 
       osh.updatedat, 
       op.quantity 
FROM   orders_products op 
       LEFT JOIN order_status_history osh 
              ON osh.order_id = op.orderid 
       LEFT JOIN orders o 
              ON o.id = op.orderid 
WHERE  op.productid = 1 
       AND (SELECT osh.order_status_id 
            FROM   order_status_history osh 
            WHERE  osh.order_id = o.id 
            ORDER  BY osh.updatedat DESC 
            LIMIT  1) = 3 

我相信 orders table 中没有任何条目,orders_productsorder_status_history table 中也不会有任何条目。因此,为了便于理解,我会更改 table 连接的顺序。此外,不需要 LEFT JOIN,因为我们正在尝试获取特定产品的行(因此,产品 table 中应该存在行),并且在特定的 order_status(因此,状态历史记录中应该存在行 table)。所以我将查询中的所有 LEFT JOIN 更改为 INNER JOIN.

现在,要获取与特定状态对应的最后 updatedat 行,我们将不得不使用 Derived Table。在此子查询中,当状态为 3.

时,我们将为每个订单获取最新的 updatedat

最后,我们将这个子查询结果集适当加入主tables,得到最新updatedat行值对应的数据。

此外,将 WHERE 条件转移到连接 ON 子句通常是一种很好的做法,以便于理解。此外,将来当您从 INNER JOIN 更改为 LEFT JOIN 等时,您可以轻松更改而不必担心由于 WHERE.

而发生不必要的过滤

尝试以下查询: View on DB Fiddle

SELECT 
  o.id, 
  op.quantity, 
  osh.updatedat 
FROM 
  orders AS o 
JOIN order_products AS op 
  ON op.order_id = o.id AND 
     op.productid = 1 
JOIN order_status_history AS osh 
  ON osh.order_id = o.id 
JOIN (SELECT order_id, 
             MAX(updatedat) AS max_updated_at
      FROM order_status_history 
      WHERE order_status_id = 3
      GROUP BY order_id
     ) AS dt 
  ON dt.order_id = o.id AND 
     dt.max_updated_at = osh.updatedat;

结果:

| id  | quantity | updatedat           |
| --- | -------- | ------------------- |
| 1   | 1        | 2017-05-22 18:45:50 |
| 1   | 2        | 2017-05-22 18:45:50 |
| 1   | 2        | 2017-05-22 18:45:50 |
| 1   | 10       | 2017-05-22 18:45:50 |

除了 updatedat 值之外,如果您不需要从 order_status_history table 中获取任何其他列,您可以通过删除进一步优化查询加入 order_status_history table

查询#2

SELECT 
  o.id, 
  op.quantity, 
  dt.max_updated_at AS updatedat 
FROM 
  orders AS o 
JOIN order_products AS op 
  ON op.order_id = o.id AND 
     op.productid = 1 
JOIN (SELECT order_id, 
             MAX(updatedat) AS max_updated_at
      FROM order_status_history 
      WHERE order_status_id = 3
      GROUP BY order_id
     ) AS dt 
  ON dt.order_id = o.id;

结果:

| id  | quantity | updatedat           |
| --- | -------- | ------------------- |
| 1   | 1        | 2017-05-22 18:45:50 |
| 1   | 2        | 2017-05-22 18:45:50 |
| 1   | 2        | 2017-05-22 18:45:50 |
| 1   | 10       | 2017-05-22 18:45:50 |