我正在为自己编写一个小型预算应用程序,并且对查询有点困惑。
这是一个用于计算每个类别每月预算总额的查询。
每个月,您都可以针对该类别设置规则,以影响主预算缓冲区,或者仅将其限制在该类别中。
它是如何工作的:
如果它是受限的,那么它始终(即使您超支)也将在下个月继续对其计数,直到将其重新设置为影响缓冲区为止。
注意:最后一个负数应计入第一次出现的ImpactsBuffer中,以便您可以还清该月的任何负数。因此,负数会在影响缓冲月份的第一个月后停止结转。
这是我的示例数据,以及余额应为列,以显示其工作原理。
CREATE TABLE [BGT].[BudgetTemp2] ( [Month] date, [CategoryID] int, [Budgeted] money, [Outflows] money, [BudgetedAndOutflows] money, [OverspendingHandling] nvarchar(50), [BalanceShouldBe] money )
INSERT INTO [BGT].[BudgetTemp2]
VALUES
( N'2016-01-01T00:00:00', 116, 0.0000, -500.0000, -500.0000, N'AffectsBuffer', -500.0000 ),
( N'2016-02-01T00:00:00', 116, 50.0000, -200.0000, -150.0000, N'AffectsBuffer', -150.0000 ),
( N'2016-03-01T00:00:00', 116, 50.0000, 0.0000, 50.0000, N'AffectsBuffer', 50.0000 ),
( N'2016-04-01T00:00:00', 116, 0.0000, -350.0000, -350.0000, N'AffectsBuffer', -300.0000 ),
( N'2016-05-01T00:00:00', 116, 100.0000, 0.0000, 100.0000, N'AffectsBuffer', 100.0000 ),
( N'2016-06-01T00:00:00', 116, 0.0000, 10699.8900, 10699.8900, N'AffectsBuffer', 10799.8900 ),
( N'2016-07-01T00:00:00', 116, 4147.8800, -16707.6900, -12559.8100, N'Confined', -1759.9200 ),
( N'2016-08-01T00:00:00', 116, 0.0000, -4504.9600, -4504.9600, N'Confined', -6264.8800 ),
( N'2016-09-01T00:00:00', 116, 0.0000, -5486.5400, -5486.5400, N'Confined', -11751.4200 ),
( N'2016-10-01T00:00:00', 116, 0.0000, -3795.7700, -3795.7700, N'Confined', -15547.1900 ),
( N'2016-11-01T00:00:00', 116, 0.0000, 407.3200, 407.3200, N'Confined', -15139.8700 ),
( N'2016-12-01T00:00:00', 116, 298324.5900, -282434.7200, 15889.8700, N'Confined', 750.0000 ),
( N'2017-01-01T00:00:00', 116, 4196.4400, -4196.4400, 0.0000, N'Confined', 750.0000 ),
( N'2017-02-01T00:00:00', 116, 10999.9000, -15199.9000, -4200.0000, N'Confined', -3450.0000 ),
( N'2017-03-01T00:00:00', 116, 4987.6600, -2875.1800, 2112.4800, N'Confined', -1337.5200 ),
( N'2017-04-01T00:00:00', 116, 4899.1600, -65100.0000, -60200.8400, N'Confined', -61538.3600 ),
( N'2017-05-01T00:00:00', 116, 504.3200, 0.0000, 504.3200, N'Confined', -61034.0400 ),
( N'2017-06-01T00:00:00', 116, 0.0000, -104505.0300, -104505.0300, N'Confined', -165539.0700 ),
( N'2017-07-01T00:00:00', 116, 0.0000, -72317.7100, -72317.7100, N'Confined', -237856.7800 ),
( N'2017-08-01T00:00:00', 116, 0.0000, -82.2200, -82.2200, N'Confined', -237939.0000 ),
( N'2017-09-01T00:00:00', 116, 237916.0900, -814.4600, 237101.6300, N'Confined', -837.3700 ),
( N'2017-10-01T00:00:00', 116, 906.8300, -1523.5500, -616.7200, N'Confined', -1454.0900 ),
( N'2017-11-01T00:00:00', 116, 175.6100, -3348.5500, -3172.9400, N'Confined', -4627.0300 ),
( N'2017-12-01T00:00:00', 116, -14.4400, -1763.4400, -1777.8800, N'AffectsBuffer', -6404.9100 ),
( N'2018-01-01T00:00:00', 116, 40.0000, -20.0000, 20.0000, N'AffectsBuffer', 20.0000 ),
( N'2018-02-01T00:00:00', 116, 0.0000, -75.4300, -75.4300, N'AffectsBuffer', -55.4300 ),
( N'2018-04-01T00:00:00', 116, 4899.7400, -4899.7400, 0.0000, N'AffectsBuffer', 0.0000 ),
( N'2018-05-01T00:00:00', 116, 750.3900, -750.3900, 0.0000, N'AffectsBuffer', 0.0000 ),
( N'2018-06-01T00:00:00', 116, 0.0000, -500.0000, -500.0000, N'Confined', -500.0000 ),
( N'2018-07-01T00:00:00', 116, 100.0000, 0.0000, 0.0000, N'Confined', -400.0000 ),
( N'2018-08-01T00:00:00', 116, 200.0000, -100.0000, 100.0000, N'Confined', -300.0000 ),
( N'2018-09-01T00:00:00', 116, 0.0000, 0.0000, 0.0000, N'AffectsBuffer', -300.0000 ),
( N'2018-10-01T00:00:00', 116, 100.0000, -50.0000, 50.0000, N'AffectsBuffer', 50.0000 ),
( N'2018-11-01T00:00:00', 116, 0.0000, -500.0000, -500.0000, N'AffectsBuffer', -450.0000 ),
( N'2018-12-01T00:00:00', 116, 100.0000, -50.0000, 50.0000, N'AffectsBuffer', 50.0000 ),
( N'2019-01-01T00:00:00', 116, 0.0000, 0.0000, 0.0000, N'AffectsBuffer', 50.0000 ),
( N'2019-02-01T00:00:00', 116, 100.0000, 0.0000, 100.0000, N'Confined', 150.0000 ),
( N'2019-03-01T00:00:00', 116, 0.0000, -50.0000, -50.0000, N'Confined', 100.0000 ),
( N'2019-04-01T00:00:00', 116, 0.0000, -200.0000, -200.0000, N'Confined', -100.0000 ),
( N'2019-05-01T00:00:00', 116, 0.0000, -200.0000, -200.0000, N'Confined', -300.0000 ),
( N'2019-06-01T00:00:00', 116, 0.0000, 0.0000, 0.0000, N'AffectsBuffer', -300.0000 ),
( N'2019-07-01T00:00:00', 116, 0.0000, 0.0000, 0.0000, N'AffectsBuffer', 0.0000 )
--DROP TABLE [BGT].[BudgetTemp2]
到目前为止,这是我的查询,但是如您所见,它最终变得一团糟。我觉得我在这里拥有一切都能够正确地做到这一点,我只是缺少了一些关键的东西。直到我在Affects Buffer和Confined之间进行更改之前,它的计数都是正确的。
-- FROM: https://stackoverflow.com/a/23020788
;WITH c1 AS (
SELECT
*,
LAG([BudgetTemp].[OverspendingHandling], 1, [BudgetTemp].[OverspendingHandling])
OVER (PARTITION BY [BudgetTemp].[CategoryID] ORDER BY [BudgetTemp].[Month]) AS PrevOverspendingHandling
FROM [BGT].[BudgetTemp2] [BudgetTemp]
), c2 AS (
SELECT
*,
SUM(CASE WHEN [c1].[OverspendingHandling] <> [c1].[PrevOverspendingHandling] THEN 1 ELSE 0 END)
OVER (PARTITION BY [c1].[CategoryID] ORDER BY [c1].[Month]) AS Ranker
FROM [c1]
), c3 AS (
SELECT
*,
SUM([c2].[BudgetedAndOutflows]) OVER (PARTITION BY [c2].[CategoryID] ORDER BY [c2].[Month] ROWS UNBOUNDED PRECEDING) AS rt,
SUM([c2].[BudgetedAndOutflows]) OVER (PARTITION BY [c2].[CategoryID], [c2].[Ranker] ORDER BY [c2].[Month] ROWS UNBOUNDED PRECEDING) AS rt2
FROM [c2]
), c4 AS (
SELECT
*,
MIN(rt) OVER (PARTITION BY [c3].[CategoryID] ORDER BY [c3].[Month] ROWS UNBOUNDED PRECEDING) AS rt_min,
MIN(rt) OVER (PARTITION BY [c3].[CategoryID], [c3].[Ranker] ORDER BY [c3].[Month] ROWS UNBOUNDED PRECEDING) AS rt2_min
FROM [c3]
), c5 AS (
SELECT
*,
-- WE WANT TO LAG MIN_CUR BY 1 SO THAT WE STILL GET A SINGLE NEGATIVE FOR THAT MONTH, BUT IT RESETS THE NEXT MONTH
LAG(rt_min, 1, 0) OVER (PARTITION BY [c4].[CategoryID] ORDER BY [c4].[Month]) AS rt_min_lag,
LAG(rt2_min, 1, 0) OVER (PARTITION BY [c4].[CategoryID], [c4].[Ranker] ORDER BY [c4].[Month]) AS rt2_min_lag
FROM [c4]
)
SELECT
[Month],
[CategoryID],
[Budgeted],
[Outflows],
[BudgetedAndOutflows],
[OverspendingHandling],
[PrevOverspendingHandling],
[Ranker],
[rt],
[rt_min],
[rt_min_lag],
[rt2],
[rt2_min],
[rt2_min_lag],
[rt] + (CASE WHEN [rt_min_lag] < 0 THEN -[rt_min_lag] ELSE 0 END) AS Balance1,
[rt2] + (CASE WHEN [rt2_min_lag] < 0 THEN -[rt2_min_lag] ELSE 0 END) AS Balance2,
[BalanceShouldBe]
FROM [c5]
ORDER BY
[CategoryID],
[Month]
任何帮助将不胜感激!
干杯
编辑: @dfundako发现了几个不正确的数据条目,并且我更新了以下内容:
我也在主插入脚本中对此进行了更新。
答案 0 :(得分:1)
在某些情况下,您需要根据自上次重置以来的运行总计重置运行总计。
这不是为窗口函数设计的。
最佳性能的方法可能是使用CLR按顺序处理流,并根据需要使用重置逻辑输出运行总计(similar to this though you would need to add the logic to reset the totals)。如果数据较小,则可以使用递归CTE。
WITH T
AS (SELECT *,
ROW_NUMBER()
OVER (
PARTITION BY CategoryID
ORDER BY Month) AS RN
FROM BGT.BudgetTemp2),
R
AS (SELECT *,
Budgeted + Outflows AS BalanceCalculated
FROM T
WHERE RN = 1
UNION ALL
SELECT T.Month,
T.CategoryID,
T.Budgeted,
T.Outflows,
T.BudgetedAndOutflows,
T.OverspendingHandling,
T.BalanceShouldBe,
T.RN,
T.Budgeted + T.Outflows
+ IIF(R.OverspendingHandling = 'AffectsBuffer' AND R.BalanceCalculated < 0, 0, R.BalanceCalculated)
FROM T
JOIN R
ON R.CategoryID = T.CategoryID
AND T.RN = R.RN + 1)
SELECT *
FROM R;
以上使用行号查找每个CategoryID的下一行。如果保证所有日期都是该月的第一天并且没有丢失的月份,则可以改用R.CategoryID = T.CategoryID AND T.Month = DATEADD(1, MONTH, R.Month)
的连接谓词。这样做会更有效率-尤其是如果您对此有支持的索引。