SQL Server中具有滚动周的案例陈述 - 更简单的方法吗?

时间:2017-12-01 17:13:29

标签: sql sql-server

我正在创建一个case语句(在视图中),它测试2个变量并输出另一个字段的值。它测试的2个变量是ForecastMultiplier和一个获取当前周数的函数。预测乘数表示需要多少个月才能生效。然后,我有一个有12个月预测的表格,我用它来计算我们应该有多少手头库存。

以下是一些例子:

  • 如果预测乘数= 1且我们处于该月的第一周,那么我将采用所有Month1预测(当前月份)。
  • 如果预测乘数= 1且我们处于该月的第二周,那么我将采用Month1预测的75%加上Month2预测的25%。
  • 如果预测乘数= 5且我们处于该月的第一周,那么我将采用Month1预测加上Month2预测加上Month3预测加上Month4预测加上Month5预测
  • 如果预测乘数= 5且我们处于该月的第三周,那么我将采用50%的Month1预测加上Month2预测加上Month3预测加上Month4预测加上Month5预测加上Month6预测的25%

并且还需要对.5,1.5等的预测乘数进行此操作。所以你可以看到,使用一个简单的选择案例会变得非常麻烦。

以下是一些案例陈述,您可以看到:

CASE 
    when mpi.UseForecast = 0 then mpi.MinimumOnHandQuantity
    when mpi.ForecastMultiplier = 1 and dbo.GetWeekNumber() = 1 Then fp.month1
    when mpi.ForecastMultiplier = 1 and dbo.GetWeekNumber() = 2 Then (fp.month1 * .75) + (fp.MONTH2 * .25)
    when mpi.ForecastMultiplier = 1 and dbo.GetWeekNumber() = 3 Then (fp.month1 * .50) + (fp.MONTH2 * .50)
    when mpi.ForecastMultiplier = 1 and dbo.GetWeekNumber() = 4 Then (fp.month1 * .25) + (fp.MONTH2 * .75)
    when mpi.ForecastMultiplier = 1.5 and dbo.GetWeekNumber() = 1 Then fp.month1 + (fp.month2 * .5)
    when mpi.ForecastMultiplier = 1.5 and dbo.GetWeekNumber() = 2 Then (fp.month1 * .75) + (fp.month2 * .5)
    when mpi.ForecastMultiplier = 1.5 and dbo.GetWeekNumber() = 3 Then (fp.month1 * .50) + (fp.month2 * .5)
    when mpi.ForecastMultiplier = 1.5 and dbo.GetWeekNumber() = 4 Then (fp.month1 * .25) + (fp.month2 * .5)
    when mpi.ForecastMultiplier = 2 and dbo.GetWeekNumber() = 1 Then fp.month1 + fp.month2
    when mpi.ForecastMultiplier = 2 and dbo.GetWeekNumber() = 2 Then (fp.month1 * .75) + fp.month2 + (fp.MONTH3 * .25)
    when mpi.ForecastMultiplier = 2 and dbo.GetWeekNumber() = 3 Then (fp.month1 * .50) + fp.month2 + (fp.MONTH3 * .50)
    when mpi.ForecastMultiplier = 2 and dbo.GetWeekNumber() = 4 Then (fp.month1 * .25) + fp.month2 + (fp.MONTH3 * .75)

SQL语句有效但有一些问题。这很麻烦。我添加的月数越来越慢(它永远不会超过12个月)。此外,它还不需要花费5周的时间来计算。最后,如果可能的话,我们宁愿滚动天,但通过这种方法几乎是不可能的。有没有办法通过存储过程和/或函数执行此操作?顺便说一句,我最初是在Scaler函数中执行select语句,但它非常慢,所以现在我正在尝试查看。

我正在寻找的是一种更好的方法来实现同样的结果,或者我在上一段中提到的更好的结果。

这是Schema和一些数据:

MinimumProductInfoes(MPI):

Code Region UseForecast ForecastMultipler MinimumOnHand
---- ------ ----------- ----------------- -------------
1    R1     0           0                 50
1    R2     1           2                 0
2    R1     1           4.5               0
2    R3     1           3                 0
3    R1     1           12                0

ForecastPivot(FP):

