我在SQL Server中有一个存储过程,该存储过程生成许多报告,所有报告都有“合计”列和“百分比计算”列。因此,临时表的最后一行基本上是合计列。以下是我的代码。
declare @Yr1Tot int, @Yr2Tot int
create table #SrcCnt(id int identity(1,1), Src varchar(100), Yr1Cnt money, Yr1Per varchar(20), Yr2Cnt money, Yr2Per varchar(20))
-- Report 1 generation
insert into #SrcCnt(Src, Yr1Cnt,Yr2Cnt)
select 'Rep1-1', 10 , 20 union
select 'Rep1-2', 1 , 2 union
select 'Rep1-3', 15 , 25 union
select 'Rep1-4', 8 , 87 union
select 'Rep1-5', 4 , 5 union
select 'Rep1-6', 7 , 9 union
select 'Rep1-7', 11 , 30
-- Following block is repeated for all reports
insert into #SrcCnt(Src, Yr1Cnt,Yr2Cnt)
select 'Total', sum(Yr1Cnt), sum(Yr2Cnt) from #SrcCnt
select @Yr1Tot = Yr1Cnt, @Yr2Tot = Yr2Cnt from #SrcCnt where Src = 'Total'
update #SrcCnt set Yr1Per = (Yr1Cnt * 100)/@Yr1Tot
update #SrcCnt set Yr2Per = (Yr2Cnt * 100)/@Yr2Tot
update #SrcCnt set Yr1Per = case when Yr1Per = '0' then null else Yr1Per + '%' end
update #SrcCnt set Yr2Per = case when Yr2Per = '0' then null else Yr2Per + '%' end
select * from #SrcCnt order by id
-- End of repetative code
-- Report 2 generation
delete from #SrcCnt
insert into #SrcCnt(Src, Yr1Cnt,Yr2Cnt)
select 'Rep2-1', 10 , 20 union
select 'Rep2-2', 1 , 2 union
select 'Rep2-3', 15 , 25 union
select 'Rep2-4', 8 , 87 union
select 'Rep2-5', 4 , 5 union
select 'Rep2-6', 7 , 9 union
select 'Rep2-7', 11 , 30
insert into #SrcCnt(Src, Yr1Cnt,Yr2Cnt)
select 'Total', sum(Yr1Cnt), sum(Yr2Cnt) from #SrcCnt
select @Yr1Tot = Yr1Cnt, @Yr2Tot = Yr2Cnt from #SrcCnt where Src = 'Total'
update #SrcCnt set Yr1Per = (Yr1Cnt * 100)/@Yr1Tot
update #SrcCnt set Yr2Per = (Yr2Cnt * 100)/@Yr2Tot
update #SrcCnt set Yr1Per = case when Yr1Per = '0' then null else Yr1Per + '%' end
update #SrcCnt set Yr2Per = case when Yr2Per = '0' then null else Yr2Per + '%' end
select * from #SrcCnt order by id
-- Report 3 generation
delete from #SrcCnt
insert into #SrcCnt(Src, Yr1Cnt,Yr2Cnt)
select 'Rep3-1', 10 , 20 union
select 'Rep3-2', 1 , 2 union
select 'Rep3-3', 15 , 25 union
select 'Rep3-4', 8 , 87 union
select 'Rep3-5', 4 , 5 union
select 'Rep3-6', 7 , 9 union
select 'Rep3-7', 11 , 30
insert into #SrcCnt(Src, Yr1Cnt,Yr2Cnt)
select 'Total', sum(Yr1Cnt), sum(Yr2Cnt) from #SrcCnt
select @Yr1Tot = Yr1Cnt, @Yr2Tot = Yr2Cnt from #SrcCnt where Src = 'Total'
update #SrcCnt set Yr1Per = (Yr1Cnt * 100)/@Yr1Tot
update #SrcCnt set Yr2Per = (Yr2Cnt * 100)/@Yr2Tot
update #SrcCnt set Yr1Per = case when Yr1Per = '0' then null else Yr1Per + '%' end
update #SrcCnt set Yr2Per = case when Yr2Per = '0' then null else Yr2Per + '%' end
select * from #SrcCnt order by id
因此,现在我的存储过程中至少有30个报告,所有这些报告都应在调用此SP时生成,这意味着我需要多次重复总计生成和百分比生成代码,如上面的代码所示。有没有比创建UserDefined DataTable /复制并粘贴几次更好的方法。
答案 0 :(得分:1)
I think the best way to do is to wrap your calculation of percentage code in a User Defined Function (udf) that returns a Table. Pass a table type variable to the udf , do the calculation in the udf and call the udf to get a result set as table data rows. However to pass a table to udf you need to create a User Define Table Type.
Here is the example codes :
/ * Create User Defined Table Type */
CREATE TYPE x2_SrcCntTableType AS TABLE (
id int identity(1,1)
,Src varchar(100)
, Yr1Cnt money
, Yr1Per varchar(20)
, Yr2Cnt money
, Yr2Per varchar(20))
GO
/* Create User Defined Table-Valued Function */
CREATE FUNCTION x2_getSrcCntPercentage
(
@srcCntOrig x2_SrcCntTableType READONLY -- this table should always readonly
)
RETURNS
@SrcCnt TABLE
(
-- Add the column definitions for the TABLE variable here
id int identity(1,1)
, Src varchar(100)
, Yr1Cnt money
, Yr1Per varchar(20)
, Yr2Cnt money
, Yr2Per varchar(20)
)
AS
BEGIN
-- Fill the table variable with the rows for your result set
-- copy data from @srcCntOrig to @SrcCnt table
-- because srcCntOrig is readonly, so do the udates on table SrcCnt
insert into @SrcCnt(Src, Yr1Cnt,Yr2Cnt)
SELECT Src, Yr1Cnt,Yr2Cnt FROM @srcCntOrig
insert into @SrcCnt(Src, Yr1Cnt,Yr2Cnt)
select 'Total', sum(Yr1Cnt), sum(Yr2Cnt) from @SrcCnt
declare @Yr1Tot int, @Yr2Tot int
select @Yr1Tot = Yr1Cnt, @Yr2Tot = Yr2Cnt from @SrcCnt where Src = 'Total'
update @SrcCnt set Yr1Per = (Yr1Cnt * 100)/@Yr1Tot
update @SrcCnt set Yr2Per = (Yr2Cnt * 100)/@Yr2Tot
update @SrcCnt set Yr1Per = case when Yr1Per = '0' then null else Yr1Per + '%' end
update @SrcCnt set Yr2Per = case when Yr2Per = '0' then null else Yr2Per + '%' end
-- select * from #SrcCnt order by id
RETURN
END
GO
And then you can call the udf from a query/stored procedure :
declare @SrcCnt x2_SrcCntTableType
INSERT INTO @SrcCnt(Src,Yr1Cnt,Yr2Cnt) VALUES ('0 - 5 Years',143,43)
INSERT INTO @SrcCnt(Src,Yr1Cnt,Yr2Cnt) VALUES ('6 - 10 Years',28,17)
INSERT INTO @SrcCnt(Src,Yr1Cnt,Yr2Cnt) VALUES ('11 - 15 Years',9,5)
INSERT INTO @SrcCnt(Src,Yr1Cnt,Yr2Cnt) VALUES ('16 - 20 Years',7,2)
INSERT INTO @SrcCnt(Src,Yr1Cnt,Yr2Cnt) VALUES ('21 - 30 Years',11,3)
INSERT INTO @SrcCnt(Src,Yr1Cnt,Yr2Cnt) VALUES ('> 30 Years',91,55)
SELECT * from dbo.x2_getSrcCntPercentage(@SrcCnt)
Result :
id Src Yr1Cnt Yr1Per Yr2Cnt Yr2Per
---------------------------------------------------
1 0 - 5 Years 143,00 49.48% 43,00 34.40%
2 6 - 10 Years 28,00 9.69% 17,00 13.60%
3 11 - 15 Years 9,00 3.11% 5,00 4.00%
4 16 - 20 Years 7,00 2.42% 2,00 1.60%
5 21 - 30 Years 11,00 3.81% 3,00 2.40%
6 > 30 Years 91,00 31.49% 55,00 44.00%
7 Total 289,00 100.00% 125,00 100.00%
(Updated) Ok. There is another option without using UDF. However you need to have the Total of Yr1Cnt and Yr2Cnt available before you make insertions to @SrcCnt table. The trick is to use COMPUTED COLUMN.
Here is the partial code for your stored procedure :
declare @SrcCnt TABLE
(
-- Add the column definitions for the TABLE variable here
id int identity(1,1)
, Src varchar(100)
, Yr1Cnt money
, Yr1Per AS Cast(Cast((Yr1Cnt/TotYr1Cnt)*100 as decimal(18,2)) as varchar(10)) + ' %' -- COMPUTED COLUMN
, Yr2Cnt money
, Yr2Per AS Cast(Cast((Yr2Cnt/TotYr2Cnt)*100 as decimal(18,2)) as varchar(10)) + ' %' -- COMPUTED COLUMN
, TotYr1Cnt money
, TotYr2Cnt money
)
declare @TotYr1Cnt money = 289 -- maybe calculated elsewhere but prior to inserting data to @SrcCnt
declare @TotYr2Cnt money = 125
INSERT INTO @SrcCnt(Src,Yr1Cnt,Yr2Cnt,TotYr1Cnt,TotYr2Cnt) VALUES ('0 - 5 Years',143,43,@TotYr1Cnt,@TotYr2Cnt)
INSERT INTO @SrcCnt(Src,Yr1Cnt,Yr2Cnt,TotYr1Cnt,TotYr2Cnt) VALUES ('6 - 10 Years',28,17,@TotYr1Cnt,@TotYr2Cnt)
INSERT INTO @SrcCnt(Src,Yr1Cnt,Yr2Cnt,TotYr1Cnt,TotYr2Cnt) VALUES ('11 - 15 Years',9,5,@TotYr1Cnt,@TotYr2Cnt)
INSERT INTO @SrcCnt(Src,Yr1Cnt,Yr2Cnt,TotYr1Cnt,TotYr2Cnt) VALUES ('16 - 20 Years',7,2,@TotYr1Cnt,@TotYr2Cnt)
INSERT INTO @SrcCnt(Src,Yr1Cnt,Yr2Cnt,TotYr1Cnt,TotYr2Cnt) VALUES ('21 - 30 Years',11,3,@TotYr1Cnt,@TotYr2Cnt)
INSERT INTO @SrcCnt(Src,Yr1Cnt,Yr2Cnt,TotYr1Cnt,TotYr2Cnt) VALUES ('> 30 Years',91,55,@TotYr1Cnt,@TotYr2Cnt)
INSERT INTO @SrcCnt(Src,Yr1Cnt,Yr2Cnt,TotYr1Cnt,TotYr2Cnt) VALUES ('Total',@TotYr1Cnt,@TotYr2Cnt,@TotYr1Cnt,@TotYr2Cnt)
SELECT * FROM @SrcCnt