基于规则集的收入随时间的分配

时间:2017-04-11 22:19:23

标签: sql-server algorithm

不确定我是否能够正确描述问题的标题,但我还没有在T-Sql中做过任何复杂的事情。这就是我想要实现的目标。

在几次查询和表构建之后,我在临时表中得到了数据(下面),这可能会成为一个永久表。 我需要帮助的部分是,需要根据以下数据创建另一个表来报告部分销售。

Table 1
LOC#    ITEM        sale_total  FiscalYear  FiscalPrd   TYPE    O_R DESC        LFT_EXP METHOD  PRD_MO  AG_PCNT PLAN_ID YEARS   STATUS
33      17879       12203.57    2017        3           A       O   Plan A      L       W       120     100.00  6       10      N
33      17903       579.97      2017        3           A       O   Plan A      L       W       120     100.00  6       10      N
33      18870       799.60      2017        3           A       O   Plan A      L       W       120     100.00  6       10      N
33      19739       319.95      2017        3           A       B   Plan C      E       F       36      100.00  8       3       N
33      21585       559.96      2017        3           B       O   Plan B      E       N       0       100.00  10      0       N

上表中sale_total的百分比需要根据下表进行分配(如果事情更简单,我还可以在上表中包含YR01-YR10列)。

Table 2
PLAN_ID EFDATE      MONTHS  METHOD  EPERCENT    YR01    YR02    YR03    YR04    YR05    YR06    YR07    YR08    YR09    YR10
6       2007-08-01  120     W       100.00      40.00   20.00   11.00   8.00    6.00    5.00    4.00    3.00    2.00    1.00
8       2012-11-12  36      F       100.00      33.00   33.00   34.00   0.00    0.00    0.00    0.00    0.00    0.00    0.00
10      2015-10-01  0       N       100.00      0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00

因此,对于LOC#33和项目18870,我的最终结果如下所示:(因此计算基于计划。对于此具体示例,项目18870属于计划6(表1)。表2具有分布所有计划。我们可以看到它分布在120个月以上,YR01-YR10的年度百分比分布。现在从表1的788.60的18870的销售数字,第1年的分布数学是(799.60 x 0.4)/ 12 = 26.65同样的方法可以追溯到本计划的第10年和计划8的第3年。对于10年计划,它将创建120个输入记录。每个项目每个月1个,位置)

LOC#    ITEM    FiscalYear  FiscalPrd   SALES   
33      18870   2017        4           26.65
33      18870   2017        5           26.85
33      18870   2017        6           26.85
.............................................
.............................................
33      18870   2017        11          26.85
33      18870   2017        12          26.85
33      18870   2018        1           13.33
33      18870   2018        2           13.33
.............................................
.............................................
33      18870   2018        11          13.33
33      18870   2018        12          13.33
.............................................
.............................................
33      18870   2027        2           0.67
33      18870   2027        3           0.67

它将按照表2中planID 6规定的百分比分配120个月以上的收入。对于planID 8,它将在36个月内进行相同的数学计算。 百分比可以随时间变化,用户将管理计划。还有其他位置,第一个表将包括当前会计期间的所有位置和所有项目(表2中显示的最大值为3)。我创建了一个状态列,以便在处理完数据后进行标记。

当下一个会计期间销售时,程序将更新上述结果集销售数字以包括新销售。它将每月运行一次以产生上述结果。

当我达到这一点并且不确定如何从这里接近时,会非常欣赏某些方向。

非常感谢。

@ZLK我在临时表中保存了第一次运行,然后添加了以下行

(33, 17879, 10000.00, 2017, 4, 6, 10),
(33, 17903, 500.00, 2017, 4, 6, 10),
(33, 18870, 800.00, 2017, 4, 6, 10),
(33, 19739, 300.00, 2017, 4, 8, 3),
(33, 21585, 500.00, 2017, 4, 10, 0);

这是保存第一组项目,在财政期间4中具有不同销售编号的位置。第一组被插入到表#finalDist中,第二组使用以下查询来插入和更新。

