为未来年份创建年度收藏

时间:2014-06-17 14:56:13

标签: sql-server sql-server-2008

我目前正在尝试根据未结余额,剩余付款数量和这些付款的价值创建一种预测类型。我有一个数据结构如下

AccountNo|Balance   |Payments    |Frequency|PaymentsRemaining
12584    |1100      |100         |MX       |11
12887    |23656     |500         |MX       |47
13190    |2000      |22          |WK       |90

余额是欠款,付款是每月/每周付款,频率是每月或每周,剩余剩余。

我想要完成的是计算未来19年每年将收集多少作为一个单独的专栏。像这样 -

AccountNo|Balance  |Payments|Frequency|PaymentsRemaining|2014  |2015  |2016 |2017
12584    |1100     |100     |MX       |11               |1100  |0     |0    |0
12887    |23656    |500     |MX       |47               |568   |568   |568  |568
13190    |2000     |22      |WK       |90               |1144  |856   |     |

*数据只是一个例子,并且是手工制作的

我甚至不确定从哪里开始尝试在SQL中复制它,因为我从来没有必要创建类似于此的任何内容。我希望我已经提供了足够的数据,并希望有人可以帮助我,即使它指向我追求自己的方向。

1 个答案:

答案 0 :(得分:1)

2016年最后一行的值6212似乎不对,其余动态PIVOT可以解决此问题。

基本思路是以列

生成未来付款
WITH N(N) AS (
          SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9
), Y AS (
  SELECT _Year = YEAR(SYSDATETIME()) + u.N + t.N * 10
       , ID = u.N + t.N * 10
  FROM   N u
         CROSS JOIN N t
  WHERE  u.N + t.N * 10 < 20
), H (freq, times)  AS (
  SELECT 'WK', 52 UNION ALL SELECT 'MX', 12
)
SELECT AccountNo, Balance, Payments, Frequency, PaymentsRemaining
     , _Year
     , yearPayment = Payments 
     * ((PaymentsRemaining - H.times * ID + H.times) / 2
     - ABS(PaymentsRemaining - H.times * ID - H.times) / 2)
FROM   Data d
       INNER JOIN H ON d.Frequency = H.freq
       CROSS JOIN Y
WHERE  PaymentsRemaining - H.times * ID > 0
ORDER BY AccountNo, _Year

SQLFiddle demo

NY生成未来年份 H作为帮助表,存储支付数量将在一年内为频率类型进行(我希望这些是静态的)。
((PaymentsRemaining - H.times * ID + H.times) / 2 - ABS(PaymentsRemaining - H.times * ID - H.times) / 2)使用公式PaymentsRemaining - H.times * ID计算H.times(A + B) / 2 - ABS(A - B) / 2之间的最小值

然后使用PIVOT将列转换为行,下面是具有有限年数的测试查询

WITH N(N) AS (
          SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9
), Y AS (
  SELECT _Year = YEAR(SYSDATETIME()) + u.N + t.N * 10
       , ID = u.N + t.N * 10
  FROM   N u
         CROSS JOIN N t
  WHERE  u.N + t.N * 10 < 20
), H (freq, times)  AS (
  SELECT 'WK', 52 UNION ALL SELECT 'MX', 12
)
SELECT AccountNo, Balance, Payments, Frequency, PaymentsRemaining
     , [2014], [2015], [2016], [2017]
FROM   (SELECT AccountNo, Balance, Payments, Frequency, PaymentsRemaining
             , _Year
             , yearPayment = Payments 
             * ((PaymentsRemaining - H.times * ID + H.times) / 2
             - ABS(PaymentsRemaining - H.times * ID - H.times) / 2)
        FROM   Data d
               INNER JOIN H ON d.Frequency = H.freq
               CROSS JOIN Y
        WHERE  PaymentsRemaining - H.times * ID > 0) D
       PIVOT
       (MAX(yearPayment) FOR _Year IN ([2014], [2015], [2016], [2017])) pvt

SQLFiddle demo

可以动态

DECLARE @query AS NVARCHAR(MAX)
DECLARE @cols AS NVARCHAR(MAX)

WITH N(N) AS (
          SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9
), Y AS (
  SELECT _Year = YEAR(SYSDATETIME()) + u.N + t.N * 10
  FROM   N u
         CROSS JOIN N t
  WHERE  u.N + t.N * 10 < 20
)
SELECT @cols = STUFF((SELECT ',' + QUOTENAME([_Year])
                      FROM Y
                      ORDER BY _Year
                      FOR XML PATH(''), TYPE
                     ).value('.', 'NVARCHAR(MAX)') 
                        , 1, 1, '')
SELECT @query ='
WITH N(N) AS (
          SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9
), Y AS (
  SELECT _Year = YEAR(SYSDATETIME()) + u.N + t.N * 10
       , ID = u.N + t.N * 10
  FROM   N u
         CROSS JOIN N t
  WHERE  u.N + t.N * 10 < 20
), H (freq, times)  AS (
  SELECT ''WK'', 52 UNION ALL SELECT ''MX'', 12
)
SELECT AccountNo, Balance, Payments, Frequency, PaymentsRemaining
     , ' + @cols + '
