【问题标题】:divide payments for periods分期付款
【发布时间】:2017-08-18 12:41:31
【问题描述】:

自他注册以来,我的总价值包含用户付款。 自注册日期起的每一年,用户都必须支付高级会员资格,但不必在同一年支付。用户可以支付 e.例如。三年前。或者提前三年。

历年费率如下:

year | rate
2014 | 100
2015 | 100
2016 | 50
2017 | 150
2018 | 100

这是用户应该支付的价值。但是他可以转移所需的更少或更多的钱。假设用户支付了 438 美元。他什么时候做的并不重要。可能是 2014 年,也可能是今年。我们只想知道他是否已经支付了他应该支付的所有费用,即从 2014 年到本年度的费用。

我想要做的是将他每年应支付的转账总额(计算并存储在一个变量中)除以检查他是否已支付所有费用或是否有超额支付/少付。

我想得到的结果:

year | rate | payed
2014 | 100  | 100
2015 | 100  | 100
2016 | 50   | 50
2017 | 150  | 150
2018 | 100  | 38

所以用户已经支付了他应该更新的所有费用并且有 38 美元的超额支付。

使用循环/光标我可以做到这一点,但想不出要使用的窗口函数。有什么提示吗?找不到这种情况如何查询谷歌。

示例(解决方案正在运行,但正在寻找等效的窗口函数)

DECLARE @total DECIMAL = 438

;WITH data AS 
(
    SELECT *, @total - (SELECT SUM(tt.rate) FROM 
        (VALUES
        (2013, 100),
        (2014, 100),
        (2015, 100),
        (2016, 100),
        (2017, 100),
        (2018, 100)) tt([Year], Rate)
        WHERE tt.[Year] < t.[Year]) AS Payed
    FROM (VALUES
    (2013, 100),
    (2014, 100),
    (2015, 100),
    (2016, 100),
    (2017, 100),
    (2018, 100)) t([Year], Rate)
),
prepared AS
(
    SELECT d.[Year], @total AS Total, d.Rate, 
        CASE 
            WHEN ISNULL(d.Payed, d.Rate) <= 0 THEN NULL 
            WHEN @total < d.Rate THEN @total ELSE ISNULL(d.Payed, d.Rate) 
        END AS Payed
    FROM data d
)
SELECT p.[Year], @total AS Total, p.Rate, 
    CASE 
        WHEN p.Payed >= p.Rate THEN p.Rate 
        ELSE p.Payed 
    END Payed 
FROM prepared p

【问题讨论】:

  • coming 从哪里付款? “除以他每年的转账总额,以检查他是否已经支付了所有费用” 说什么?我们为什么要分裂?你不想把每年的转账加起来看看已经支付了什么吗?我迷路了。考虑显示所涉及的表格、示例数据和预期结果,以帮助澄清您的问题。
  • 添加示例代码和额外说明

标签: sql sql-server tsql window-functions


【解决方案1】:

如果您使用的是 SQL Server 2012+,则使用 SUM() OVER (ORDER BY...) 计算总未偿(或多付)金额非常容易。

样本数据

DECLARE @TotalPaid money = 438;

DECLARE @Rates TABLE (Y int, Rate money);
INSERT INTO @Rates (Y, Rate) VALUES
(2014, 100),
(2015, 100),
(2016,  50),
(2017, 150),
(2018, 100),
(2019, 100);

查询

