分期付款
divide payments for periods
我有一个总值,其中包含自他注册以来的用户付款。
自注册之日起的每一年,用户都必须支付高级会员资格,但不必在同一年支付。用户可以支付e。例如。三年前。或者提前三年。
年份费率如下:
year | rate
2014 | 100
2015 | 100
2016 | 50
2017 | 150
2018 | 100
这是用户应该支付的价值。但他可以转移一个或多或少的钱,那是需要的。假设用户支付了 438 美元。他什么时候做的并不重要。可能是 2014 年或当年。我们只想知道他该交的有没有,也就是2014年至今年的费用。
我想做的是将他每年应支付的转账总额(计算并存储在一个变量中)除以检查他是否已支付所有款项或有多余的payment/underpayment。
我想得到的结果:
year | rate | payed
2014 | 100 | 100
2015 | 100 | 100
2016 | 50 | 50
2017 | 150 | 150
2018 | 100 | 38
所以用户已经支付了他应该支付的所有费用,并且有 38 美元的超额支付。
使用 loop/cursor 我可以做到,但想不出要使用的 window 功能。有什么提示吗?找不到如何为这种情况查询 google。
示例(解决方案有效但正在寻找 window 等效函数)
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
我不确定我是否 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 |
+----+------+-------+------+--------------+
或者现在我仔细观察可能是这样的:
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
这给了我们与第二个查询相同的结果。
如果您使用的是 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 |
+------+--------+---------+-----------+--------+
我有一个总值,其中包含自他注册以来的用户付款。 自注册之日起的每一年,用户都必须支付高级会员资格,但不必在同一年支付。用户可以支付e。例如。三年前。或者提前三年。
年份费率如下:
year | rate
2014 | 100
2015 | 100
2016 | 50
2017 | 150
2018 | 100
这是用户应该支付的价值。但他可以转移一个或多或少的钱,那是需要的。假设用户支付了 438 美元。他什么时候做的并不重要。可能是 2014 年或当年。我们只想知道他该交的有没有,也就是2014年至今年的费用。
我想做的是将他每年应支付的转账总额(计算并存储在一个变量中)除以检查他是否已支付所有款项或有多余的payment/underpayment。
我想得到的结果:
year | rate | payed
2014 | 100 | 100
2015 | 100 | 100
2016 | 50 | 50
2017 | 150 | 150
2018 | 100 | 38
所以用户已经支付了他应该支付的所有费用,并且有 38 美元的超额支付。
使用 loop/cursor 我可以做到,但想不出要使用的 window 功能。有什么提示吗?找不到如何为这种情况查询 google。
示例(解决方案有效但正在寻找 window 等效函数)
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
我不确定我是否 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 |
+----+------+-------+------+--------------+
或者现在我仔细观察可能是这样的:
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
这给了我们与第二个查询相同的结果。
如果您使用的是 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 |
+------+--------+---------+-----------+--------+