Code  Region  Month1 Month2 Month3 Month4 Month5 Month6 Month7 ... Month12
----  ------  ------ ------ ------ ------ ------ ------ ------     -------
1     R1      200    200    50     75     200    50     50         80
1     R2      500    500    500    500    500    500    500        500
2     R1      1000   0      0      0      0      0      0          0
2     R3      25     1000   1000   1000   1000   1000   1000       1000

2 个答案:

答案 0 :(得分:0)

如果您将一个乘数组合的案例合并为:

,您的陈述可能会变得不那么麻烦,缩短了近4倍。
var errors = ko.validation.group(self.personalViewModel, { deep: true });
    if (fieldHidden()) {
        errors()[0] = "New error message";
    }
    errors.showAllMessages();

不知何故,我认为这仍然适用于第5周。

答案 1 :(得分:0)

SQL Fiddle

架构设置(SQLFiddle使用MS SQL 2014,但这在2008年仍然可以使用。)

CREATE TABLE MPI ( Code int, Region varchar(5), UseForecast bit
    , ForecastMultiplier decimal(5,1), MinimumOnHand int ) ;
INSERT INTO MPI ( Code, Region, UseForecast, ForecastMultiplier, MinimumOnHand )
VALUES 
      ( 1,'R1',0,0,50 )
    , ( 1,'R2',1,2,0)
    , ( 2,'R1',1,4.5,0)
    , ( 2,'R3',1,3,0)
    , ( 3,'R1',1,12,0)
;

CREATE TABLE FP ( Code int,  Region varchar(5), Month1 int, Month2 int
    , Month3 int, Month4 int, Month5 int, Month6 int, Month7 int
    , Month8 int, Month9 int, Month10 int, Month11 int, Month12 int ) ;
INSERT INTO FP (Code, Region, Month1, Month2, Month3, Month4, Month5
    , Month6, Month7, Month8, Month9, Month10, Month11, Month12 )
VALUES 
      ( 1,'R1',200,200,50,75,200,50,50,50,50,50,50,80 )
    , ( 1,'R2',500,500,500,500,500,500,500,50,50,50,50,500 )
    , ( 2,'R1',1000,0,0,0,0,0,0,50,50,50,50,0 )
    , ( 2,'R3',25,1000,1000,1000,1000,1000,1000,50,50,50,50,1000 )
;

构建简单日历表

注意:日历表适用于您的数据库,因此请添加您需要的任何计算。我经常使用Aaron Bertrand的修改版本的例子:https://github.com/shawnoden/SQL_Stuff/blob/master/sql_CreateDateDimension.sql

/* #dim is just a temp holding table for intermediate calculations. */
CREATE TABLE #dim (
      theDate           date        PRIMARY KEY
    , theDay            AS DATEPART(day, theDate)           --int
    , theWeek           AS DATEPART(week, theDate)          --int 
    , theMonth          AS DATEPART(month, theDate)         --int
    , theYear           AS DATEPART(year, theDate)          --int
    , yyyymmdd          AS CONVERT(char(8), theDate, 112)   /* yyyymmdd */
    , mm_dd_yy          AS CONVERT(char(10), theDate, 101)  /* mm/dd/yyyy */
);

/**************************************************************************/
/* Use the catalog views to generate as many rows as we need. */

INSERT INTO #dim ( theDate ) 
SELECT d
FROM (
    SELECT d = DATEADD(day, rn - 1, '20170101')
    FROM 
    (
        SELECT TOP (DATEDIFF(day, '20160101', '20190101')) 
            rn = ROW_NUMBER() OVER (ORDER BY s1.object_id)
        FROM sys.all_objects AS s1
        CROSS JOIN sys.all_objects AS s2
        ORDER BY s1.object_id
    ) AS x
) AS y;

/* Now create the final ref table for the dates. */
CREATE TABLE refDateDimension
(
      DateKey             int         NOT NULL PRIMARY KEY
    , theDate             date        NOT NULL
    , theDay              tinyint     NOT NULL  
    , WeekOfMonth         tinyint     NOT NULL
    , theMonth            tinyint     NOT NULL
    , theYear             int         NOT NULL
    , mm_dd_yy            char(10)    NOT NULL   /* mm/dd/yyyy */
);