WITH
CTE
AS
(
    SELECT
        Y
        ,Rate
        ,SUM(Rate) OVER (ORDER BY Y 
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS SumRate
    FROM @Rates
)
,CTE2
AS
(
    SELECT
        Y
        ,Rate
        ,SumRate
        ,@TotalPaid - SumRate AS PaidExtra
    FROM CTE
)
SELECT
    Y
    ,Rate
    ,SumRate
    ,PaidExtra
    ,CASE WHEN PaidExtra >= 0
        THEN Rate
        ELSE Rate + PaidExtra
    END AS Paid
FROM CTE2
ORDER BY Y
;

结果

+------+--------+---------+-----------+--------+
|  Y   |  Rate  | SumRate | PaidExtra |  Paid  |
+------+--------+---------+-----------+--------+
| 2014 | 100.00 | 100.00  | 338.00    | 100.00 |
| 2015 | 100.00 | 200.00  | 238.00    | 100.00 |
| 2016 | 50.00  | 250.00  | 188.00    | 50.00  |
| 2017 | 150.00 | 400.00  | 38.00     | 150.00 |
| 2018 | 100.00 | 500.00  | -62.00    | 38.00  |
| 2019 | 100.00 | 600.00  | -162.00   | -62.00 |
+------+--------+---------+-----------+--------+

【讨论】:

    【解决方案2】:

    我不确定我是否 100% 关注,但也许是这样:

    工作示例:http://rextester.com/OCHHR61883

    DECLARE @total DECIMAL = 438
    
    ;WITH data AS 
    (
        SELECT *, @total - (SELECT SUM(tt.rate) FROM 
            (VALUES
            (2013, 100),
            (2014, 100),
            (2015, 100),
            (2016, 100),
            (2017, 100),
            (2018, 100)) tt([Year], Rate)
            WHERE tt.[Year] < t.[Year]) AS Payed
        FROM (VALUES
        (2013, 100),
        (2014, 100),
        (2015, 100),
        (2016, 100),
        (2017, 100),
        (2018, 100)) t([Year], Rate)
    ),
    prepared AS
    (
        SELECT d.[Year], @total AS Total, d.Rate
              , case when @total-sum(rate) over( Order by year asc)<0  then 0 
                     else @total-sum(rate) over( Order by year asc) end  as RunningTotal
    
        FROM data d
    )
    Select * from prepared
    

    这给了我们:

    +----+------+-------+------+--------------+
    |    | Year | Total | Rate | RunningTotal |
    +----+------+-------+------+--------------+
    |  1 | 2013 |   438 |  100 |          338 |
    |  2 | 2014 |   438 |  100 |          238 |
    |  3 | 2015 |   438 |  100 |          138 |
    |  4 | 2016 |   438 |  100 |           38 |
    |  5 | 2017 |   438 |  100 |            0 |
    |  6 | 2018 |   438 |  100 |            0 |
    +----+------+-------+------+--------------+
    

    或者现在我仔细观察可能是这样的:

    http://rextester.com/AFR44863

    DECLARE @total DECIMAL = 438
    
    ;WITH data AS 
    (
        SELECT *, @total - (SELECT SUM(tt.rate) FROM 
            (VALUES
            (2013, 100),
            (2014, 100),
            (2015, 100),
            (2016, 100),
            (2017, 100),
            (2018, 100)) tt([Year], Rate)
            WHERE tt.[Year] < t.[Year]) AS Payed
        FROM (VALUES
        (2013, 100),
        (2014, 100),
        (2015, 100),
        (2016, 100),
        (2017, 100),
        (2018, 100)) t([Year], Rate)
    ),
    prepared AS
    (
        SELECT d.[Year], @total AS Total, d.Rate
              , case when @total-sum(rate) over( Order by year asc) > 0  then d.rate
                     when @total-sum(rate) over( Order by year asc) < 0 and sum(rate) over( Order by year asc) > 0 and d.rate+@total-sum(rate) over( Order by year asc) < 0 then 0              
                     when @total-sum(rate) over( Order by year asc) < 0 and sum(rate) over( Order by year asc) > 0 then d.rate+@total-sum(rate) over( Order by year asc)
                     end  as Paid
    
        FROM data d
    )
    Select * from prepared
    
    
    +----+------+-------+------+------+
    |    | Year | Total | Rate | Paid |
    +----+------+-------+------+------+
    |  1 | 2013 |   438 |  100 |  100 |
    |  2 | 2014 |   438 |  100 |  100 |
    |  3 | 2015 |   438 |  100 |  100 |
    |  4 | 2016 |   438 |  100 |  100 |
    |  5 | 2017 |   438 |  100 |   38 |
    |  6 | 2018 |   438 |  100 |    0 |
    +----+------+-------+------+------+
    

    或基于第一个查询的不同方法: http://rextester.com/BVSDS50299

    DECLARE @total DECIMAL = 438
    
    ;WITH data AS 
    (
        SELECT *, @total - (SELECT SUM(tt.rate) FROM 
            (VALUES
            (2013, 100),
            (2014, 100),
            (2015, 100),
            (2016, 100),
            (2017, 100),
            (2018, 100)) tt([Year], Rate)
            WHERE tt.[Year] < t.[Year]) AS Payed
        FROM (VALUES
        (2013, 100),
        (2014, 100),
        (2015, 100),
        (2016, 100),
        (2017, 100),
        (2018, 100)) t([Year], Rate)
    ),
    prepared AS
    (
        SELECT d.[Year], @total AS Total, d.Rate
              , case when @total-sum(rate) over( Order by year asc) < 0  then 0 
                     else case when @total-sum(rate) over( Order by year asc)> d.rate then d.rate else @total-sum(rate) over( Order by year asc) end  end
        as RunningTotal
    
        FROM data d
    )
    Select * from prepared
    

    这给了我们与第二个查询相同的结果。

    【讨论】:

      猜你喜欢
      • 2020-01-03
      • 2014-12-19
      • 2017-04-16
      • 2020-02-09
      • 2015-03-12
      • 2016-12-10
      • 2013-01-19
      • 2019-03-15
      • 2012-01-06
      相关资源
      最近更新 更多