SQL Server存储过程在临时表上重复操作

时间:2018-09-18 06:54:32

标签: sql-server tsql stored-procedures

我在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 /复制并粘贴几次更好的方法。

1 个答案:

答案 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