/* Insert data in the dimension table. */
INSERT refDateDimension WITH (TABLOCKX)
SELECT
      DateKey              = CONVERT(int, yyyymmdd)
    , theDate              = theDate
    , theDay               = CONVERT(tinyint, theDay)
    , WeekOfMonth          = CONVERT(tinyint
                              , DENSE_RANK() OVER 
                                (PARTITION BY theYear, theMonth 
                                 ORDER BY theWeek)
                           )
    , theMonth             = CONVERT(tinyint, theMonth)
    , theYear              = theYear
    , mm_dd_yy             = mm_dd_yy
FROM #dim
OPTION (MAXDOP 1);

/* CLEANUP */
DROP TABLE #dim ;

现在我们使用日历表快速查找周数。

SELECT s1.Code
  , s1.Region
  , CEILING(s1.ForecastMinOnHand) AS ForecastMinOnHand /* Round up to even number. */
FROM (
  SELECT MPI.Code, MPI.Region
    --, MPI.UseForecast, MPI.ForecastMultiplier, d.WeekOfMonth
    , CASE 
          WHEN MPI.UseForecast = 0          THEN MPI.MinimumOnHand
          WHEN MPI.ForecastMultiplier = 1    THEN CASE d.WeekOfMonth 
                                                      WHEN 1 THEN FP.Month1 
                                                      WHEN 2 THEN (FP.Month1*.75) + (FP.Month2*.25)
                                                      WHEN 3 THEN (FP.Month1*.5) + (FP.Month2*.5)
                                                      ELSE (FP.Month1*.25) + (FP.Month2*.75)
                                                 END
          WHEN MPI.ForecastMultiplier = 2    THEN CASE d.WeekOfMonth 
                                                      WHEN 1 THEN FP.Month1 + FP.Month2
                                                      WHEN 2 THEN (FP.Month1*.75) + FP.Month2 + (FP.Month3*.25)
                                                      WHEN 3 THEN (FP.Month1*.5) + FP.Month2 + (FP.Month3*.5)
                                                      ELSE (FP.Month1*.25) + FP.Month2 + (FP.Month3*.75)
                                                 END     
          WHEN MPI.ForecastMultiplier = 3    THEN CASE d.WeekOfMonth 
                                                      WHEN 1 THEN FP.Month1 + FP.Month2 + FP.Month3
                                                      WHEN 2 THEN (FP.Month1*.75) + FP.Month2 + FP.Month3 + (FP.Month4*.25)
                                                      WHEN 3 THEN (FP.Month1*.5) + FP.Month2 + FP.Month3 + (FP.Month4*.5)
                                                      ELSE (FP.Month1*.25) + FP.Month2 + FP.Month3 + (FP.Month4*.75)
                                                 END   
          WHEN MPI.ForecastMultiplier = 4.5  THEN CASE d.WeekOfMonth 
                                                      WHEN 1 THEN FP.month1 + fp.month2 + fp.month3 + fp.month4 + (FP.month5*.5)
                                                      WHEN 2 THEN (FP.Month1*.75) + fp.month2 + fp.month3 + fp.month4 + (FP.Month5*.75)
                                                      WHEN 3 THEN (FP.Month1*.5) + fp.month2 + fp.month3 + fp.month4 + FP.Month5
                                                      ELSE (FP.Month1*.25) + fp.month2 + fp.month3 + fp.month4 + FP.Month5 + (FP.Month6*.25)
                                                 END     

     END AS ForecastMinOnHand
     --, MPI.*, FP.*
  FROM MPI 
  INNER JOIN refDateDimension d ON CAST(getDate() AS date) = d.theDate 
  INNER JOIN FP ON MPI.Code = FP.Code
    AND MPI.Region = FP.Region
) s1

<强> Results

| Code | Region | ForecastMinOnHand |
|------|--------|-------------------|
|    1 |     R1 |                50 |
|    1 |     R2 |              1000 |
|    2 |     R1 |               750 |
|    2 |     R3 |              2269 |

我在这里使用日历表的主要目的是它取消了GetWeekNumber()功能并节省了大量处理周期。它还可以让您的SQL优化器更高效地运行。通常,您可以向其添加更多列,以计算您在其他地方需要的时间计算。这是一个实用工具表。 非常方便。

我仍然认为长CASE语句可以稍微提炼一下。如果您能够编辑ForecastPivot视图,那么您应该能够在没有支点的情况下更轻松地获取这些数据。