FROM   (SELECT AccountNo, Balance, Payments, Frequency, PaymentsRemaining
             , _Year
             , yearPayment = Payments 
             * ((PaymentsRemaining - H.times * ID + H.times) / 2
             - ABS(PaymentsRemaining - H.times * ID - H.times) / 2)
        FROM   Data d
               INNER JOIN H ON d.Frequency = H.freq
               CROSS JOIN Y
        WHERE  PaymentsRemaining - H.times * ID > 0) D
       PIVOT
       (MAX(yearPayment) FOR _Year IN (' + @cols + ')) pvt'

execute(@query);

SQLFiddle demo


如果在第一年开始到付款开始之间存在延迟,则可以使用CTE H的相同形式将信息存储在其他CTE中例如,滞后六个月的值将是26周和6个月,这些值将被添加到年底留下的付款,因此剩余付款的公式将从

更改
PaymentsRemaining - H.times * ID

PaymentsRemaining - H.times * ID + L.times

基础查询,用了几年,变成了

WITH N(N) AS (
  SELECT N FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) D(N)
), Y AS (
  SELECT _Year = YEAR(SYSDATETIME()) + u.N + t.N * 10
       , ID = u.N + t.N * 10
  FROM   N u
         CROSS JOIN N t
  WHERE  u.N + t.N * 10 < 20
), H (freq, times)  AS (
  SELECT 'WK', 52 UNION ALL SELECT 'MX', 12
), L (freq, times) AS (
  SELECT 'WK', 26 UNION ALL SELECT 'MX', 6
)  
SELECT AccountNo, Balance, Payments, Frequency, PaymentsRemaining
     , [2014], [2015], [2016], [2017], [2018]
FROM   (SELECT AccountNo, Balance, Payments, Frequency, PaymentsRemaining
             , _Year
             , yearPayment = Payments 
             * (((PaymentsRemaining - H.times * ID + L.times + H.times) / 2
             - ABS(PaymentsRemaining - H.times * ID + L.times - H.times) / 2)
             - CASE WHEN _Year = Year(SYSDATETIME()) THEN L.Times
                    ELSE 0
               END)

        FROM   Data d
               INNER JOIN H ON d.Frequency = H.freq
               INNER JOIN L ON d.Frequency = L.freq
               CROSS JOIN Y
        WHERE  PaymentsRemaining - H.times * ID + L.times > 0) D
       PIVOT
       (MAX(yearPayment) FOR _Year IN ([2014], [2015], [2016], [2017])) pvt

SQLFiddle Demo

动态表单是

DECLARE @query AS NVARCHAR(MAX)
DECLARE @cols AS NVARCHAR(MAX)

WITH N(N) AS (
  SELECT N FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) D(N)
), Y AS (
  SELECT _Year = YEAR(SYSDATETIME()) + u.N + t.N * 10
  FROM   N u
         CROSS JOIN N t
  WHERE  u.N + t.N * 10 < 20
)
SELECT @cols = STUFF((SELECT ',' + QUOTENAME([_Year])
                      FROM Y
                      ORDER BY _Year
                      FOR XML PATH(''), TYPE
                     ).value('.', 'NVARCHAR(MAX)') 
                        , 1, 1, '')

SELECT @query ='
WITH N(N) AS (
  SELECT N FROM (VALUES (0), (1), (2), (3), (4)
                      , (5), (6), (7), (8), (9)) D(N)
), Y AS (
  SELECT _Year = YEAR(SYSDATETIME()) + u.N + t.N * 10
       , ID = u.N + t.N * 10
  FROM   N u
         CROSS JOIN N t
  WHERE  u.N + t.N * 10 < 20
), H (freq, times)  AS (
  SELECT ''WK'', 52 UNION ALL SELECT ''MX'', 12
), L (freq, times) AS (
  SELECT ''WK'', 26 UNION ALL SELECT ''MX'', 6
)  
SELECT AccountNo, Balance, Payments, Frequency, PaymentsRemaining
     , ' + @cols + '
FROM   (SELECT AccountNo, Balance, Payments, Frequency, PaymentsRemaining
             , _Year
             , yearPayment = Payments 
             * (((PaymentsRemaining - H.times * ID + L.times + H.times) / 2
             - ABS(PaymentsRemaining - H.times * ID + L.times - H.times) / 2)
             - CASE WHEN _Year = Year(SYSDATETIME()) THEN L.Times
                    ELSE 0
               END)

        FROM   Data d
               INNER JOIN H ON d.Frequency = H.freq
               INNER JOIN L ON d.Frequency = L.freq
               CROSS JOIN Y
        WHERE  PaymentsRemaining - H.times * ID + L.times > 0) D
       PIVOT
       (MAX(yearPayment) FOR _Year IN (' + @cols + ')) pvt'

execute(@query);

SQLFiddle Demo