划分期间付款

时间:2017-08-18 12:41:32

标签: sql sql-server tsql window-functions

我的总价值包含用户付款,因为他注册了。 自注册之日起的每年,用户必须支付高级会员资格,但不必在同一年支付。用户可以付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

2 个答案:

答案 0 :(得分: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 |
+------+--------+---------+-----------+--------+

答案 1 :(得分:0)

我不确定我是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

给出了与第二个查询相同的结果。