【问题标题】:Can we write a sub function or procedure inside another stored procedure我们可以在另一个存储过程中编写一个子函数或过程吗
【发布时间】:2009-01-30 14:37:41
【问题描述】:

我想检查 SQL Server(2000/05/08) 是否具有编写嵌套存储过程的能力,我的意思是 - 在另一个存储过程中编写子函数/过程。不调用另一个 SP。

我为什么要考虑它是-我的一个 SP 有重复的代码行,并且只针对这个 SP。所以如果我们有这个嵌套的 SP 功能,那么我可以在我的内部声明另一个子/本地过程主 SP 并将所有重复行放入其中。我可以在我的主 SP 中调用该本地 sp。我记得 Oracle SP 中提供了这样的功能。

如果 SQL Server 也具有此功能,请有人解释一下有关它的更多详细信息或提供一个链接,我可以在其中找到文档。

提前致谢 赛

【问题讨论】:

    标签: sql sql-server-2005


    【解决方案1】:

    我不建议这样做,因为每次创建时都必须计算一个新的执行计划,但是是的,它绝对可以完成(一切皆有可能,但并不总是推荐)。

    这是一个例子:

    CREATE PROC [dbo].[sp_helloworld]
    AS
    BEGIN
        SELECT 'Hello World'
        DECLARE @sSQL VARCHAR(1000)
        SET @sSQL = 'CREATE PROC [dbo].[sp_helloworld2]
                AS
                BEGIN
                    SELECT ''Hello World 2''
                END'
        EXEC (@sSQL)
    
        EXEC [sp_helloworld2];
        DROP PROC [sp_helloworld2];
    END
    

    你会收到警告

    The module 'sp_helloworld' depends on the missing object 'sp_helloworld2'.
    The module will still be created; however, it cannot run successfully until
    the object exists.
    

    您可以使用上面的 EXEC('sp_helloworld2') 绕过此警告。

    但是如果你调用 EXEC [sp_helloworld] 你会得到结果

    Hello World
    Hello World 2
    

    【讨论】:

    • 似乎这个答案是关于如何破坏缓存的执行计划并增加不必要的开销的一个教训......我的建议是永远不要做任何事情......
    • 答案是为了表明通过使用动态 SQL 是可能的。 ..当然,所有关于它的大红色警告。我认为在实际生产环境中使用这样的东西没有意义。
    【解决方案2】:

    它没有这个功能。除了阻止嵌套 SPROC 中的代码被其他地方调用之外,很难看出这样的功能会提供什么真正的好处。

    【讨论】:

    • 尝试定义一次硬编码的简单哈希表映射怎么样,因为您不想在 SP 中对其进行多次硬编码。然而,您不想将这种硬编码暴露给外界。
    • 它的真正好处是不会用只在一个地方使用的特定于任务的功能和过程弄乱我的数据库!
    • 这对于可以打开和运行 .SQL 文件但不能存储过程或用户定义函数的只读用户也是必需的。访问 prod 或 pre-prod 的测试人员和开发人员有时属于该类别。
    • 为什么 T SQL 不能从作用域子例程中受益?几乎每种语言都有。 BASIC 有。
    【解决方案3】:

    Oracle 的 PL/SQL 是一种特殊情况,它是一种严重基于 Ada 的语言,而不是带有一些过程结构的简单 DML。您是否认为这是一个好主意可能取决于您对 DBMS 中的过程代码的兴趣以及您对学习复杂的新语言的喜好。

    根据我的经验(主要是 Oracle、MS SQL、Sybase、MySQL、SQLite),为了减少重复或其他原因而使用子例程的想法在很大程度上与其他数据库平台不同。

    虽然 SQL 构建过程会起作用,但我认为 John 建议您不要使用他原本正确的答案是正确的!

    你没有说你的重复行采用什么形式,所以我将提供三种可能的替代方案,从最简单的开始:

    1. 什么都不做。接受该程序 SQL是一种原始语言,缺乏 如此多的“基本”结构 如果它,你根本不会使用它 不是唯一的选择。

    2. 将您的过程操作移到 DBMS 之外,并在用更复杂的语言编写的代码中执行它们。考虑调整架构以从数据存储平台中提取业务逻辑的方式(嘿,为什么不重新设计整个东西!)

    3. 如果在 DML(尤其是 SELECT)中发生重复,请考虑引入视图以减少查询。

    4. 编写代码以生成存储过程,作为构建过程的一部分。这样,如果重复的行需要更改,您可以在一处更改它们并自动生成重复。

    那是four。我在打字时想到了另一个;把它当作奖金。

    【讨论】:

      【解决方案4】:
      CREATE TABLE #t1 (digit INT, name NVARCHAR(10));  
      GO
      
      CREATE PROCEDURE #insert_to_t1  
      (  
          @digit INT  
      ,    @name NVARCHAR(10)  
      )  
      AS  
      BEGIN  
          merge #t1 AS tgt  
          using (SELECT @digit, @name) AS src (digit,name)  
          ON    (tgt.digit = src.digit)  
          WHEN matched THEN  
                UPDATE SET name = src.name  
          WHEN NOT matched THEN  
                INSERT (digit,name) VALUES (src.digit,src.name);  
      END;  
      GO  
      
      
      EXEC #insert_to_t1 1,'One';  
      EXEC #insert_to_t1 2,'Two';  
      EXEC #insert_to_t1 3,'Three';  
      EXEC #insert_to_t1 4,'Not Four';  
      EXEC #insert_to_t1 4,'Four'; --update previous record!  
      
      
      SELECT    * FROM #t1;
      

      我们在这里所做的是创建一个过程,该过程在连接的整个生命周期内都存在,然后用于将一些数据插入到表中。

      【讨论】:

        【解决方案5】:

        John 的 sp_helloworld 确实有效,但这就是您不经常看到此操作的原因。

        编译存储过程时会对性能产生非常大的影响。微软有一篇关于解决大量重新编译导致的性能问题的文章,因为这确实会降低你的系统速度:

        http://support.microsoft.com/kb/243586

        与其创建存储过程,不如只使用要调用的 T-SQL 创建一个字符串变量,然后重复执行该字符串变量。

        不要误会我的意思 - 这也是一个非常糟糕的性能想法,但它比动态创建存储过程要好。如果您可以将此代码持久保存在永久存储过程或函数中并消除重新编译延迟,SQL Server 可以为代码构建一个单一的执行计划,然后非常快速地重用该计划。

        【讨论】:

          【解决方案6】:

          我刚刚在 SQL 触发器(类似于 SQL 过程)中遇到了类似的情况,其中对于 1 个事件导致的 13 个可能的键值,我基本上有相同的插入语句最多执行 13 次。我使用了一个计数器,使用 DO WHILE 循环了 13 次,并使用 CASE 处理每个键值,同时保留一个标志来确定何时需要插入以及何时跳过。

          【讨论】:

          • 不确定这是否回答了问题并假设重复的代码可以简单地循环?
          【解决方案7】:

          我也需要这个。我有两个函数可以将案例计数带回存储过程,该存储过程正在提取所有用户的列表及其案例计数。

          顺理成章

          select name, userID, fnCaseCount(userID), fnRefCount(UserID)
            from table1 t1
            left join table2 t2
              on t1.userID = t2.UserID
          

          对于一个相对较小的集合(400 个用户),它会调用这两个函数中的每一个。总共有 800 个来自存储过程的调用。不漂亮,但人们不会期望 sql server 有这么几个调用的问题。

          这需要 4 多分钟才能完成。

          就个人而言,函数调用几乎是瞬间完成的。即使是 800 个近乎瞬时的调用也应该是近乎瞬时的。

          所有索引都已就位,当针对存储过程和函数分析执行计划时,SSMS 建议没有新索引。

          我从函数中复制代码,并将其放入存储过程中的 SQL 查询中。但似乎 sp 和 function 之间的转换占用了时间。

          18 秒的执行时间仍然太长,但允许查询在我们的 30 秒超时窗口内完成。

          如果我可以有一个子过程,它会使代码更漂亮,但仍然可能增加开销。

          接下来我可能会尝试将相同的功能移动到我可以在连接中使用的视图中。

          select t1.UserID, t2.name, v1.CaseCount, v2.RefCount
            from table1 t1
            left join table2 t2
              on t1.userID = t2.UserID
            left join vwCaseCount v1
              on v1.UserID = t1.UserID
            left join vwRefCount v2
              on v2.UserID = t1.UserID
          

          好的,我刚刚从函数中创建了视图,所以我的执行时间从 4 多分钟到 18 秒再到 8 秒。我会一直玩下去的。

          【讨论】:

            【解决方案8】:

            如果MS在GOTO之外开发GOSUB就好了,很简单!

            为“内部例程”创建过程或函数会污染对象结构。

            我这样“实现”它

            身体 1:

            转到 HEADER HEADER_RET1:

            insert into body ...
            
            goto BODY1_RET
            

            身体2:

            转到 HEADER HEADER_RET2:

            INSERT INTO body....
            
            goto BODY2_RET
            

            标题:

            insert into header
            
            if @fork=1 goto HEADER_RET1
            
            if @fork=2 goto HEADER_RET2
            
            select 1/0 --flow check!
            

            【讨论】:

            • 它可能会工作,但很难调试和维护。如前所述(三年前......)单独的触发是要走的路。
            【解决方案9】:

            我同意 andynormancx 的观点,这样做似乎没有多大意义。

            如果您真的希望共享代码包含在 SP 中,那么您可能会用 GOTO 或动态 SQL 拼凑一些东西,但使用单独的 SP 或 UDF 几乎在所有方面都做得更好。

            【讨论】:

              【解决方案10】:

              感谢大家的回复! 我最好用重复的代码再创建一个 SP 并调用它,这是性能和外观方面的最佳方式。

              【讨论】:

              • 您需要找出每次更改的变量并将这些输入输入到您的 SP。我不确定您的应用程序,但我只能为每组输入参数假设某种循环。
              猜你喜欢
              • 2011-01-21
              • 1970-01-01
              • 2019-11-28
              • 2023-04-02
              • 2014-09-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多