在存储过程之间共享数据

时间:2015-03-20 19:07:50

标签: sql-server tsql stored-procedures

我有一个名为dbo.Match的存储过程。 它看起来像这样:

CREATE Procedure [dbo].[MATCH]
@parameterFromUser  nvarchar(30),
@checkbool int
As 
Begin
--SOME CODE
select RowId, 
       PercentMatch 
from @Matches
End

正在从另一个存储过程调用此过程:

CREATE Procedure MatchMotherFirstName 
@MotherFN nvarchar(20) , @checkbool int
As begin
 SELECT @constVal = FunctionWeight 
 FROM   dbo.FunctionWeights 
 WHERE  FunctionWeights.FunctionId = 20;
 /*
    Some code to execute `dbo.Match` procedure in above procedure called `MatchMotherFirstName` , retrieve `RowNumber` and `PercentMatch`,
    Insert into #Temp in their respective fields , and calculate `PercentMatch * constVal`,
    and insert in corresponding column called `percentage` in `#Temp`
 */
End 

我需要在上面的过程中执行dbo.Match存储过程,检索RowID和PecrntMatch值,我们上面的@constval值,乘以@constValpercentmatch并将其存储在Percentage的{​​{1}}列中,并将#Temp的结果插入临时表中。 dbo.Match procedure仅返回dbo.MatchRowId

临时表的结构:

PercentMatch

create table #Temp ( Rownumber int not null, ValFromUser nvarchar(30), ColumnName nvarchar(30), ValFromFunc decimal(18, 4), FuncWeight decimal(18, 4), -- @constVal here Percentage decimal(18, 4) not null, -- calculated value here i.e (FuncWeight * ValFromFunc) ); 中,我需要插入#Temp的值以及计算一个列,并为仅在此执行调用中插入的行插入@constVal。 我怎样才能以最有效的方式在上述程序中做到这一点?

编辑: 为了清楚起见,如果PercentMatch * contVal是一个函数而不是一个过程,我正在做的是:

dbo.Match

就像我可以检索 if @MotherFN is not null begin SELECT @constVal = FunctionWeight FROM dbo.FunctionWeights WHERE FunctionWeights.FunctionId = 20; INSERT INTO #Temp2 (RowNumber,ValFromUser,ColumnName,ValFromFunc,FuncWeight,percentage) SELECT RowId, @MotherFN , 'mothersfirstname' ,PercentMatch, @constVal, PercentMatch * @constVal FROM dbo.MatchMatch(@MotherFN, 0) end Percentmatch的值并将它们两者相乘以插入#Temp一样,我在执行@constval过程时如何执行此操作而不是调用{{ 1}}功能?

3 个答案:

答案 0 :(得分:1)

