求个关于库龄分析的算法

qingbbbb 2017-12-12 12:59:16
表1,入库表
行 产品 入库时间 入库数量
1 A 12月1日 20
2 A 11月18日 10
3 A 10月1日 15
4 A 9月20日 30
5 A 8月3日 25
表2,库存结存表
产品 结存数
A 50


希望得出的结果
产品 结存 最早入库时间 30天内 30-60天 61到90天 91天以上
A 50 9月20日 30 15 5 0
按照先进先出原则
假设结存50个 是12,月12号,今天的,
减去行1的20个,剩余30个,大于0,继续;
减去行2的10个,剩余20个,大于0,继续;
减去行3的15个,剩余5个,大于0,继续;
减去行4的30个,结果为负数,OK,计算到这里截至。根据这个时间,我们知道,最早的入库时间是9月20日。
然后按照序号1、2、3、4去归类,行1、2归类到30天内,行3归类到1-2个月,行4归类到61-90天
其中表1的行号,实际不存在的,而且数据比较多
可否直接用SQL语句直接查询出来
...全文
474 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
servercg 2018-11-07
  • 打赏
  • 举报
回复
oracle10中,没有OUTER APPLY,请问用什么方法替代合适
RINK_1 2017-12-12
  • 打赏
  • 举报
回复


if not object_id(N'Tempdb..#T_1') is null
    drop table #T_1
Go
Create table #T_1([产品] nvarchar(20),[入库时间] nvarchar(20),[入库数量] int)
Insert #T_1
select N'A',N'12月1日',20 union all
select N'A',N'11月18日',10 union all
select N'A',N'10月18日',15 union ALL    
select N'A',N'9月20日',30 union all
select N'A',N'8月3日',25 union all
select N'B',N'12月1日',20 union all
select N'B',N'11月18日',10 union all   
select N'B',N'10月1日',15 union all       
select N'B',N'9月20日',30 union all
select N'B',N'8月3日',25

if not object_id(N'Tempdb..#T_2') is null
    drop table #T_2
Go

CREATE TABLE #T_2
(产品 nvarchar(20),结存数 int)

INSERT INTO #T_2
SELECT 'A',50

