【问题标题】:Select minimal rows until sum of a column greater than a value选择最少的行,直到一列的总和大于一个值
【发布时间】:2020-09-14 20:37:54
【问题描述】:

我在 Postgresql 中有一个表(订单),架构和数据是这样的:

+----+---------+------+-------+------+--------+
| id | symbol  | qty  | price | side | status |
+----+---------+------+-------+------+--------+
| 1  | BTCUSDT | 0.02 | 6500  | SELL | NEW    | 
+----+---------+------+-------+------+--------+
| 2  | BTCUSDT | 1.00 | 6550  | SELL | NEW    |
+----+---------+------+-------+------+--------+
| 3  | BTCUSDT | 0.03 | 6600  | SELL | NEW    |
+----+---------+------+-------+------+--------+

我想选择边为 SELL 且状态为 NEW 的行,按价格升序排列,直到 sum(qty) 大于 0.5,所以结果应该为我选择 id 1 和 2

当我运行这个查询时:

SELECT * FROM (
       SELECT *, SUM(qty) OVER (ORDER BY price ASC) as total_qty FROM orders 
       WHERE symbol = 'BTCUSDT' AND side = 'SELL' AND status = 'NEW'
    ) AS o WHERE o.total_qty <= 0.5

它只返回第一行 (id: 1),我需要一个查询返回 1 和 2

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    您需要所有行,直到总和等于或超过 0.5 的行。
    这意味着,如果您从要返回的最后一行的总和中减去 qty,则结果必须小于 qty
    从总和中减去qty 并从WHERE 子句中删除等号:

    SELECT o.id, o.symbol, o.qty, o.price, o.side, o.status 
    FROM (
      SELECT *, SUM(qty) OVER (ORDER BY price ASC) - qty as total_qty 
      FROM orders 
      WHERE symbol = 'BTCUSDT' AND side = 'SELL' AND status = 'NEW'
    ) AS o 
    WHERE o.total_qty < 0.5
    

    请参阅demo
    结果:

    | id  | symbol  | qty  | price | side | status |
    | --- | ------- | ---- | ----- | ---- | ------ |
    | 1   | BTCUSDT | 0.02 | 6500  | SELL | NEW    |
    | 2   | BTCUSDT | 1.00 | 6550  | SELL | NEW    |
    

    【讨论】:

    • 请解释解决方案
    【解决方案2】:

    您需要保留两个SUMs。一个用于当前行,另一个用于上一行。然后,过滤很容易。例如:

    select *
    from (
      SELECT *, 
        SUM(qty) OVER (partition by side, status ORDER BY price ASC) as total_qty,
        SUM(qty) OVER (partition by side, status ORDER BY price ASC
          rows between unbounded preceding and 1 preceding) as prev_total_qty
      FROM orders 
      WHERE symbol = 'BTCUSDT' AND side = 'SELL' AND status = 'NEW'
    ) AS o 
    WHERE coalesce(prev_total_qty, 0) <= 0.5
    

    请参阅DB Fiddle 的运行示例。

    【讨论】:

      【解决方案3】:

      你走在正确的轨道上。问题是累积总和包括当前行。因此,您的 where 子句在通过 0.5 时将其过滤掉。简单的解决方案是从当前行中减去该值:

      SELECT *
      FROM (SELECT o.*, SUM(qty) OVER (ORDER BY price ASC) as total_qty
            FROM orders o
            WHERE symbol = 'BTCUSDT' AND side = 'SELL' AND status = 'NEW'
           ) o
      WHERE o.total_qty - o.qty <= 0.5;
      

      您也可以像 Impaler 那样使用窗口框架,但这会在第一行引入NULL 值的问题。您还可以像 forpas 一样减去子查询中的金额。在所有情况下,想法都是一样的:您需要在每行之前累积数量。

      【讨论】:

      • 此查询在第一行 qty = 0.5 的情况下失败,因为在这种情况下结果应该只是第一行但它返回 2:db-fiddle.com/f/9xM7jv5epRmzRN72xhepon/4
      • @forpas 。 . .问题是:“按价格升序排列,直到 sum(qty) 大于 0.5”。
      • 但是 OP 的条件:WHERE o.total_qty &lt;= 0.5 暗示直到总和等于或超过 0.5。对于发布的示例数据,预期结果包含 1 额外行,因为总和不等于 0.5。
      猜你喜欢
      • 2020-05-31
      • 2020-03-12
      • 1970-01-01
      • 2018-08-25
      • 1970-01-01
      • 1970-01-01
      • 2014-01-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多