PostgreSQL:选择行直到单列的总和达到一个阈值

4
我想要写一个查询,从一个有序表中取出行,同时聚合一列的值,直到聚合值达到所需的阈值。
另一个条件是违反阈值的行应该包括在查询结果中。
我已经查找了在PostgreSQL中完成的其他解决方案,导致我创建了以下查询:
SELECT * FROM (
    SELECT *, SUM(amount) OVER (ORDER BY amount DESC) AS running_amount
    FROM public.orders WHERE price = 0.09) AS t
WHERE t.running_amount <= 15;

然而,这个查询存在一个问题,它代表了一个PostgreSQL窗口查询,如果给定行的列值不是唯一的,则跳过对所有行的列值进行聚合。
遗憾的是,窗口查询不支持考虑具有唯一值列的情况。
我听说还有一些替代方法可以实现这个功能,比如创建一个PostgreSQL函数,但我不知道从哪里开始进行这种聚合查询。
如果有任何想法或知识,我将非常感激。
2个回答

4

向窗口函数的ORDER BY子句添加唯一列(主键),例如:

SELECT * FROM (
    SELECT *, SUM(amount) OVER (ORDER BY amount DESC, id) AS running_amount
    FROM public.orders WHERE price = 0.09
) AS t
WHERE t.running_amount <= 15;

如果缺少唯一列,可以使用系统列ctid


您可以使用UNION ALL获取违反阈值的行,例如:

WITH cte AS (
    SELECT *, SUM(amount) OVER (ORDER BY amount DESC, id) AS running_amount
    FROM public.orders 
    WHERE price = 0.09
)
SELECT * 
FROM cte
WHERE running_amount <= 15
UNION ALL (
    SELECT * 
    FROM cte
    WHERE running_amount > 15
    LIMIT 1
);

非常感谢!这解决了窗口工作的一个问题。那么如何获取超过阈值的违规行呢? - Dranithix
非常完美!谢谢你哈哈 :)。 - Dranithix

4

如果您想获取最终行(即跨越临界值的那一行),您有两个相对简单的选择。我更喜欢:

SELECT o.*
FROM (SELECT o.*,
             SUM(amount) OVER (ORDER BY amount DESC) AS running_amount
      FROM public.orders
      WHERE price = 0.09
     ) o
WHERE o.running_amount - o.amount <= 15;

另一种方式是使用窗口函数:

SELECT o.*
FROM (SELECT o.*,
             SUM(amount) OVER (ORDER BY amount DESC
                               ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
                              ) AS running_amount
      FROM public.orders
      WHERE price = 0.09
     ) o
WHERE o.running_amount <= 15;

将第一个查询转换为 WHERE o.running_amount - o.amount <= 15; 实际上适用于我进行的所有测试案例!我会回答并解释为什么你的第一个查询是最佳答案。 - Dranithix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接