我尝试根据之前的列(月)创建18(!)个月的动态预测,但我陷入了困境:
我有三列:
我需要实现的目标: 索引,库存-当月,安全库存-当月,生产需要(从Nfp中选择*,日期为= getdate()),库存-本月+1,安全库存-本月+1,生产需要-本月+1。 ..等到18个月
计算: 库存-当月+ 1 =上个月的库存+上个月的SafetyStock-当月的生产需求
是否有可能创建类似的东西?它必须是动态的并获得当前日期和未来18个月的计算结果。所以现在我必须计算从2020-10到2022-04
我尝试过的事情:
我准备了18 cte并加入了所有内容。然后我进行计算-它可以工作,但是速度很慢,我认为它不是专业的。
我尝试做动态sql,在下面您可以看到我的代码,但是当我想做计算列取决于先前的计算列时,我却卡住了:
-------------------代码------------------------- >
if object_id('tempdb..#tmp') is not null
drop table #tmp
if object_id('tempdb..#tmp2') is not null
drop table #tmp2
declare @cols as int
declare @iteration as int
declare @Mth as nvarchar(30)
declare @data as date
declare @sql as nvarchar(max)
declare @sql2 as nvarchar(max)
set @cols = 18
set @iteration = 0
set @Mth = month(getdate())
set @data = cast(getdate() as date)
select
10 as SS,
12 as Stock
into #tmp
WHILE @iteration < @cols
begin
set @iteration = @iteration + 1
set @sql =
'
alter table #tmp
add [StockUwzgledniajacSS - ' + cast(concat(year(DATEADD(Month, @Iteration, @data)),'-', month(DATEADD(Month, @Iteration, @data))) as nvarchar(max)) +'] as (Stock - SS)
'
exec (@sql)
set @Mth= @Mth+ 1
set @sql2 =
'
alter table #tmp
add [StockUwzgledniajacSS - ' + @Mth +'] as ([StockUwzgledniajacSS - ' + @Mth +'])
'
end
select * from #tmp
提前谢谢!
答案 0 :(得分:0)
更新1条注释:在您发布数据之前,我已经写了这篇。我相信这仍然成立,但是,当然,存货水平会有所不同。鉴于您的NFP数据是按天计算的,而您的报告是按月计算的,我建议添加一些东西以将该数据预处理为几个月,例如,按月分组的NPS值之和。
更新2(第二天)注意:从下面的操作注释中,我试图将其与编写的内容集成在一起,并更直接地回答问题,例如,创建报告表#tmp。 鉴于OP还提到了数百万行,因此我想每行代表一个特定的部件/项目-我将其包含在一个称为StockNum的字段中。
我做了一些可能无法正确计算的事情,但是演示了这种方法,应该使您摆脱目前的障碍。的确,如果您以前从未使用过这些代码,那么使用自己的计算方法更新此代码将有助于您了解其工作方式,从而可以对其进行维护。
我假设此处要计算的关键问题是,本月的库存基于上个月的库存,然后是本月的新库存减去旧库存。
可以在18个单独的语句中进行计算(更新表集col2 = col1的某些功能,然后更新表集col3 = col2的某些功能,等等)。但是,多次更新同一张表通常是一种反模式,导致性能下降-尤其是在您需要一次又一次地读取基本数据的情况下。
相反,通常最好使用Recusive CTE(here's an example description)来计算出类似的结果,在此基础上,它会基于先前的结果“构建”一组数据。
此方法的主要区别在于
我广泛使用了临时表/ etc,以帮助演示该过程。
您没有解释什么是安全库存,也没有说明如何测量安全库存,因此在下面的示例中,我假设安全库存是生产的数量,每月5个。然后,我假设NFP是每个月要支出的金额(例如,销售的前瞻性估算)。关键结果将是月底的库存(例如,您可以查看库存过高还是过低)。
由于要将其存储在一个以月为列的表中,因此第一步是使用相关存储桶(月)创建一个列表。这些包括用于以后的计算/等中匹配的字段。注意我包含了一些日期字段(开始日期和结束日期),这些日期字段在您自定义代码时可能很有用。 SQL的这一部分被设计为尽可能简单。
然后,我们创建包含我们库存变动参考数据的暂存表,替换您的SELECT * FROM NFP WHERE date = getdate()
/* SET UP BUCKET LIST TO HELP CALCULATION */
CREATE TABLE #RepBuckets (BucketNum int, BucketName nvarchar(30), BucketStartDate datetime, BucketEndDate datetime)
INSERT INTO #RepBuckets (BucketNum) VALUES
(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),
(11),(12),(13),(14),(15),(16),(17),(18)
DECLARE @CurrentBucketStart date
SET @CurrentBucketStart = DATEFROMPARTS(YEAR(getdate()), MONTH(getdate()), 1)
UPDATE #RepBuckets
SET BucketName = 'StockAtEnd_' + FORMAT(DATEADD(month, BucketNum, @CurrentBucketStart), 'MMM_yy'),
BucketStartDate = DATEADD(month, BucketNum, @CurrentBucketStart),
BucketEndDate = DATEADD(month, BucketNum + 1, @CurrentBucketStart)
/* CREATE BASE DATA */
-- Current stock
CREATE TABLE #Stock (StockNum int, MonthNum int, StockAtStart int, SafetyStock int, NFP int, StockAtEnd int, PRIMARY KEY(StockNum, MonthNum))
INSERT INTO #Stock (StockNum, MonthNum, StockAtStart, SafetyStock, NFP, StockAtEnd) VALUES
(12422, 0, NULL, NULL, NULL, 10)
-- Simulates SELECT * FROM NFP WHERE date = getdate()
CREATE TABLE #NFP_by_month (StockNum int, MonthNum int, StockNFP int, PRIMARY KEY(StockNum, MonthNum))
INSERT INTO #NFP_by_month (StockNum, MonthNum, StockNFP) VALUES
(12422, 1, 4), (12422, 7, 4), (12422, 13, 4),
(12422, 2, 5), (12422, 8, 5), (12422, 14, 5),
(12422, 3, 2), (12422, 9, 2), (12422, 15, 2),
(12422, 4, 7), (12422, 10, 7), (12422, 16, 7),
(12422, 5, 9), (12422, 11, 9), (12422, 17, 9),
(12422, 6, 3), (12422, 12, 3), (12422, 18, 3)
然后,我们使用递归CTE来计算数据。它将这些存储在表#StockProjections中。
这是什么
请注意,在CTE的递归部分中,“ SBM”(StockByMonth)是指上个月的数据)。然后将其与任何外部数据(例如,#NFP)一起使用以计算新数据。
这些计算使用创建一个表
/* CALCULATE PROJECTIONS */
CREATE TABLE #StockProjections (StockNum int, BucketName nvarchar(30), MonthNum int, StockAtStart int, SafetyStock int, NFP int, StockAtEnd int, PRIMARY KEY (StockNum, BucketName))
; WITH StockByMonth AS
(-- Anchor
SELECT TOP 1 StockNum, MonthNum, StockAtStart, SafetyStock, NFP, StockAtEnd
FROM #Stock S
ORDER BY MonthNum DESC
-- Recursion
UNION ALL
SELECT NFP.StockNum,
SBM.MonthNum + 1 AS MonthNum,
SBM.StockAtEnd AS NewStockAtStart,
5 AS Safety_Stock,
NFP.StockNFP,
SBM.StockAtEnd + 5 - NFP.StockNFP AS NewStockAtEnd
FROM StockByMonth SBM
INNER JOIN #NFP_by_month NFP ON NFP.MonthNum = SBM.MonthNum + 1
WHERE NFP.MonthNum <= 18
)
INSERT INTO #StockProjections (StockNum, BucketName, MonthNum, StockAtStart, SafetyStock, NFP, StockAtEnd)
SELECT StockNum, BucketName, MonthNum, StockAtStart, SafetyStock, NFP, StockAtEnd
FROM StockByMonth
INNER JOIN #RepBuckets ON StockByMonth.MonthNum = #RepBuckets.BucketNum
现在我们有了数据,我们建立了一个表用于报告。请注意,此表在列名称中嵌入了月份名称(例如StockAtEnd_Jun_21)。使用通用名称(例如StockAtEnd_Month4)会更容易,但我在这里只是进行了稍微复杂一些的示例演示。
/* SET UP TABLE FOR REPORTING */
DECLARE @cols int = 18
DECLARE @iteration int = 0
DECLARE @colname nvarchar(30)
DECLARE @sql2 as nvarchar(max)
CREATE TABLE #tmp (StockNum int PRIMARY KEY)
WHILE @iteration <= @cols
BEGIN
SET @colname = (SELECT TOP 1 BucketName FROM #RepBuckets WHERE BucketNum = @iteration)
SET @sql2 = 'ALTER TABLE #tmp ADD ' + QUOTENAME(@colname) + ' int'
EXEC (@sql2)
SET @iteration = @iteration + 1
END
最后一步是将数据添加到报表中。我在这里使用过枢轴,但可以随意使用任何您喜欢的东西。
/* POPULATE TABLE */
DECLARE @columnList nvarchar(max) = N'';
SELECT @columnList += QUOTENAME(BucketName) + N' ' FROM #RepBuckets
SET @columnList = REPLACE(RTRIM(@columnList), ' ', ', ')
DECLARE @sql3 nvarchar(max)
SET @sql3 = N'
;WITH StockPivotCTE AS
(SELECT *
FROM (SELECT StockNum, BucketName, StockAtEnd
FROM #StockProjections
) StockSummary
PIVOT
(SUM(StockAtEnd)
FOR [BucketName]
IN (' + @columnList + N')
) AS StockPivot
)
INSERT INTO #tmp (StockNum, ' + @columnList + N')
SELECT StockNum, ' + @columnList + N'
FROM StockPivotCTE'
EXEC (@sql3)
这是DB<>fiddle,其中显示了该子程序的运行以及每个子步骤的结果。