WITH CTE_1
AS
(SELECT *,
 CAST(CAST(YEAR(GETDATE()) AS VARCHAR)+'.'+REPLACE(REPLACE([入库时间],'月','.'),'日','') AS DATE) AS 完整时间 FROM #T_1 A),

CTE_2
AS
(SELECT A.产品,结存数,最早入库时间,
       SUM(CASE WHEN SUB_TOTAL<=结存数 THEN 入库数量 ELSE 结存数-(SUB_TOTAL-入库数量) END) AS 期间库存,
       CASE WHEN DATEDIFF(DD,完整时间,GETDATE())<30 THEN '30天内'
            WHEN DATEDIFF(DD,完整时间,GETDATE())>=30 AND DATEDIFF(DD,完整时间,GETDATE())<=60 THEN '30-60天'
            WHEN DATEDIFF(DD,完整时间,GETDATE())>=61 AND DATEDIFF(DD,完整时间,GETDATE())<90 THEN '61-90天'
            ELSE '91天以上' END AS 期间
FROM
(SELECT A.*,B.SUB_TOTAL,C.结存数,MIN(完整时间) OVER (PARTITION BY A.产品) AS 最早入库时间
FROM CTE_1 A
OUTER APPLY (SELECT SUM([入库数量]) AS SUB_TOTAL FROM CTE_1 WHERE 产品=A.产品 AND 完整时间>=A.完整时间) AS B
JOIN #T_2 C ON A.产品=C.产品 AND B.SUB_TOTAL-A.入库数量<=C.结存数) AS A
GROUP BY A.产品,结存数,最早入库时间,CASE WHEN DATEDIFF(DD,完整时间,GETDATE())<30 THEN '30天内'
                                         WHEN DATEDIFF(DD,完整时间,GETDATE())>=30 AND DATEDIFF(DD,完整时间,GETDATE())<=60 THEN '30-60天'
                                         WHEN DATEDIFF(DD,完整时间,GETDATE())>=61 AND DATEDIFF(DD,完整时间,GETDATE())<90 THEN '61-90天'
                                         ELSE '91天以上' END)
   
SELECT 产品,结存数,最早入库时间,
       ISNULL([30天内],0) AS [30天内],ISNULL([30-60天],0) AS [30-60天],ISNULL([61-90天],0) AS [61-90天],ISNULL([91天以上],0) AS [91天以上]
FROM CTE_2 A
PIVOT (MAX(期间库存) FOR 期间 IN ([30天内],[30-60天],[61-90天],[91天以上])) B

听雨停了 2017-12-12
  • 打赏
  • 举报
回复
引用 9 楼 qingbbbb 的回复:
不好意思,因为为了方便才写了几月几号,其实是日期类型
那你慢慢研究上面的代码,看懂了就明白了逻辑什么样的。都是很简单的逻辑,没啥复杂的地方。楼上美女版主那个应该也是可以得。但是多个产品就会报错。应该还要小改一下。你自己慢慢倒腾吧,语法应该可以看懂,逻辑你就更应该能看懂了,业务数据都在你那还有啥看不懂的。
qingbbbb 2017-12-12
  • 打赏
  • 举报
回复
不好意思,因为为了方便才写了几月几号,其实是日期类型
听雨停了 2017-12-12
  • 打赏
  • 举报
回复

use Tempdb
go
--> --> 听雨停了-->测试数据

if not object_id(N'Tempdb..#tabA') is null
drop table #tabA
Go
Create table #tabA([产品] nvarchar(21),[入库时间] nvarchar(26),[入库数量] int)
Insert #tabA
select N'A',N'12月1日',20 union all
select N'A',N'11月18日',10 union all
select N'A',N'10月18日',15 union ALL --A改成了18号
select N'A',N'9月20日',30 union all
select N'A',N'8月3日',25 union all
select N'B',N'12月1日',20 union all
select N'B',N'11月18日',10 union all
select N'B',N'10月1日',15 union all --B没改(数据上面复制下来的)
select N'B',N'9月20日',30 union all
select N'B',N'8月3日',25

if not object_id(N'Tempdb..#tabB') is null
drop table #tabB
Go
Create table #tabB([产品] nvarchar(21),[结存数] int)
Insert #tabB
select N'A',50 UNION ALL
select N'B',80
--测试数据结束


--先删除#tabC表,因为后面会用到
if not object_id(N'Tempdb..#tabC') is null
drop table #tabC
GO

--说实话你这里面的时间搞成这样也是醉了,好好的日期类型不用搞成几月几日干嘛啊。
;WITH cte AS (
SELECT *,cast(('2017-'+replace(replace(入库时间,'月','-'),'日','')) as date) as dt,ROW_NUMBER() OVER(PARTITION BY 产品 order BY cast(('2017-'+replace(replace(入库时间,'月','-'),'日','')) as date) desc) as rn
FROM #tabA
),
cte2 AS(
SELECT *,
CASE WHEN dt>DATEADD(DAY,-30,GETDATE())
THEN '1'
WHEN dt<DATEADD(DAY,-30,GETDATE()) AND dt>DATEADD(DAY,-60,GETDATE())
THEN '2'
WHEN dt<DATEADD(DAY,-60,GETDATE()) AND dt>DATEADD(DAY,-90,GETDATE())
THEN '3'
WHEN dt<DATEADD(DAY,-90,GETDATE())
THEN '4'
END AS title
FROM cte
),
cte3 AS (
SELECT 产品,title,sum(入库数量) as cnt,row_number() over(PARTITION by 产品 order by title) as rn
FROM cte2 GROUP BY 产品,title
),
cte4 AS (
SELECT a.*, b.结存数-a.cnt as total_cnt FROM cte3 a
INNER JOIN #tabb b ON a.产品=b.产品
WHERE rn=1
UNION ALL
SELECT a.*, b.total_cnt-a.cnt
FROM cte3 a
INNER JOIN cte4 b ON a.rn=b.rn+1 AND a.产品=b.产品
),
cte5 AS (
SELECT *,ROW_NUMBER() OVER(PARTITION BY 产品 order by total_cnt desc) as rn_2 FROM cte4
WHERE total_cnt<=0
),
--统计每个产品在 (30天内 30-60天 61到90天 91天以上) 的真实库存数据
cte6 AS (
SELECT 产品,title,cnt FROM cte4
WHERE total_cnt>0
UNION ALL
SELECT 产品,title,cnt-abs(total_cnt) FROM cte5
WHERE rn_2=1
),
cte7 AS (
SELECT a.*,b.结存数, b.结存数-a.入库数量 as cnt FROM cte a
INNER JOIN #tabb b ON a.产品=b.产品
WHERE rn=1
UNION ALL
SELECT a.*,b.结存数, b.cnt-a.入库数量
FROM cte a
INNER JOIN cte7 b ON a.rn=b.rn+1 AND a.产品=b.产品
),
cte8 AS (
SELECT 产品,入库时间,结存数,row_number() over(PARTITION by 产品 order by dt desc ) as rn
FROM cte7
WHERE cnt<=0
),
--获取每个产品的最早入库时间
cte9 AS (
SELECT 产品,入库时间,结存数 FROM cte8
WHERE rn=1
),
--建立辅助表,用以连接上面的cte6
cte10 AS (
SELECT 1 AS id ,'30天内' as title union all
SELECT 2 AS id ,'31-60天' as title union all
SELECT 3 AS id ,'61到90天' as title union all
SELECT 4 AS id ,'91天以上' as title
)
--最终数据插入#tabC中
SELECT a.产品,a.cnt,b.入库时间,b.结存数,c.title
into #tabC
FROM cte6 a
INNER JOIN cte9 b ON a.产品=b.产品
RIGHT JOIN cte10 c ON a.title=c.id
--查询最终数据
SELECT * FROM #tabC

--#tabC表行转列显示
DECLARE @name VARCHAR(max),@sql VARCHAR(max)
set @name =stuff((SELECT DISTINCT ',['+[title]+']' from #tabC for xml PATH('')),1,1,'')
set @sql ='SELECT * from #tabC pivot(max(cnt)for title in('+@name+'))a where isnull(产品,'''')<>'''''
PRINT @sql
EXEC( @sql)

qingbbbb 2017-12-12
  • 打赏
  • 举报
回复
版主大大好牛逼啊,看着代码好长啊, 有点看不懂了,能否讲下思路
吉普赛的歌 版主 2017-12-12
  • 打赏
  • 举报
回复
SET NOCOUNT ON
DECLARE @t1 TABLE (rid INT,productName NVARCHAR(10),inTime DATETIME,inCnt INT)
INSERT INTO @t1 VALUES (1,'A','2017-12-01',20)
INSERT INTO @t1 VALUES (2,'A','2017-11-18',10)
INSERT INTO @t1 VALUES (3,'A','2017-10-18',15)
INSERT INTO @t1 VALUES (4,'A','2017-09-20',30)
INSERT INTO @t1 VALUES (5,'A','2017-08-03',25)
 
DECLARE @t2 TABLE (productName NVARCHAR(10),jcCnt INT)
INSERT INTO @t2 VALUES ('A',50)
 
;with cte as (
	SELECT t2.productName
	,t2.jcCnt
	,ISNULL((SELECT sum(inCnt) FROM @t1 AS t1 WHERE t1.productName=t2.productName AND DATEDIFF(DAY,t1.inTime,GETDATE())<30),0) AS in30
	,ISNULL((SELECT sum(inCnt) FROM @t1 AS t1 WHERE t1.productName=t2.productName AND DATEDIFF(DAY,t1.inTime,GETDATE()) BETWEEN 30 AND 60 ),0) AS in30and60
	,ISNULL((SELECT sum(inCnt) FROM @t1 AS t1 WHERE t1.productName=t2.productName AND DATEDIFF(DAY,t1.inTime,GETDATE()) BETWEEN 61 AND 90 ),0) AS in61and90
	,ISNULL((SELECT sum(inCnt) FROM @t1 AS t1 WHERE t1.productName=t2.productName AND DATEDIFF(DAY,t1.inTime,GETDATE())>=91),0) AS over91
	FROM @t2 AS t2
),cte2 AS(
SELECT  *
,jcCnt-in30 as sub_in30
,jcCnt-in30-in30and60 as sub_in30and60
,jcCnt-in30-in30and60-in61and90 AS sub_in61and90
,jcCnt-in30-in30and60-in61and90-over91 AS sub_over91
FROM cte
),cte3 AS (
SELECT productName
	,jcCnt
	,CASE WHEN sub_in30>=0 THEN in30 ELSE 0 END AS in30
	,CASE WHEN sub_in30and60>=0 THEN in30and60 ELSE sub_in30 END AS in30and60
	,CASE WHEN sub_in61and90>=0 THEN in61and90 ELSE sub_in30and60 END AS in61and90
	,CASE WHEN sub_over91>=0 THEN over91 ELSE sub_in61and90 END AS over91
FROM cte2
),cte4 AS (
SELECT  productName
	,jcCnt
	,CASE WHEN in30>=0 THEN in30 ELSE 0 END AS in30
	,CASE WHEN in30and60>=0 THEN in30and60 ELSE 0 END AS in30and60
	,CASE WHEN in61and90>=0 THEN in61and90 ELSE 0 END AS in61and90
	,CASE WHEN over91>=0 THEN over91 ELSE 0 END AS over91
FROM cte3
),cte5 AS (
	SELECT 'in30' AS flag,productName,MIN(inTime) AS minInTime FROM @t1 AS t1 WHERE DATEDIFF(DAY,t1.inTime,GETDATE())<30 GROUP BY t1.productName
	UNION ALL
	SELECT 'in30and60' AS flag,productName,MIN(inTime) AS minInTime FROM @t1 AS t1 WHERE DATEDIFF(DAY,t1.inTime,GETDATE()) BETWEEN 30 AND 60 GROUP BY t1.productName
	UNION ALL
	SELECT 'in61and90' AS flag,productName,MIN(inTime) AS minInTime FROM @t1 AS t1 WHERE DATEDIFF(DAY,t1.inTime,GETDATE()) BETWEEN 61 AND 90 GROUP BY t1.productName
	UNION ALL
	SELECT 'over91' AS flag,productName,MIN(inTime) AS minInTime FROM @t1 AS t1 WHERE DATEDIFF(DAY,t1.inTime,GETDATE()) >=91 GROUP BY t1.productName
)
SELECT productName
	,jcCnt
	,(SELECT minInTime 
	  FROM cte5
	  WHERE flag=CASE WHEN A.in30=0 OR A.in30and60=0 THEN 'in30'
					WHEN A.in61and90=0 THEN 'in30and60'
					ELSE 'in61and90' END   
	) AS minInTime
	,in30
	,in30and60
	,in61and90
	,over91
FROM cte4 AS A
/*
productName jcCnt       minInTime               in30        in30and60   in61and90   over91
----------- ----------- ----------------------- ----------- ----------- ----------- -----------
A           50          2017-09-20 00:00:00.000 30          15          5           0
*/
按你意思拼出来了, 有小问题你自己再改改
qingbbbb 2017-12-12
  • 打赏
  • 举报
回复
引用 4 楼 qq_37170555 的回复:
[quote=引用 2 楼 qingbbbb 的回复:] 不对啊,我不是单纯需要入库时间小于系统时间30,的合计数量。需要结存数,按区间分成几段
你这上面不对吧,行3应该归到60天道90天里吧,10月1号离现在已经两个多月了[/quote] 行3的时间我算错了,是60到90的,吧行3的时间改成18号左右
听雨停了 2017-12-12
  • 打赏
  • 举报
回复
引用 2 楼 qingbbbb 的回复:
不对啊,我不是单纯需要入库时间小于系统时间30,的合计数量。需要结存数,按区间分成几段
你这上面不对吧,行3应该归到60天道90天里吧,10月1号离现在已经两个多月了
qingbbbb 2017-12-12
  • 打赏
  • 举报
回复
表1,入库表 行 产品 入库时间 入库数量 1 A 12月1日 20 2 A 11月18日 10 3 A 10月18日 15 时间搞错了 4 A 9月20日 30 5 A 8月3日 25 表2,库存结存表 产品 结存数 A 50 希望得出的结果 产品 结存 最早入库时间 30天内 30-60天 61到90天 91天以上 A 50 9月20日 30 15 5 0 用上面的SQL,最后的0,和5需要怎么得出来,还有,最早入库时间怎么来
qingbbbb 2017-12-12
  • 打赏
  • 举报
回复
不对啊,我不是单纯需要入库时间小于系统时间30,的合计数量。需要结存数,按区间分成几段
吉普赛的歌 版主 2017-12-12
  • 打赏
  • 举报
回复
SET NOCOUNT ON
DECLARE @t1 TABLE (rid INT,productName NVARCHAR(10),inTime DATETIME,inCnt INT)
INSERT INTO @t1 VALUES (1,'A','2017-12-01',20)
INSERT INTO @t1 VALUES (2,'A','2017-11-18',10)
INSERT INTO @t1 VALUES (3,'A','2017-10-01',15)
INSERT INTO @t1 VALUES (4,'A','2017-09-20',30)
INSERT INTO @t1 VALUES (5,'A','2017-08-03',25)

DECLARE @t2 TABLE (productName NVARCHAR(10),jcCnt INT)
INSERT INTO @t2 VALUES ('A',50)

SELECT t2.productName
,t2.jcCnt
,ISNULL((SELECT sum(inCnt) FROM @t1 AS t1 WHERE t1.productName=t2.productName AND DATEDIFF(DAY,t1.inTime,GETDATE())<30),0) AS in30
,ISNULL((SELECT sum(inCnt) FROM @t1 AS t1 WHERE t1.productName=t2.productName AND DATEDIFF(DAY,t1.inTime,GETDATE()) BETWEEN 30 AND 60 ),0) AS in30and60
,ISNULL((SELECT sum(inCnt) FROM @t1 AS t1 WHERE t1.productName=t2.productName AND DATEDIFF(DAY,t1.inTime,GETDATE()) BETWEEN 61 AND 90 ),0) AS in61and90
,ISNULL((SELECT sum(inCnt) FROM @t1 AS t1 WHERE t1.productName=t2.productName AND DATEDIFF(DAY,t1.inTime,GETDATE())>=91),0) AS over91
FROM @t2 AS t2
/*
productName jcCnt       in30        in30and60   in61and90   over91
----------- ----------- ----------- ----------- ----------- -----------
A           50          30          0           45          25
 */
基本就是这样, 结果跟你的有些对不上, 你自己去计算日期吧。
计算机论坛开发毕业设计 目 录 绪论 1 1 计算机论坛的调查及初步分析 2 1.1计算机论坛的开发背景 2 1.1.1 项目背景 2 1.1.2 项目的选题意义 2 1.2 关于开发工具的描述 3 1.3 计算机论坛的需求分析 6 1.3.1 需要解决的问题 6 1.3.2系统目标 6 2 系统分析 7 2.1 系统的功能分析 7 2.2 系统的用况图 7 3 系统设计 8 3.1 功能设计 8 3.1.1 登录注册模块 8 3.1.2用户发帖模块 9 3.1.3用户回帖模块 9 3.1.4浏览帖子模块 9 3.1.5管理员信息模块 9 3.1.6用户搜索模块 9 3.2业务流程图 9 3.3数据库设计 10 3.3.1 数据库概念设计 10 3.3.2 数据库概要设计 10 3.3.3 数据库物理设计 10 3.4 代码设计 12 3.4.1 会员注册 10 3.4.2 会员登入 13 3.4.3 会员发帖 14 3.4.4 会员查找 15 4 系统实现 17 4.1 用户注册登入 17 4.2 注册用户发帖 18 4.3 注册用户浏览帖子 19 4.4 注册用户回帖 20 4.5管理会员信息 21 4.6主界面 21 结论 23 参考文献 24 致谢 25 绪论 论坛系统是互联网上一种人与人之间交互的必备工具。界面主要以文字为主,提供一 个信息交流的空间,它开辟了一块空间供用户读取信息、讨论问题。早期的论坛系统只 提供消息投递和阅读功能,如今的论坛系统允许用户之间分享工具软件、技术文档等资 源。如何设计、建立一个经济、实惠、全面、高效、稳定的论坛系统,是每个开发论坛 系统的程序员必须要考虑的问题,在本系统上,网站浏览者可以畅所欲言,任何问题都 可以在这里交流,它的最大优点就是自由平等、快捷互动。一个成功的论坛系统可大大 提高承载此论坛的网站点击率,使企业的用户加入到产品、服务等内容的讨论上来,起 到收集反馈资料的作用,对企业战略发展有着很好的参考作用。 本文档主要论述了开发一个简单的网上在线论坛系统所用到的关键技术和方法。其中 运用了ASP.NET技术、SQL SERVER数据库、一些数据库控件的运用等等, 对上述技术的实现方法进行了详细的讨论。在WEB 服务器上,利用ASP.NET技术等技术,实现了一个很简单的网上在线论坛系统,并详细讨论 了该系统的结构及其实现过程。具有显示版面、查看版面、查看贴子、查看发表的贴子 、查看跟帖内容、用户注册、用户登入、发表贴子、回复贴子、进入后台等功能,在设 计的过程中力求界面友好、简洁、易于操作。代码部分尽量避免逻辑错误,算法设计简 单合理,尽量使程序具有较好的可读性,有利于其他的设计者对程序的阅读;力求对数 据库的操作安全稳定,尽量避免数据库异常,并要保证查询的快速无误。保证程序的逻 辑结构,编程时多注意使用通用函数。 1 计算机论坛的调查及初步分析 1.1 计算机论坛的开发背景 1.1.1 项目背景 本论坛是在.net framework框架下,采用c#语言开发的一个网站。它提供一块公共电子白板,每个用户都 可以在上面书写,可发布信息或提出看法。电子公告牌按不同的主题、分主题分成很多 个布告栏,布告栏的设立的依据是大多数论坛使用者的要求和喜好,使用者可以阅读他 人关于某个主题的最新看法,也可以将自己的想法毫无保留地贴到公告栏中。在论坛里 ,人们之间的交流打破了时间、空间的限制。在与别人进行交往时,无须考虑自身的年 龄、学历、知识、社会地位、财富、外貌、健康状况,而这些条件往往是人们在其他交 流形式中无可回避的。同样地,也无从知道交谈的对方的真是社会身份。这样,参与论 坛的人可以处于一个平等的位置与其他人进行任何问题的探讨。虽然谈话的双方素不相 识,却可以亲近地交谈。这对于现在的所有其他交流方式来说是不可能的。论坛站往往 是由一些有志于此道的爱好者建立,对所有人都免费开放。而且,由于论坛的参与人众 多,因此各方面的话题都不乏热心者。可以说,在论坛上可以找到任何你感兴趣的话题 。在论坛上可以不考虑空间的差异,可以与任何一个志趣相投的人进行交谈。论坛一个 非常重要的特点就是传输纯文本信息,数据量小,交互的速度也比较快,因此它的更新 速度也非常地快。在其他很多方面,论坛同样也起着非常重要的作用。论坛正以其独特 的魅力,吸引着众多的网络用户。本毕业设计所采用的,称为浏览器——服务器的B/S结构 应用系统,它的特点是在客户端直接采用了功能强大的浏览器软件作为界面,其优点在 于软件开发效率高,客户端不受操作平台的限制、也不受地域的限制,网络传输量少, 既适用于局域网,更适用于Internet,而且投资小、见效快,这是一个基于asp.net的论 坛,基本上涵盖了论坛的一些功能. 1.1.2 项目

34,588

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server相关内容讨论专区
社区管理员
  • 基础类社区
  • 二月十六
  • 卖水果的net
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