你真的會玩SQL嗎?系列目錄
上一篇 你真的會玩SQL嗎?玩爆你的數據報表之存儲過程編寫(上) 已經講到了列轉行的數據。
對於留下的作業不知道有沒有思路?
這里接下來講怎么做:
從表#tempSaleDtl2行轉列,按類型聚合 求出每個產品每個類型(面積、金額……)的合計 放入表#tempSaleDtl3
SELECT ProductGUID,type,typecode, MAX(CASE YearMonth WHEN '9999-13' THEN val ELSE 0 END) AS '項目合計', MAX(CASE YearMonth WHEN @Year+'-00' THEN val ELSE 0 END) AS '以前年度合計', MAX(CASE YearMonth WHEN @Year+'-13' THEN val ELSE 0 END) AS '2011年合計', MAX(CASE YearMonth WHEN @Year+'-01' THEN val ELSE 0 END) AS '2011-01', MAX(CASE YearMonth WHEN @Year+'-02' THEN val ELSE 0 END) AS '2011-02', MAX(CASE YearMonth WHEN @Year+'-03' THEN val ELSE 0 END) AS '2011-03', MAX(CASE YearMonth WHEN @Year+'-04' THEN val ELSE 0 END) AS '2011-04', MAX(CASE YearMonth WHEN @Year+'-05' THEN val ELSE 0 END) AS '2011-05', MAX(CASE YearMonth WHEN @Year+'-06' THEN val ELSE 0 END) AS '2011-06', MAX(CASE YearMonth WHEN @Year+'-07' THEN val ELSE 0 END) AS '2011-07', MAX(CASE YearMonth WHEN @Year+'-08' THEN val ELSE 0 END) AS '2011-08', MAX(CASE YearMonth WHEN @Year+'-09' THEN val ELSE 0 END) AS '2011-09', MAX(CASE YearMonth WHEN @Year+'-10' THEN val ELSE 0 END) AS '2011-10', MAX(CASE YearMonth WHEN @Year+'-11' THEN val ELSE 0 END) AS '2011-11', MAX(CASE YearMonth WHEN @Year+'-12' THEN val ELSE 0 END) AS '2011-12', MAX(CASE YearMonth WHEN '9999-12' THEN val ELSE 0 END) AS '以后年度合計' into #tempSaleDtl3 FROM #tempSaleDtl2 GROUP BY ProductGUID,type,typecode ORDER BY ProductGUID,typecode
來看看這里的數據與前一步的對應關系:
結果部分數據如圖:
其中列名為了顯示方便,這里用了2011,可以將列名變了year-01……。
關於行轉列的知識前系列也提過,不理解的請自覺前去復習。
小技巧是用到了MAX聚合,關於這點前面聚合的文章中有提到。整個的數據結構現在越來越趨近於最后的結果了,year~繼續。
來看看結果中還差什么?項目!
這里用 “--”來代替數據:
--從Project表中加入項目數據select ProjectName as orderCode,ProjectGUID,ProjectName, '--' AS '項目合計', '--' AS '以前年度合計', '--' AS '2011年合計', '--' AS '2011-01', '--' AS '2011-02', '--' AS '2011-03', '--' AS '2011-04', '--' AS '2011-05', '--' AS '2011-06', '--' AS '2011-07', '--' AS '2011-08', '--' AS '2011-09', '--' AS '2011-10', '--' AS '2011-11', '--' AS '2011-12', '--' AS '以后年度合計' from Project where ProjectGUID=@ProjectGUID
還有產品:
select Project.ProjectName+'.'+a.ProductCode as orderCode,a.ProductGUID,a.ProductName, '--' AS '項目合計', '--' AS '以前年度合計', '--' AS '2011年合計', '--' AS '2011-01', '--' AS '2011-02', '--' AS '2011-03', '--' AS '2011-04', '--' AS '2011-05', '--' AS '2011-06', '--' AS '2011-07', '--' AS '2011-08', '--' AS '2011-09', '--' AS '2011-10', '--' AS '2011-11', '--' AS '2011-12', '--' AS '以后年度合計' from #product a left join Project on a.ProjectGUID=Project.ProjectGUID
部分數據如圖:
最后從表#tempSaleDtl3 聯接產品表#product 與項目表查詢出最后的顯示:
--從產品表和Project表、#tempSaleDtl3中加入類型行數據 select c.ProjectName+'.'+b.ProductCode+'.'+a.typecode as orderCode,a.ProductGUID, a.[type], cast(a.[項目合計] as varchar(20)), cast(a.[以前年度合計] as varchar(20)), cast(a.[2011年合計] as varchar(20)), cast(a.[2011-01] as varchar(20)), cast(a.[2011-02] as varchar(20)), cast(a.[2011-03] as varchar(20)), cast(a.[2011-04] as varchar(20)), cast(a.[2011-05] as varchar(20)), cast(a.[2011-06] as varchar(20)), cast(a.[2011-07] as varchar(20)), cast(a.[2011-08] as varchar(20)), cast(a.[2011-09] as varchar(20)), cast(a.[2011-10] as varchar(20)), cast(a.[2011-11] as varchar(20)), cast(a.[2011-12] as varchar(20)), cast(a.[以后年度合計] as varchar(20)) from #tempSaleDtl3 a left join #product b on a.ProductGUID=b.ProductGUID left join Project c on b.ProjectGUID=c.ProjectGUID
以上三塊數據加在一起,用union ALL,然后排個序就得到最開始效果圖中的結果,再次來看看:
其中注意orderCode,這里用到層級,前台顯示時可以更方便顯示層級關系。
最后完整的code:
--從Project表中加入項目數據 select * from ( select ProjectName as orderCode,ProjectGUID,ProjectName, '--' AS '項目合計', '--' AS '以前年度合計', '--' AS '2011年合計', '--' AS '2011-01', '--' AS '2011-02', '--' AS '2011-03', '--' AS '2011-04', '--' AS '2011-05', '--' AS '2011-06', '--' AS '2011-07', '--' AS '2011-08', '--' AS '2011-09', '--' AS '2011-10', '--' AS '2011-11', '--' AS '2011-12', '--' AS '以后年度合計' from Project where ProjectGUID=@ProjectGUID ----項目1 8FA659C8-3DA9-4330-B277-9B517E67606D 項目1 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- union all --從產品表和Project表中加入合計行數據 select Project.ProjectName+'.'+a.ProductCode as orderCode,a.ProductGUID,a.ProductName, '--' AS '項目合計', '--' AS '以前年度合計', '--' AS '2011年合計', '--' AS '2011-01', '--' AS '2011-02', '--' AS '2011-03', '--' AS '2011-04', '--' AS '2011-05', '--' AS '2011-06', '--' AS '2011-07', '--' AS '2011-08', '--' AS '2011-09', '--' AS '2011-10', '--' AS '2011-11', '--' AS '2011-12', '--' AS '以后年度合計' from #product a left join Project on a.ProjectGUID=Project.ProjectGUID ----項目1 8FA659C8-3DA9-4330-B277-9B517E67606D 項目1 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ----項目1.00 00000000-0000-0000-0000-000000000000 合計 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ----項目1.產品1 18908255-DB67-4EA3-A231-8BB39D5B748B 產品1 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ----項目1.產品2 EDB216A9-EBB8-4F2C-AE4E-0A989EC7A993 產品2 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ----項目1.產品3 7040241F-5A66-4F17-AACA-7CBE2FB3BCB9 產品3 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ----項目1.產品4 108778CD-47C0-4258-9CB6-1FBE90CDEBDA 產品4 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ----項目1.產品5 43F7B9BA-EF91-4A38-A048-090179F33C9B 產品5 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- union ALL --從產品表和Project表、#tempSaleDtl3中加入類型行數據 select c.ProjectName+'.'+b.ProductCode+'.'+a.typecode as orderCode,a.ProductGUID, a.[type], cast(a.[項目合計] as varchar(20)), cast(a.[以前年度合計] as varchar(20)), cast(a.[2011年合計] as varchar(20)), cast(a.[2011-01] as varchar(20)), cast(a.[2011-02] as varchar(20)), cast(a.[2011-03] as varchar(20)), cast(a.[2011-04] as varchar(20)), cast(a.[2011-05] as varchar(20)), cast(a.[2011-06] as varchar(20)), cast(a.[2011-07] as varchar(20)), cast(a.[2011-08] as varchar(20)), cast(a.[2011-09] as varchar(20)), cast(a.[2011-10] as varchar(20)), cast(a.[2011-11] as varchar(20)), cast(a.[2011-12] as varchar(20)), cast(a.[以后年度合計] as varchar(20)) from #tempSaleDtl3 a left join #product b on a.ProductGUID=b.ProductGUID left join Project c on b.ProjectGUID=c.ProjectGUID ) t1 order by orderCode
最重要的重要的!!最后不要忘了刪除臨時表:
drop table #product drop table #TempAllSaleDtl DROP TABLE #TempSaleDtl DROP TABLE #tempSaleDtl2 DROP TABLE #tempSaleDtl3 DROP TABLE #ProductSaleArea
至此 數據報表系列已結束,以上也只是提供一下思路,再次提醒一下各位在作以上每一步數據處理時請用數據集合的思維來思考,可能對於一般人來說長篇幅的存儲過程編寫沒有寫過,文中涉及到的技巧和思路是值得借鑒的,其中不理解的可以回過頭來將之前系列的各部分基礎知識點復習一下,然后一步步將結果打印出來測試,在腦袋里留下個前一步的數據,然后再思考下一步的數據怎么處理。如果你能毫無鴨梨的完全寫出來,那恭喜你,你已經進階為小大師,將會迎娶白富美,走上人生巔峰……想想是不是有點小激動……
SQL類下載資源已放入公眾號【一個碼農的日常】 ,回復:數據庫 即可,今后會不定期更新
最終的SQL,其中兩個參數 需要查詢的項目ID @ProjectGUID和需要查詢的年份 @Year 可以由外部傳入,可以自己寫個存儲過程,在這里就不演示了:
DBCC DROPCLEANBUFFERS DBCC FREEPROCCACHE --SET STATISTICS IO ON --SET STATISTICS TIME ON DECLARE @ProjectGUID UNIQUEIDENTIFIER SET @ProjectGUID='8FA659C8-3DA9-4330-B277-9B517E67606D' DECLARE @Year CHAR(4) SET @Year='2011' /*臨時表說明 #product:用項目過濾后,將“合計”作為一個產品的集合 #TempAllSaleDtl:通過項目過濾后的銷售明細,所有月的 #ProductSaleArea:各個產品的總面積,用於計算比例 #TempSaleDtl:通過日期過濾,且加工過后的銷售明細,包括增加累積列,以前年度、以后年度、項目合計的記錄 #tempSaleDtl2:列轉行后的數據集 #tempSaleDtl3:行轉列后的數據集 */ select ProductGUID,ProductName,ProjectGUID,ProductCode into #product from( select ProductGUID,ProductName,ProjectGUID,ProductName as ProductCode from Product where ProjectGUID=@ProjectGUID union all select '00000000-0000-0000-0000-000000000000','合計',@ProjectGUID,'00' as ProductCode ) a --SELECT * FROM #product ----查找項目所有產品的銷售明細:#TempAllSaleDtl SELECT ProductGUID,YearMonth,SaleAmount,SaleArea,SaleNum,SalePrice INTO #TempAllSaleDtl FROM dbo.SaleDtl WHERE ProductGUID IN ( SELECT ProductGUID FROM dbo.Product WHERE ProjectGUID=@ProjectGUID ) --SELECT * FROM #TempAllSaleDtl --ORDER BY ProductGUID,YearMonth --根據現有數據統計,向#TempAllSaleDtl添加總合計記錄 insert into #TempAllSaleDtl(ProductGUID,YearMonth,SaleAmount,SaleArea,SaleNum,SalePrice) select '00000000-0000-0000-0000-000000000000',YearMonth,SUM(SaleAmount),SUM(SaleArea),SUM(SaleNum),SUM(SaleAmount)/SUM(SaleArea) from #TempAllSaleDtl group by YearMonth --SELECT * FROM #TempAllSaleDtl --ORDER BY ProductGUID,YearMonth --查找某年的銷售明細:#TempSaleDtl SELECT ProductGUID,YearMonth,SaleAmount,SaleArea,SaleNum,SalePrice, SalePrice AS ljSaleArea, SalePrice AS blSaleArea, SalePrice AS ljSaleAmount INTO #TempSaleDtl FROM #TempAllSaleDtl WHERE LEFT([YearMonth],4)=@Year --SELECT * FROM #TempSaleDtl --ORDER BY ProductGUID,YearMonth --獲取項目各個產品的總銷售面積:#ProductSaleArea SELECT ProductGUID,SUM(SaleArea) AS all_SaleArea INTO #ProductSaleArea FROM #TempAllSaleDtl GROUP BY ProductGUID --SELECT * FROM #ProductSaleArea --ORDER BY ProductGUID --添加2011合計列的記錄(本年度的各產品的所有面積、金額、均價總合計) insert into #TempSaleDtl( ProductGUID,YearMonth,SaleAmount,SaleArea,SaleNum,SalePrice,ljSaleArea, blSaleArea,ljSaleAmount) select ProductGUID,@Year+'-13',SUM(SaleAmount),SUM(SaleArea),SUM(SaleNum),SUM(SaleAmount)/SUM(SaleArea),0,0,0 from #TempSaleDtl group by ProductGUID --SELECT * FROM #TempSaleDtl --ORDER BY ProductGUID,YearMonth --以前年度列記錄(本年度以前的各產品的所有面積、金額、均價總合計 操作與上一步類似) insert into #TempSaleDtl( ProductGUID,YearMonth,SaleAmount,SaleArea,SaleNum,SalePrice,ljSaleArea, blSaleArea,ljSaleAmount) select ProductGUID,@Year+'-00',SUM(SaleAmount),SUM(SaleArea),SUM(SaleNum),SUM(SaleAmount)/SUM(SaleArea),0,0,0 from #TempAllSaleDtl where YearMonth <@Year+'-00' group by ProductGUID --SELECT * FROM #TempSaleDtl --ORDER BY ProductGUID,YearMonth --以后年度列記錄(本年度以后的各產品的所有面積、金額、均價總合計 操作與上一步類似) insert into #TempSaleDtl( ProductGUID,YearMonth,SaleAmount,SaleArea,SaleNum,SalePrice,ljSaleArea, blSaleArea,ljSaleAmount) select ProductGUID,'9999-12',SUM(SaleAmount),SUM(SaleArea),SUM(SaleNum),SUM(SaleAmount)/SUM(SaleArea),0,0,0 from #TempAllSaleDtl where YearMonth >cast((cast(@Year as int) +1) as CHAR(4))+'-00' group by ProductGUID --SELECT * FROM #TempSaleDtl --ORDER BY ProductGUID,YearMonth --項目合計列記錄(各產品取所有的合計。與上面的區別在於沒有添加 here YearMonth >cast((cast(@Year as int) +1) as CHAR(4))+'-00') insert into #TempSaleDtl( ProductGUID,YearMonth,SaleAmount,SaleArea,SaleNum,SalePrice,ljSaleArea, blSaleArea,ljSaleAmount) select ProductGUID,'9999-13',SUM(SaleAmount),SUM(SaleArea),SUM(SaleNum),SUM(SaleAmount)/SUM(SaleArea),0,0,0 from #TempAllSaleDtl group by ProductGUID --SELECT * FROM #TempSaleDtl --ORDER BY ProductGUID,YearMonth --更新銷售明細TempSaleDtl的累積銷售面積、累積銷售面積比例,累積銷售金額 UPDATE #TempSaleDtl SET ljSaleArea=b.sum_SaleArea, ljSaleAmount=b.sum_SaleAmount, blSaleArea=b.sum_SaleArea/c.all_SaleArea FROM #TempSaleDtl left JOIN ( SELECT n.ProductGUID,n.YearMonth,SUM(m.SaleArea) AS sum_SaleArea,SUM(m.SaleAmount) AS sum_SaleAmount FROM #TempAllSaleDtl m INNER JOIN #TempSaleDtl n ON m.YearMonth<=n.YearMonth AND m.ProductGUID=n.ProductGUID GROUP BY n.ProductGUID,n.YearMonth ) b ON #TempSaleDtl.ProductGUID=b.ProductGUID AND #TempSaleDtl.YearMonth=b.YearMonth LEFT JOIN #ProductSaleArea c ON c.ProductGUID=#TempSaleDtl.ProductGUID --SELECT * FROM #TempSaleDtl --ORDER BY ProductGUID,YearMonth --列轉行,轉換后的表只有 產品、統計類型、日期,值4列;(每個產品對應的0-12、13 月對應的值) SELECT * INTO #tempSaleDtl2 FROM ( SELECT ProductGUID,'銷售套數' AS type,'01' AS typecode,YearMonth,MAX(SaleNum) AS val FROM #TempSaleDtl GROUP BY ProductGUID,YearMonth UNION ALL SELECT ProductGUID,'銷售面積' AS type,'02' AS typecode,YearMonth,MAX(SaleArea) AS val FROM #TempSaleDtl GROUP BY ProductGUID,YearMonth UNION ALL SELECT ProductGUID,'銷售均價' AS type,'03' AS typecode,YearMonth,MAX(SalePrice) AS val FROM #TempSaleDtl GROUP BY ProductGUID,YearMonth UNION ALL SELECT ProductGUID,'銷售金額' AS type,'04' AS typecode,YearMonth,MAX(SaleAmount) AS val FROM #TempSaleDtl GROUP BY ProductGUID,YearMonth UNION ALL SELECT ProductGUID,'累計銷售面積' AS type, '05' AS typecode,YearMonth,SUM(ljSaleArea) FROM #TempSaleDtl GROUP BY ProductGUID,YearMonth UNION ALL SELECT ProductGUID,'累計銷售面積比例' AS type, '06' AS typecode,YearMonth,SUM(blSaleArea) FROM #TempSaleDtl GROUP BY ProductGUID,YearMonth UNION ALL SELECT ProductGUID,'累計銷售金額' AS type, '07' AS typecode,YearMonth,SUM(ljSaleAmount) FROM #TempSaleDtl GROUP BY ProductGUID,YearMonth ) t --SELECT * FROM #tempSaleDtl2 --ORDER BY ProductGUID,yearmonth --行轉列,按日期聚合 SELECT ProductGUID,type,typecode, MAX(CASE YearMonth WHEN '9999-13' THEN val ELSE 0 END) AS '項目合計', MAX(CASE YearMonth WHEN @Year+'-00' THEN val ELSE 0 END) AS '以前年度合計', MAX(CASE YearMonth WHEN @Year+'-13' THEN val ELSE 0 END) AS '2011年合計', MAX(CASE YearMonth WHEN @Year+'-01' THEN val ELSE 0 END) AS '2011-01', MAX(CASE YearMonth WHEN @Year+'-02' THEN val ELSE 0 END) AS '2011-02', MAX(CASE YearMonth WHEN @Year+'-03' THEN val ELSE 0 END) AS '2011-03', MAX(CASE YearMonth WHEN @Year+'-04' THEN val ELSE 0 END) AS '2011-04', MAX(CASE YearMonth WHEN @Year+'-05' THEN val ELSE 0 END) AS '2011-05', MAX(CASE YearMonth WHEN @Year+'-06' THEN val ELSE 0 END) AS '2011-06', MAX(CASE YearMonth WHEN @Year+'-07' THEN val ELSE 0 END) AS '2011-07', MAX(CASE YearMonth WHEN @Year+'-08' THEN val ELSE 0 END) AS '2011-08', MAX(CASE YearMonth WHEN @Year+'-09' THEN val ELSE 0 END) AS '2011-09', MAX(CASE YearMonth WHEN @Year+'-10' THEN val ELSE 0 END) AS '2011-10', MAX(CASE YearMonth WHEN @Year+'-11' THEN val ELSE 0 END) AS '2011-11', MAX(CASE YearMonth WHEN @Year+'-12' THEN val ELSE 0 END) AS '2011-12', MAX(CASE YearMonth WHEN '9999-12' THEN val ELSE 0 END) AS '以后年度合計' into #tempSaleDtl3 FROM #tempSaleDtl2 GROUP BY ProductGUID,type,typecode ORDER BY ProductGUID,typecode --SELECT * FROM #tempSaleDtl3 --ORDER BY ProductGUID,typecode --從Project表中加入項目數據 select * from ( select ProjectName as orderCode,ProjectGUID,ProjectName, '--' AS '項目合計', '--' AS '以前年度合計', '--' AS '2011年合計', '--' AS '2011-01', '--' AS '2011-02', '--' AS '2011-03', '--' AS '2011-04', '--' AS '2011-05', '--' AS '2011-06', '--' AS '2011-07', '--' AS '2011-08', '--' AS '2011-09', '--' AS '2011-10', '--' AS '2011-11', '--' AS '2011-12', '--' AS '以后年度合計' from Project where ProjectGUID=@ProjectGUID ----項目1 8FA659C8-3DA9-4330-B277-9B517E67606D 項目1 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- union all --從產品表和Project表中加入合計行數據 select Project.ProjectName+'.'+a.ProductCode as orderCode,a.ProductGUID,a.ProductName, '--' AS '項目合計', '--' AS '以前年度合計', '--' AS '2011年合計', '--' AS '2011-01', '--' AS '2011-02', '--' AS '2011-03', '--' AS '2011-04', '--' AS '2011-05', '--' AS '2011-06', '--' AS '2011-07', '--' AS '2011-08', '--' AS '2011-09', '--' AS '2011-10', '--' AS '2011-11', '--' AS '2011-12', '--' AS '以后年度合計' from #product a left join Project on a.ProjectGUID=Project.ProjectGUID union ALL --從產品表和Project表、#tempSaleDtl3中加入類型行數據 select c.ProjectName+'.'+b.ProductCode+'.'+a.typecode as orderCode,a.ProductGUID, a.[type], cast(a.[項目合計] as varchar(20)), cast(a.[以前年度合計] as varchar(20)), cast(a.[2011年合計] as varchar(20)), cast(a.[2011-01] as varchar(20)), cast(a.[2011-02] as varchar(20)), cast(a.[2011-03] as varchar(20)), cast(a.[2011-04] as varchar(20)), cast(a.[2011-05] as varchar(20)), cast(a.[2011-06] as varchar(20)), cast(a.[2011-07] as varchar(20)), cast(a.[2011-08] as varchar(20)), cast(a.[2011-09] as varchar(20)), cast(a.[2011-10] as varchar(20)), cast(a.[2011-11] as varchar(20)), cast(a.[2011-12] as varchar(20)), cast(a.[以后年度合計] as varchar(20)) from #tempSaleDtl3 a left join #product b on a.ProductGUID=b.ProductGUID left join Project c on b.ProjectGUID=c.ProjectGUID ) t1 order by orderCode drop table #product drop table #TempAllSaleDtl DROP TABLE #TempSaleDtl DROP TABLE #tempSaleDtl2 DROP TABLE #tempSaleDtl3 DROP TABLE #ProductSaleArea