您有多种选择,从难以置信的容易到过于复杂。做你所描述的最简单(也是最有效)的方法是:

  1. 不要这样做:只需在查询中包含该计算。为什么需要在表定义中?

  2. 在创建临时表时添加计算列。这要求您还包含一个字段来存储"常数值"这样它就可以被计算列引用。如果计算有点昂贵和/或会有很多行并且经常从(并且可能在WHERE和/或ORDER BY子句中使用)中选择,那么您可以创建计算列PERSISTED以便计算它在INSERT以及更新计算列中引用的字段的任何UPDATE

  3. 在创建表后,在临时表中添加计算列。这允许嵌入"常数值"进入计算列,这样就不需要[ConstantValue]列。如果计算有点昂贵和/或会有很多行并且经常从(并且可能在WHERE和/或ORDER BY子句中使用)中选择,那么您可以创建计算列PERSISTED以便计算它在INSERT和任何UPDATE更新计算列中引用的字段。

    P.S。万一你发现自己要求"为什么不只是一步而不是两步动态创建临时表?":在{{1}之后,动态SQL中创建的本地临时表将不复存在那个动态SQL。全局临时表将在执行动态SQL后继续存在,但随后表名将在所有会话中共享,因此同时执行此代码的另一个会话将在名称冲突时出错。在这种情况下,您需要通过EXEC生成GUID以用作全局临时表名称,并将该值连接到动态SQL中,但是您将无法使用动态SQL进行所有引用全局临时表(包括NEWID()),这只是更有效的工作。

  4. 测试设置

    INSERT...EXEC

    选项1

    IF (OBJECT_ID(N'tempdb..#InnerProc') IS NOT NULL)
    BEGIN
      DROP PROCEDURE #InnerProc;
    END;
    GO
    
    IF (OBJECT_ID(N'tempdb..#TempResults1') IS NOT NULL)
    BEGIN
      DROP TABLE #TempResults1;
    END;
    IF (OBJECT_ID(N'tempdb..#TempResults2') IS NOT NULL)
    BEGIN
      DROP TABLE #TempResults2;
    END;
    IF (OBJECT_ID(N'tempdb..#TempResults3') IS NOT NULL)
    BEGIN
      DROP TABLE #TempResults3;
    END;
    GO
    
    CREATE PROCEDURE #InnerProc
    AS
    SET NOCOUNT ON;
    
      SELECT TOP 20 so.[object_id], so.[modify_date]
      FROM   [master].[sys].[objects] so
      ORDER BY so.[modify_date] DESC;
    GO
    

    选项2

    CREATE TABLE #TempResults1
    (
      [ObjectId] INT NOT NULL,
      [ModifyDate] DATETIME NOT NULL
    );
    
    DECLARE @ConstantValue1 INT;
    SET @ConstantValue1 = 13;
    
    INSERT INTO #TempResults1 ([ObjectId], [ModifyDate])
      EXEC #InnerProc;
    
    SELECT 1 AS [Test], *, DATEADD(DAY, @ConstantValue1, [ModifyDate]) AS [SomeCalculation]
    FROM #TempResults1;
    

    选项3

    CREATE TABLE #TempResults2
    (
      [ObjectId] INT NOT NULL,
      [ModifyDate] DATETIME NOT NULL,
      [ConstantValue] INT NULL, -- will be added via UPDATE
      [SomeCalculation] AS (DATEADD(DAY, [ConstantValue], [ModifyDate])) -- PERSISTED ??
    );
    
    INSERT INTO #TempResults2 ([ObjectId], [ModifyDate])
      EXEC #InnerProc;
    
    SELECT 2 AS [Test], * FROM #TempResults2;
    
    UPDATE #TempResults2
    SET    [ConstantValue] = 13;
    
    SELECT 2 AS [Test], * FROM #TempResults2;
    

答案 1 :(得分:0)

嗯,总的来说,如果你只需要做简单的事情就没有创造复杂逻辑的价值。在您描述的场景中,我倾向于认为最好的方法是使用可以随时通过dbo.Match和dbo.MatchMotherFirstName过程访问的物理表。如果您不想在逻辑执行后将其保留在数据库中,请使用CREATE / DROP语句根据您的需要创建/删除表。

答案 2 :(得分:0)

你有3个足够简单的选择。其中一个具有相当大的性能,一个需要在服务器上进行配置更新,一个需要更改匹配存储过程。

选项1 在MatchMotherFirstName过程中,为匹配结果声明一个表。

CREATE TABLE #tmpMatchResults (Col1 , Col2....)
Insert into #tmpMatchResults
EXEC [dbo].[MATCH]

这会影响性能,但它可以在不对Match proc代码或服务器配置进行任何更改的情况下运行。如果你只想要很少的线,这将很好地工作

选项2 使用OpenRowSet或OpenQuery

Select * FROM OPENROWSET(connection,'Exec database.dbo.MATCH')

这需要更改配置以允许数据访问

选项3 更新MATCH存储过程以将结果推送到临时表

CREATE Procedure [dbo].[MATCH]
--SOME CODE
select RowId, PercentMatch from #tmpMatches

请确保不要将临时表放在proc

的末尾

然后在MatchMotherFirstName过程中,当会话处于活动状态时,您可以调用proc

EXEC dbo.MATCH @param

,结果设置为

SELECT * FROM #tmpMatches

有些人会争辩说你应该在MATCH proc调用结束时清理(删除表)临时表。您可以在MATCH过程中包含一个参数来保存结果或进行表清理。