begin tran
if exists (select * FROM #finalDist AGE
                INNER JOIN #dist PrdAg on PrdAg.Loc = AGE.Loc 
                    and PrdAg.Item = AGE.Item 
                    and PrdAg.FiscalYear = AGE.FiscalYear 
                    and PrdAg.FiscalPrd = AGE.FiscalPrd)
begin
    UPDATE AGE 
        SET AGE.SALES = AGE.SALES + PrdAg.SALES 
    FROM #finalDist AGE
    INNER JOIN #dist PrdAg on PrdAg.Loc = AGE.Loc 
            and PrdAg.Item = AGE.Item 
            and PrdAg.FiscalYear = AGE.FiscalYear 
            and PrdAg.FiscalPrd = AGE.FiscalPrd
end
else
begin
    INSERT INTO #finalDist (Loc, Item, FiscalYear, FiscalPrd, SALES) 
    SELECT Loc, Item, FiscalYear, FiscalPrd, SALES from #dist PrdAg

        WHERE NOT EXISTS (SELECT AGE.Item FROM #finalDist AGE WHERE PrdAg.Loc = AGE.Loc 
                                                                    and PrdAg.Item = AGE.Item 
                                                                    and PrdAg.FiscalYear = AGE.FISCALYear
                                                                    and PrdAg.FiscalPrd = AGE.FiscalPrd)
end
commit tran

我意识到我无法做其他事情,因为我需要同时进行插入和更新。它将更新除财政期间3之外的所有内容,然后它将为此案例插入四行

Loc Item    FiscalYear  FiscalPrd   SALES
33  18870   2027           4       0.66666666666
33  19739   2020           4       8.50000000000
33  17903   2027           4       0.41666666666
33  17879   2027           4       8.33333333333

更新看起来像这样

33  17879   2017           4       406.78566666666 -- this row wasn't updated
33  17879   2017           5       740.11899999999

1 个答案:

答案 0 :(得分:0)

你能做到的一种方法是:

DECLARE @T TABLE (Loc INT, Item INT, Sales_Total DECIMAL(10, 2), FiscalYear INT, FiscalPrd INT, Plan_ID INT, YEARS INT);

INSERT @T (Loc, Item, Sales_Total, FiscalYear, FiscalPrd, Plan_ID, YEARS) VALUES
(33, 17879, 12203.57, 2017, 3, 6, 10),
(33, 17903, 579.97, 2017, 3, 6, 10),
(33, 18870, 799.60, 2017, 3, 6, 10),
(33, 19739, 319.95, 2017, 3, 8, 3),
(33, 21585, 559.96, 2017, 3, 10, 0);

DECLARE @T2 TABLE (PLAN_ID INT, YR01 DECIMAL(4, 2), YR02 DECIMAL(4, 2), YR03 DECIMAL(4, 2), YR04 DECIMAL(4, 2), YR05 DECIMAL(4, 2), YR06 DECIMAL(4, 2), YR07 DECIMAL(4, 2), YR08 DECIMAL(4, 2), YR09 DECIMAL(4, 2), YR10 DECIMAL(4, 2));

INSERT @T2 (PLAN_ID, YR01, YR02, YR03, YR04, YR05, YR06, YR07, YR08, YR09, YR10) VALUES
(6, 40.00, 20.00, 11.00, 8.00, 6.00, 5.00, 4.00, 3.00, 2.00, 1.00),
(8, 33.00, 33.00, 34.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00),
(10, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00);

WITH CTE(N) AS (SELECT 1 FROM (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) AS A(B)),
    CTE2(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM CTE AS A CROSS JOIN CTE) -- Numbers 1-121
SELECT T.Loc, 
       T.Item,
       FiscalYear = DATEPART(YEAR, DATEADD(MONTH, CTE2.N, DATEFROMPARTS(T.FiscalYear, T.FiscalPrd, 1))),
       FiscalPrd = DATEPART(MONTH, DATEADD(MONTH, CTE2.N, DATEFROMPARTS(T.FiscalYear, T.FiscalPrd, 1))),
       SALES = T.Sales_Total * C.Vals / 100 / 12
