【问题标题】:Select rows until running sum reaches specific value选择行直到运行总和达到特定值
【发布时间】:2019-03-16 08:21:31
【问题描述】:

我有以下数据:

DECLARE @t TABLE (usr VARCHAR(100), dt DATE, amount INT);
INSERT INTO @t VALUES
('a', '2018-01-01', 100), -- 100
('a', '2018-02-01', 100), -- 200
('a', '2018-03-01', 100), -- 300
('a', '2018-04-01', 100), -- 400
('a', '2018-05-01', 100), -- 500
('b', '2018-01-01', 150), -- 150
('b', '2018-02-01', 150), -- 300
('b', '2018-03-01', 150), -- 450
('b', '2018-04-01', 150), -- 600
('b', '2018-05-01', 150); -- 750

还有一个值,例如 300 或 301(用户变量或列)。我想选择行,直到运行总金额达到指定值,具有以下扭曲:

  • 对于 300,我想为 a 选择前 3 行,为 b 选择前 2 行
  • 对于 301,我想为 a 选择前 4 行,为 b 选择前 3 行

这应该很简单,但我找到的解决方案不能处理第二种情况。

【问题讨论】:

  • 金额如何从 100 变为 150?你是怎么得到 300/301 的?细节缺失。
  • amount 是否保证为非负数?即运行总数永远不会下降?
  • @MartinSmith 很有趣。假设它始终是正值,但如果它也处理负值,则为奖励。
  • 您需要定义应该如何处理。它应该只返回所有行,直到运行总数首次达到 300 或后续行,如果总数再次低于该值
  • @MartinSmith 假设它是贪婪的,即选择尽可能多的行。

标签: sql sql-server sql-server-2012 window-functions


【解决方案1】:
DECLARE @t TABLE (usr VARCHAR(100), dt DATE, amount INT);
INSERT INTO @t VALUES
('a', '2018-01-01', 100), -- 100
('a', '2018-02-01', 100), -- 200
('a', '2018-03-01', 100), -- 300
('a', '2018-04-01', 100), -- 400
('a', '2018-05-01', 100), -- 500
('b', '2018-01-01', 150), -- 150
('b', '2018-02-01', 150), -- 300
('b', '2018-03-01', 150), -- 450
('b', '2018-04-01', 150), -- 600
('b', '2018-05-01', 150); -- 750

DECLARE @Total INT = 301;

WITH cte AS
(
    SELECT *, SUM(amount) OVER (PARTITION BY usr ORDER BY dt) AS RunTotal
    FROM @t
)
SELECT *
FROM   cte
WHERE  cte.RunTotal - cte.amount < @Total -- running total for previous row is less
                                          -- than @Total then include current row

【讨论】:

  • 我在 dt 的订单附近遇到异常
  • 在 SQL Server 2017 Express (14.0.1000.169) 上运行良好,您使用的是什么版本?我认为窗口函数 (OVER) 是在 SQL 2008 中添加的,所以如果您使用的是非常旧的版本,您可能无法使用它们?
  • @3N1GM4 我在 ssms 2017
  • @Amitchauhan - 使用 SSMS 2017 并不意味着底层数据库引擎就是那个版本。运行select @@version会得到什么结果?
  • 是的,我刚刚仔细检查了一下,此功能已完全添加到 SQL 2012 中,所以这可能是您遇到问题的原因。
【解决方案2】:
DECLARE @t TABLE (usr VARCHAR(100), dt DATE, amount INT);
INSERT INTO @t VALUES
('a', '2018-01-01', 100), -- 100
('a', '2018-02-01', 100), -- 200
('a', '2018-03-01', 100), -- 300
('a', '2018-04-01', 100), -- 400
('a', '2018-05-01', 100), -- 500
('b', '2018-01-01', 150), -- 150
('b', '2018-02-01', 150), -- 300
('b', '2018-03-01', 150), -- 450
('b', '2018-04-01', 150), -- 600
('b', '2018-05-01', 150); -- 750

declare @target int = 300;

with cte_RunningTotal as
(
    select  
        usr,
        dt,
        amount,
        sum(amount) over (partition by usr order by dt rows unbounded preceding) as runningTotal
    from @t 
)
select *
from cte_RunningTotal 
where runningTotal < @target + amount
order by usr, dt

【讨论】:

    猜你喜欢
    • 2013-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-31
    • 2012-05-19
    • 2023-04-01
    • 1970-01-01
    相关资源
    最近更新 更多