【问题标题】:Get order number that makes the total sum of orders be 1000获取订单总和为1000的订单号
【发布时间】:2013-07-18 15:12:40
【问题描述】:

我有一个订单表,其中每一行都有一个名为价格的列。这些订单中的每一个还有一个名为created_at 的列,用于说明该订单的创建时间。

找出哪个订单使总价格超过 1000 美元的好方法是什么?

所以,假设我有三个如下所示的订单:

Order 1: price: $800 - created_at: 2013/07/11 

Order 2: price: $100 - created_at: 2013/07/13 

Order 3: price: $300 - created_at: 2013/07/14 

我有兴趣发现订单 3 是让我超过 1000 美元的订单,因为如果我们将 800 美元 + 100 美元 + 300 美元相加,正是那些使总金额超过 1000 美元的 300 美元。

我可以执行什么查询来找到它?

【问题讨论】:

  • 订单有什么区别?或者,会有多个结果,还是只有一个?
  • 主键是一个叫uuid的列,是一个唯一的字符串。

标签: sql postgresql sum window-functions cumulative-sum


【解决方案1】:

用窗口聚合函数sum()计算运行总和后,只需根据created_at选择超过1000的第一行:

SELECT *
FROM (
   SELECT order_id, created_at
        , sum(price) OVER (ORDER BY created_at) AS sum_price
   FROM   orders
   ) sub
WHERE  sum_price >= 1000
ORDER  BY created_at 
LIMIT  1;

这应该比@Gordon's version 快,因为按照窗口函数中已经使用的相同顺序选择第一个比为每一行计算一个值(不是sargable)便宜很多。

我使用sum_price >= 1000,所以达到 1000 也完全符合条件。如果只有 超过 符合条件,请使用 > 而不是 >=

The manual on window functions informs:

除了这些函数之外,任何内置或用户定义的聚合 函数可以作为窗口函数使用

应该注意,与@Gordon 的查询相反,此查询始终只提供一行。在具有相同created_at 的多行跨越 1000 障碍的情况下,它们都将符合 Gordon 的答案(或者它会失败,见下文),而我的只有one 被选中。这将是任意的,只要您不向ORDER BY 添加更多项目作为决胜局。喜欢:

ORDER BY created_at, order_id

此查询中有两个 ORDER BY 实例,碰巧您可以修改其中一个或两个以使其工作。两者都这样做以使排序顺序相同,这应该是最快的。

实际上,对于这个测试用例,Gordon 的版本会完全失败

CREATE TEMP TABLE orders(order_id int, price int, created_at date);

INSERT INTO orders VALUES
  (1, 500, '2013-07-01')
 ,(2, 400, '2013-07-02')
 ,(3, 100, '2013-07-03')
 ,(4, 100, '2013-07-03')
 ,(5, 100, '2013-07-03');

您可以通过使窗口函数中的排序顺序唯一来解决它,如上所示。

或者您可以将窗口函数的框架定义更改为:

ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

Read the fine print in the manual.

但无论哪种方式都比较慢。

-> SQLfiddle

【讨论】:

    【解决方案2】:

    为此,您需要一个累积和,Postgres 将其作为窗口函数提供:

    select o.*
    from (select o.*,
                 sum(o2.price) over (order by created_at) as cumsum
          from orders o
         ) o
    where 1000 > cumsum - price and 1000 <= cumsum;
    

    where 子句只对添加价格首先超过 1000 美元的行进行罚款。

    【讨论】:

    • 嗨,戈登,什么是 o2?我猜是从内部订单 o2。
    • @HommerSmith: o2只是一个错字。您可以在此处删除 all 表限定条件和表别名。但是,对于重复的created_at,此查询可能会完全失败。我在答案中添加了一段。
    猜你喜欢
    • 2022-01-04
    • 1970-01-01
    • 1970-01-01
    • 2018-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-11
    • 1970-01-01
    相关资源
    最近更新 更多