FROM @T AS T
JOIN CTE2
    ON CTE2.N <= T.YEARS * 12 
JOIN 
(
    SELECT T.PLAN_ID, C.Y, C.Vals
    FROM @T2 AS T
    CROSS APPLY (VALUES (1, YR01), (2, YR02), (3, YR03), (4, YR04), (5, YR05), (6, YR06), (7, YR07), (8, YR08), (9, YR09), (10, YR10)) AS C(Y, Vals) -- Unpivot
) AS C
    ON C.PLAN_ID = T.PLAN_ID
    AND C.Y = (CTE2.N + 11) / 12
ORDER BY T.Loc, T.Item, CTE2.N;

基本上你想得到一个数字列表(这里最多120个,但如果超过10年,你需要更多)并交叉加入你的桌子。然后将表2中的值取消,并将它们链接到数字表中(按年份分组)。

修改

考虑到价值可以改变并且假设结束日期仍然相同而且没有增加,这就是我认为你正在寻找的:

DECLARE @T TABLE (Loc INT, Item INT, Sales_Total DECIMAL(10, 2), FiscalYear INT, FiscalPrd INT, Plan_ID INT, YEARS INT);

INSERT @T (Loc, Item, Sales_Total, FiscalYear, FiscalPrd, Plan_ID, YEARS) VALUES
(33, 17879, 12203.57, 2017, 3, 6, 10),
(33, 17903, 579.97, 2017, 3, 6, 10),
(33, 18870, 799.60, 2017, 3, 6, 10),
(33, 19739, 319.95, 2017, 3, 8, 3),
(33, 21585, 559.96, 2017, 3, 10, 0),
(33, 17879, 10000.00, 2017, 4, 6, 10),
(33, 17903, 500.00, 2017, 4, 6, 10),
(33, 21585, 500.00, 2017, 4, 10, 0),
(33, 17879, 5000.00, 2017, 5, 6, 10),
(33, 17879, 6900.00, 2017, 7, 6, 10);

DECLARE @T2 TABLE (PLAN_ID INT, YR01 DECIMAL(4, 2), YR02 DECIMAL(4, 2), YR03 DECIMAL(4, 2), YR04 DECIMAL(4, 2), YR05 DECIMAL(4, 2), YR06 DECIMAL(4, 2), YR07 DECIMAL(4, 2), YR08 DECIMAL(4, 2), YR09 DECIMAL(4, 2), YR10 DECIMAL(4, 2));

INSERT @T2 (PLAN_ID, YR01, YR02, YR03, YR04, YR05, YR06, YR07, YR08, YR09, YR10) VALUES
(6, 40.00, 20.00, 11.00, 8.00, 6.00, 5.00, 4.00, 3.00, 2.00, 1.00),
(8, 33.00, 33.00, 34.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00),
(10, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00);

WITH CTE(N) AS (SELECT 1 FROM (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) AS A(B)),
    CTE2(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 FROM CTE AS A CROSS JOIN CTE) -- Numbers 0-120

SELECT T.Loc, 
       T.Item,
       FiscalYear = DATEPART(YEAR, DATEADD(MONTH, CTE2.N, T.FirstDate)),
       FiscalPrd = DATEPART(MONTH, DATEADD(MONTH, CTE2.N, T.FirstDate)),
       SALES = T.TotalToDate * C.Vals / 100 / 12
