在Sqlserver下巧用行列转换日期的数据统计
? ? ? ? ? ? ? ? ?在Sqlserver下巧用行列轉(zhuǎn)換日期的數(shù)據(jù)統(tǒng)計(jì)
?
前言
????在SQLSERVER?中有很多統(tǒng)計(jì)函數(shù)的基礎(chǔ)語(yǔ)法,有使用Group By?或?partition by?后配合Sum,Count(*)?等用法。常應(yīng)用于統(tǒng)計(jì)網(wǎng)站的PV流量、合同項(xiàng)目中月收入等業(yè)務(wù)場(chǎng)景中。在文中我分享下最近做過(guò)的統(tǒng)計(jì)小案例,和大家互相學(xué)習(xí)下:)?
背景?
?????? 合同中行項(xiàng)目按月收入的統(tǒng)計(jì)
??1.業(yè)務(wù)邏輯及需求?
? 1.1 表業(yè)務(wù)邏輯?
??? 合同是公司間互相簽署的法律契約,一份合同從誕生起,就開(kāi)始流轉(zhuǎn)于公司的各個(gè)部門(mén),最核心的還是盈虧的數(shù)值。盈虧是結(jié)果,數(shù)據(jù)的產(chǎn)生源于每個(gè)自然月或其他時(shí)段的匯總。?往往在實(shí)際業(yè)務(wù)中,例如有些廣告行業(yè),立項(xiàng)是分為固定排期和合同活動(dòng)收入。??
?? 固定排期一般以一個(gè)自然月為周期,例如[201503,201504]間產(chǎn)生的預(yù)收入;活動(dòng)收入表中的活動(dòng)是指收入周期不固定,可能ConfirmDate??發(fā)生在一個(gè)月中的若干天中,也可能在間隔一個(gè)月后發(fā)生。
?? 無(wú)論是固定排期還是活動(dòng)收入都和行項(xiàng)目有關(guān),行項(xiàng)目是一個(gè)編號(hào),一個(gè)行項(xiàng)目可以對(duì)應(yīng)多次排期或活動(dòng)收入的統(tǒng)計(jì),在我給大家介紹的Demo中,將暫時(shí)考慮固定排期的情況。
?1.2 項(xiàng)目的需求
?? 統(tǒng)計(jì)合同中行項(xiàng)目的金額:分為結(jié)轉(zhuǎn)金額數(shù)據(jù)匯總,和按自然月條件下金額的匯總。
?
?? 2.準(zhǔn)備的基礎(chǔ)表
?
? ? 2.1 合同信息表?
CREATE TABLE ContractInfo --基本信息表 ( [ContractCode] [varchar](50) Primary key ,[CustomName] [varchar](100) NULL, )insert into ContractInfo (ContractCode,CustomName) values('30100013000861','弘化四方'),('30100013000862','明心見(jiàn)性'),('30100013000863','心綻蓮花')??? 2.2?合同行項(xiàng)目表
CREATE TABLE ContractLine --合同行項(xiàng)目表 ([LineID] [int] IDENTITY(1,1) Primary Key NOT NULL,[ContractCode] [varchar](50) NOT NULL, )insert into ContractLine (ContractCode) values('30100013000861'),('30100013000862'),('30100013000862') ,('30100013000863'),('30100013000863')?2.3?合同固定排期表
CREATE TABLE ContractSchedule --合同固定排期表([ScheduleID] [int] Primary key NOT NULL,-- 排期ID[LineID] [int] NOT NULL, -- 行項(xiàng)目ID[Period] [int] NOT NULL, --時(shí)間段[Amount] [decimal](18, 2) NOT NULL, --交易金額 )insert into ContractSchedule (ScheduleID,LineID,Period,Amount) values (89106,1,201507,90900.00) ,(89107,1,201508,9453.00) ,(89108,1,201510,13000.00) ,(89109,2,201501,12000.00) ,(89110,2,201503,11000.00) ,(89111,3,201509,9000.00) ,(89112,4,201510,8500.00)?
??? 3.補(bǔ)充其他(待)
?
基礎(chǔ)知識(shí)點(diǎn):
? ?1.FOR XML PATH? //用于統(tǒng)計(jì)時(shí)轉(zhuǎn)換行列的格式,
? ?參考:王波洋老師的 靈活運(yùn)用?FOR XML PATH
? ?2.PIVOT (SUM(Amount)) For Period //用于基礎(chǔ)表基礎(chǔ)上的行列轉(zhuǎn)換,
? ?參考:大志若愚老師的 縱表、橫表互轉(zhuǎn)的SQL
? ?3.Select SUM(Amount)?From ContractSchedule
????group by LineID // 根據(jù)條件匯總數(shù)據(jù)
???
實(shí)現(xiàn)思路
?
?邏輯?
/*計(jì)算時(shí)間的基礎(chǔ)序列*/ ->/*格式化日期序列*/ -> /*關(guān)聯(lián)邏輯表,查詢計(jì)算8月份之前的匯總,8月份之后的按月份統(tǒng)計(jì)*/
?
代碼片段
1 /*---------------計(jì)算時(shí)間的基礎(chǔ)序列------------*/ 2 3 /*獲取日期序列起始值*/DECLARE @sdate CHAR(10);
DECLARE @edate CHAR(10);?? 4 SET @sdate = '2015-08-01'--開(kāi)始日期 5 SET @edate = '2015-12-1' 6 7 /*存入臨時(shí)表*/ 8 SELECT * into #DateArr 9 from ( 10 select 11 CONVERT(varchar(6),DATEADD(MONTH,a.number,@sdate),112) totalDate 12 FROM master..spt_values a --系統(tǒng)表 13 WHERE a.type = 'P' 14 AND number BETWEEN 0 AND (select DATEDIFF(MONTH,@sdate,@edate)) 15 )a 16 17 select * from #DateArr
?
1 /*格式化日期序列,用@Months接收*/ 2 DECLARE @Months VARCHAR(1000); 3 DECLARE @SQL NVARCHAR(MAX); 4 5 SET @SQL = 'SELECT @Months=STUFF((SELECT DISTINCT '',[''+totalDate+'']'' FROM #DateArr s 6 FOR XML PATH('''')),1,1,'''')'; 7 EXECUTE sp_executesql @SQL,N'@Months VARCHAR(1000) OUTPUT',@Months OUTPUT; 8 9 print @Months?
1 /*未關(guān)聯(lián)時(shí)間序列前的基礎(chǔ)數(shù)據(jù)*/ 2 with tab as( 3 select 4 c.ContractCode 5 ,c.CustomName 6 ,cl.LineID 7 ,ISNULL(b.TheEndYearAmount,0) as NearAYearAgo 8 ,cs.Amount 9 ,cs.Period 10 from ContractInfo c 11 left join 12 ContractLine cl 13 on c.ContractCode=cl.ContractCode 14 left join 15 ContractSchedule cs 16 on cs.LineID=cl.LineID 17 --計(jì)算8月份之前的統(tǒng)計(jì) 18 left join 19 ( 20 select LineID,Sum(Amount) as TheEndYearAmount 21 from 22 ContractSchedule 23 where Period between 201508 and 201512 24 group by LineID 25 --select * from ContractSchedule 26 )b on b.LineID=cl.LineID 27 ) select * from tab 1 /*--------添加日期序列后的統(tǒng)計(jì) --------*/ 2 SET @SQL=' 3 with tab as( 4 select c.CustomName 5 ,ISNULL(b.TheEndYearAmount,0) as NearAYearAgo 6 ,c.ContractCode --合同號(hào) 7 ,cl.LineID --合同的行ID 8 ,cs.Amount --待計(jì)算的數(shù)量 9 ,cs.Period --統(tǒng)計(jì)的日期 10 from ContractInfo c 11 left join 12 ContractLine cl 13 on c.ContractCode=cl.ContractCode 14 left join 15 ContractSchedule cs 16 on cs.LineID=cl.LineID 17 --計(jì)算8月份之前的統(tǒng)計(jì) 18 left join 19 ( 20 select LineID,Sum(Amount) as TheEndYearAmount 21 from 22 ContractSchedule 23 where Period between 201412 and 201508 24 group by LineID 25 --select * from ContractSchedule 26 )b on b.LineID=cl.LineID 27 ) select * from tab 28 PIVOT (SUM(Amount) FOR Period 29 IN( 30 '+@Months+' 31 ))b 32 ' 33 EXEC (@SQL)?
查詢后結(jié)果?? 腳本下載
?
?
?
思考
?
留下的思考
1. 對(duì)空值的處理:?select * from tab PIVOT (SUM(Amount)...
??? 這里我嘗試用ISNULL(SUM(Amount),0.00) 去處理,但語(yǔ)法沒(méi)有通過(guò),我將繼續(xù)嘗試..
2. 腳本片段中獲取日期序列,或許在其他統(tǒng)計(jì)腳本中也會(huì)復(fù)用,我準(zhǔn)備寫(xiě)到標(biāo)量函數(shù)或表值函數(shù)中試一下。
3. 常用的業(yè)務(wù)統(tǒng)計(jì)腳本中關(guān)聯(lián)的表比較多,如何能有效避免重復(fù),在最后結(jié)果集中減少使用 distinct ,而使用Group by 去過(guò)濾重復(fù)字段
這一個(gè)知識(shí)點(diǎn)我比較薄弱,不斷總結(jié),在分享經(jīng)驗(yàn)給大家,少走彎路。
?
感謝
????我的好朋友歡,一直致力于SQL方面的統(tǒng)計(jì),他給了我很多建議{
1.理解需求并開(kāi)始寫(xiě)之前,要知道每個(gè)表里會(huì)出現(xiàn)什么數(shù)據(jù)
2.出現(xiàn)問(wèn)題后,先查表與表之間是什么關(guān)聯(lián),關(guān)聯(lián)從少到多,去檢查錯(cuò)誤
3.最核心的想清楚再寫(xiě)sql,如果腦子里不清楚就上手寫(xiě),萬(wàn)一出現(xiàn)一個(gè)錯(cuò)誤的想法,再糾正就麻煩了???}
?? 博學(xué)的龍叔,總是第一時(shí)間幫助大家理清混亂的邏輯。
???永遠(yuǎn)的濤哥,在不斷修改濤哥的統(tǒng)計(jì)腳本中,使自己受益匪淺。
?
posted on 2015-03-31 10:06 NET未來(lái)之路 閱讀(...) 評(píng)論(...) 編輯 收藏轉(zhuǎn)載于:https://www.cnblogs.com/lonelyxmas/p/4380084.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的在Sqlserver下巧用行列转换日期的数据统计的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java执行时的两个常见问题(无法加载主
- 下一篇: python requests