用于获取订单状态的复杂SQL查询

时间:2018-06-22 19:12:20

标签: mysql sql

表格

因此,我有一个line_items表与一个orders表链接。显然,一个order可以有多个line_item

line_item除其他事项外还具有item_status。所有item_status都列在一个自然称为item_statuses的表中。该表中有一个stage列,它指示状态的线性性质。因此,item_status中的proofing具有stage中的20,因为它在生产管道中的出现要早于printing,即{{ 1}}。 60shipping,依此类推。它指示生产管道中80的状态。

因此,我很容易查看给定line_item有多少line_item个并按其order进行分组。一个item_status可能有2个orderline_item部门中,但是有1个项目仍然是shipping。到目前为止有道理吗?

景观

要实现我的最终目标,我需要确定printing的“状态”。为此,我决定将order的“状态”与其所有order最早 item_status相同。 (我将引号用在line_item的“状态”中,因为它从未真正存储在任何地方,只是即时计算出来。一个View可以帮助存储它。)

因此,如果order具有3个order,其中line_item个分别为item_statusprintingshipping,则shipping的总体“状态”应为order,因为它的printing仍然停留在较早的line_item中。 (因为item_status表中printing的{​​{1}}数比stage小。)

我想创建一个视图,该视图将为我提供item_statuses表中所有所有记录的shipping号及其“状态”。

目标

最终,我需要通过它们的order(也是orders表中的一列)来统计所有order的数量,但按其“状态”细分。 (因此,例如,给定的ship_date将有78个orders,分别是ship_date,139个order和43个shipping。我认为创建视图我提到这将是实现该目标的垫脚石。)

进度

到目前为止,这是我所能想到的:

printing

这很丑。这很复杂。它所做的就是为单个硬编码的proofing获取“最早的” SELECT orders.ship_date_id, orders.id, item_statuses.id FROM item_statuses JOIN line_items ON item_statuses.id = line_items.item_status_id JOIN orders ON line_items.order_id = orders.id WHERE item_statuses.stage = ( SELECT MIN(item_statuses.stage) FROM item_statuses JOIN ( SELECT line_items.item_status_id FROM line_items JOIN orders ON line_items.order_id = orders.id WHERE orders.id = '521079' ) AS x ON x.item_status_id = item_statuses.id ) ,然后仅显示具有该“状态”的订单。我需要这样做才能向我显示所有订单及其关联的“状态”。

问题

所有这些只是让我成为实现目标的一部分。正如我之前提到的,我最终将需要它来获取所有订单的计数,并按日期(在一周范围内)分开,并查看当天每个“状态”中有多少订单。

最糟糕的是,我知道这应该是可能的。我什至看不见它甚至有一个优雅的解决方案。因此,我来​​这里是不得已而为之。

2 个答案:

答案 0 :(得分:2)

(实际上)比您想象的要简单。让我们来建立它。

首先,让我们开始介绍所有项目:

SELECT Line_Items.order_id, Item_Statuses.stage
FROM Line_Items
JOIN Item_Statuses
  ON Item_Statuses.id = Line_Items.item_status_id

这会(看起来)像这样:

1 | 20
1 | 39
2 | 50

好吧,现在,由于每个订单有多行,所以我们需要每个订单的最小阶段。简单汇总:

SELECT Line_Items.order_id, MIN(Item_Status.stage) AS stage
FROM Line_Items
JOIN Item_Statuses
  ON Item_Statuses.id = Line_Items.item_status_id
GROUP BY Line_Items.order_id

哪个会产生:

1 | 20
2 | 50

现在,由于您需要交货日期,因此我们需要将Orders表联接到上一个查询的整个结果集中。这意味着我们需要一个子查询:

SELECT Orders.id, Orders.ship_date_id,
       Order_Status.stage,
FROM Orders
LEFT JOIN (SELECT Line_Items.order_id, MIN(Item_Status.stage) AS stage
           FROM Line_Items
           JOIN Item_Statuses
             ON Item_Statuses.id = Line_Items.item_status_id
           GROUP BY Line_Items.order_id) AS Order_Status
       ON Order_Status.order_id = Orders.id