FROM
(
    SELECT T.Loc,
           T.Item,
           T.YEARS,
           T.Plan_ID,
           C.TotalToDate,
           FirstDate = CAST(DATEADD(MONTH, 1, CAST(MIN(FiscalYear) OVER (PARTITION BY Loc, Item) AS CHAR(4)) + '-' +
                       CAST(MIN(FiscalPrd) OVER (PARTITION BY Loc, Item) AS VARCHAR(2)) + '-01') AS DATE),
           CurrentDate = CAST(DATEADD(MONTH, 1, CAST(FiscalYear AS CHAR(4)) + '-' + CAST(FiscalPrd AS VARCHAR(2)) + '-01') AS DATE),
           C2.NextDate
    FROM @T AS T
    CROSS APPLY 
    (
        SELECT SUM(Sales_Total) 
        FROM @T 
        WHERE Loc = T.Loc
        AND Item = T.Item
        AND FiscalYear * 12 + FiscalPrd <= T.FiscalYear * 12 + T.FiscalPrd
    ) AS C(TotalToDate)
    OUTER APPLY
    (
        SELECT CAST(DATEADD(MONTH, 1, CAST(MIN(FiscalYear) AS CHAR(4)) + '-' +
               CAST(MIN(FiscalPrd) AS VARCHAR(2)) + '-01') AS DATE)
        FROM @T
        WHERE Loc = T.Loc
        AND Item = T.Item
        AND FiscalYear * 12 + FiscalPrd > T.FiscalYear * 12 + T.FiscalPrd
    ) AS C2(NextDate)
) AS T
JOIN CTE2
    ON CTE2.N <= T.YEARS * 12 - 1
    AND CTE2.N >= DATEDIFF(MONTH, T.FirstDate, T.CurrentDate)
    AND DATEADD(MONTH, CTE2.N, T.FirstDate) < COALESCE(T.NextDate, DATEADD(MONTH, T.YEARS * 12, T.FirstDate))
JOIN 
(
    SELECT T.PLAN_ID, C.Y, C.Vals
    FROM @T2 AS T
    CROSS APPLY (VALUES (1, YR01), (2, YR02), (3, YR03), (4, YR04), (5, YR05), (6, YR06), (7, YR07), (8, YR08), (9, YR09), (10, YR10)) AS C(Y, Vals) -- Unpivot
) AS C
    ON C.PLAN_ID = T.PLAN_ID
    AND C.Y = (CTE2.N + 12) / 12
ORDER BY T.Loc, T.Item, CTE2.N;

第二次编辑(修改后的开始日期为宣布的月份,例如2017年3月意味着计划于2017年3月开始,而不是2017年4月。唯一的区别是调整row_number连接数): < / p>

/*
    @PlanTable: A static table for plans.
*/

DECLARE @PlanTable TABLE (PLAN_ID INT, YR01 DECIMAL(4, 2), YR02 DECIMAL(4, 2), YR03 DECIMAL(4, 2), YR04 DECIMAL(4, 2), YR05 DECIMAL(4, 2), YR06 DECIMAL(4, 2), YR07 DECIMAL(4, 2), YR08 DECIMAL(4, 2), YR09 DECIMAL(4, 2), YR10 DECIMAL(4, 2));
INSERT @PlanTable (PLAN_ID, YR01, YR02, YR03, YR04, YR05, YR06, YR07, YR08, YR09, YR10) VALUES
(6, 40.00, 20.00, 11.00, 8.00, 6.00, 5.00, 4.00, 3.00, 2.00, 1.00),
(8, 33.00, 33.00, 34.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00),
(10, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00);   

/* 
    @RecordsToInsert: The values you're inserting into your final table. Starting with FiscalYear 2017 and FiscalPrd 3 as an example.
*/

DECLARE @RecordsToInsert TABLE (Loc INT, Item INT, Sales_Total DECIMAL(10, 2), FiscalYear INT, FiscalPrd INT, Plan_ID INT, YEARS INT);
INSERT @RecordsToInsert (Loc, Item, Sales_Total, FiscalYear, FiscalPrd, Plan_ID, YEARS) VALUES
(33, 17879, 12203.57, 2017, 3, 6, 10),
(33, 17903, 579.97, 2017, 3, 6, 10),
(33, 18870, 799.60, 2017, 3, 6, 10),
(33, 19739, 319.95, 2017, 3, 8, 3),
(33, 21585, 559.96, 2017, 3, 10, 0);

/*
    @FinalTable: This is the table you're using to insert the data. 
*/

DECLARE @FinalTable TABLE (Loc INT, Item INT, FiscalYear INT, FiscalPrd INT, SALES DECIMAL(10, 2));

/*
    Start by inserting the first lot of information into the final table.
*/

WITH CTE(N) AS (SELECT 1 FROM (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) AS A(B)),
    CTE2(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 FROM CTE AS A CROSS JOIN CTE) -- Numbers 0-120