哪个会产生:

1 | "2015-01-01" | 20
2 | "2015-01-04" | 50
3 | "2015-01-05" | (null) -- you might have orders without line items!

尚不清楚是否要/阶段有文字说明。如果是这样,您还需要再次加入状态表:

SELECT Orders.id, Orders.ship_date_id,
       Item_Statuses.stage,
FROM Orders
LEFT JOIN (SELECT Line_Items.order_id, MIN(Item_Status.stage) AS stage
           FROM Line_Items
           JOIN Item_Statuses
             ON Item_Statuses.id = Line_Items.item_status_id
           GROUP BY Line_Items.order_id) AS Order_Status
       ON Order_Status.order_id = Orders.id
JOIN Item_Statuses
  ON Item_Status.stage = Order_Status.stage

(此特定版本假定stage是一个唯一值-如果不是,则还有其他问题)

1 | "2015-01-01" | 'Printing'
2 | "2015-01-04" | 'Proofing'
3 | "2015-01-05" | (null) -- you might have orders without line items!

如何获取订单数?

好吧,获取特定日期的订单数很容易:

SELECT Orders.ship_date_id, COUNT(*) as orders
FROM Orders
GROUP By Orders.ship_date_id

"2015-01-01" | 1
"2015-01-04" | 400
"2015-04-05" | 33

然后您可以将两个查询大部分合并在一起:

SELECT Orders.ship_date_id, Order_Status.stage,
       COUNT(*) AS orders
FROM Orders
LEFT JOIN (SELECT Line_Items.order_id, MIN(Item_Status.stage) AS stage
           FROM Line_Items
           JOIN Item_Statuses
             ON Item_Statuses.id = Line_Items.item_status_id
           GROUP BY Line_Items.order_id) AS Order_Status
       ON Order_Status.order_id = Orders.id
GROUP BY Orders.ship_date_id, Order_Status.stage

类似这样:

"2015-01-01" | 20     | 1 
"2015-01-04" | 30     | 200
"2015-01-04" | 40     | 200
"2015-04-05" | 40     | 2
"2015-04-05" | 20     | 30
"2015-04-05" | (null) | 1

(如果此时需要阶段名称,最好将整个查询推送到子查询中,然后再次为该名称联接,因为否则必须将名称添加为分组中的额外列。保留为读者的练习。)

如果您想限制日期范围,可以将其包括在视图中,但是我可能会保留它,而只在查询视图本身时添加WHERE子句:

SELECT ship_date_id, stage, orders
FROM Orders_Per_Day
WHERE ship_date_id >= :start
      AND ship_date_id < :end

精明的读者会注意到日期范围中存在差距。不管是否在视图中,加入日历表(获取缺少日期的推荐方法)也留给读者练习。

答案 1 :(得分:0)

您的问题并不复杂,只要您的帖子很长。 :)

我认为您要寻找的是这个

SELECT 
  tbl.ship_date_id,
  tbl.order_id,
  tbl.item_status_id order_status
FROM
(
  SELECT 
    orders.ship_date_id,
    orders.id order_id,
    item_statuses.id item_status_id,
    ROW_NUMBER()OVER(PARTITION BY orders.id ORDER BY item_statuses.id ASC) rn
  FROM orders 
  JOIN line_items     ON line_items.order_id = orders.id
  JOIN item_statuses  ON item_statuses.id = line_items.item_status_id
) tbl
WHERE tbl.rn = 1

那么您应该能够根据ship_date_id`s

进行统计

这应该在item_statuses.id是随过程进展而递增的数字的假设下进行(例如printing的ID为3,而shipping的ID为4`,等等)

说明: ROW_NUMBER函数将根据item_statuses.id按每个订单(1,2,3,...)对商品进行排序,我们只需要在流程的最早步骤中为该商品排一行。