INSERT @FinalTable (Loc, Item, FiscalYear, FiscalPrd, SALES)
SELECT T.Loc, 
       T.Item,
       FiscalYear = DATEPART(YEAR, DATEADD(MONTH, CTE2.N, CAST(T.FiscalYear AS CHAR(4)) + '-' + CAST(T.FiscalPrd AS VARCHAR(2)) + '-01')),
       FiscalPrd = DATEPART(MONTH, DATEADD(MONTH, CTE2.N, CAST(T.FiscalYear AS CHAR(4)) + '-' + CAST(T.FiscalPrd AS VARCHAR(2)) + '-01')),
       SALES = T.Sales_Total * C.Vals / 100 / 12
FROM @RecordsToInsert AS T
JOIN CTE2
    ON CTE2.N <= T.YEARS * 12 - 1
JOIN 
(
    SELECT T.PLAN_ID, C.Y, C.Vals
    FROM @PlanTable AS T
    CROSS APPLY (VALUES (1, YR01), (2, YR02), (3, YR03), (4, YR04), (5, YR05), (6, YR06), (7, YR07), (8, YR08), (9, YR09), (10, YR10)) AS C(Y, Vals) -- Unpivot
) AS C
    ON C.PLAN_ID = T.PLAN_ID
    AND C.Y = CTE2.N / 12 + 1
ORDER BY T.Loc, T.Item, CTE2.N;

/*
    Next we want to insert records for the next FiscalPrd, so we get rid of the last ones and put in FiscalYear 2017, FiscalPrd 4.
*/

DELETE @RecordsToInsert;
INSERT @RecordsToInsert (Loc, Item, Sales_Total, FiscalYear, FiscalPrd, Plan_ID, YEARS) VALUES
(33, 17879, 10000.00, 2017, 4, 6, 10),
(33, 17903, 500.00, 2017, 4, 6, 10),
(33, 18870, 800.00, 2017, 4, 6, 10),
(33, 19739, 300.00, 2017, 4, 8, 3),
(33, 21585, 500.00, 2017, 4, 10, 0);

/*
    Then we update the final table with the new sales total, ignoring periods that were prior to the fiscal year and period being inserted.
*/

UPDATE T
SET SALES = SALES_NEW
FROM (
SELECT FT.Loc,
       FT.Item,
       FT.FiscalYear,
       FT.FiscalPrd,
       FT.SALES,
       SALES_NEW = (SUM(FT.SALES) OVER (PARTITION BY FT.Loc, FT.Item) + R.Sales_Total)  * C.Vals / 100 / COUNT(1) OVER (PARTITION BY FT.Loc, FT.Item, (FT.RN + 11) / 12)
FROM (SELECT *, RN = ROW_NUMBER() OVER (PARTITION BY Loc, Item ORDER BY FiscalYear, FiscalPrd) FROM @FinalTable) AS FT
JOIN @RecordsToInsert AS R
    ON R.Loc = FT.Loc
    AND R.Item = FT.Item
    AND R.FiscalYear * 12 + R.FiscalPrd <= FT.FiscalYear * 12 + FT.FiscalPrd
JOIN 
(
    SELECT T.PLAN_ID, C.Y, C.Vals
    FROM @PlanTable AS T
    CROSS APPLY (VALUES (1, YR01), (2, YR02), (3, YR03), (4, YR04), (5, YR05), (6, YR06), (7, YR07), (8, YR08), (9, YR09), (10, YR10)) AS C(Y, Vals) -- Unpivot
) AS C
    ON C.PLAN_ID = R.PLAN_ID
    AND C.Y = (FT.RN + 11) / 12
) T;

/*
    For the first year, the sales for 2017/5 onwards have changed to (totalsales - firstmonth) * 40% / 11
    For the second years onward, the sales have changed to totalsales * whatever% / 12
    The first month of the first year remains the same.
*/

SELECT Loc, Item, FiscalYear, FiscalPrd, SALES
FROM @FinalTable
ORDER BY Loc, Item, FiscalYear, FiscalPrd;