【问题标题】:Can I create a One-Time-Use Function in a Script or Stored Procedure?我可以在脚本或存储过程中创建一次性函数吗?
【发布时间】:2010-11-02 03:50:08
【问题描述】:

在 SQL Server 2005 中,是否存在一次性使用或在 SQL 脚本或存储过程中声明的本地函数的概念?我想抽象出我正在编写的脚本中的一些复杂性,但这需要能够声明一个函数。

只是好奇。

【问题讨论】:

  • 在没有函数的情况下,可能有更好的方法来做你想做的事。也许您应该发布您想要转换为函数的代码的 sn-p?
  • 你是动态生成一个函数,所以每次都不一样吗?如果您的功能始终相同,请将其保留在数据库中
  • 我试图这样做是为了让查询更具可读性。创建大量查询的想法很难维护。

标签: sql sql-server sql-server-2005 tsql scripting


【解决方案1】:

您可以创建临时存储过程,例如:

create procedure #mytemp as
begin
   select getdate() into #mytemptable;
end

在 SQL 脚本中,但不在函数中。不过,您可以让 proc 将其结果存储在临时表中,然后稍后在脚本中使用该信息..

【讨论】:

  • 这应该是答案。如果只有连接作用域是临时的(单#),这是真正的一次性使用,并且具有规避 sql 用户限制的好处。
  • 那它是怎么用的呢?是不是 select into 表达式中使用的过程名打错了?
  • 当我删除 BEGIN 关键字并将 END 关键字替换为 GO 时,我能够从您的示例存储过程中获得结果。
  • OP 要求临时 FUNCTION,至少 SQL Server 2012 不允许函数使用 #-syntax。只有程序。
  • 这不是脚本中的功能,可能仍需要权限。为了避免重复段,SQL 的唯一选择是 WITH 语句。
【解决方案2】:

您可以在脚本开头附近调用CREATE Function,在脚本结尾附近调用DROP Function

【讨论】:

  • 我打算建议这个。请注意您的脚本完成;如果它中止,您仍然会在数据库中拥有该功能。
  • 您可以在每次运行前进行 IF EXISTS 检查,如果发现任何内容则删除。
  • @chocojosh,如果您将其包装在事务中应该没问题。如果事务爆炸,该函数不应该在数据库中。
  • @JoelCoehoorn :这仍然需要写权限。
  • 请注意,这在函数内部不起作用 - 函数内部的临时函数是不允许的。见:technet.microsoft.com/en-us/library/ms191320.aspx#Restrictions
【解决方案3】:

Common Table Expressions 让您定义仅在您的 select、insert、update 和 delete 语句范围内有效的视图。根据您的需要,它们可能非常有用。

【讨论】:

  • 这应该被接受为正确答案。接受的答案不是线程安全的。
  • 取决于你想要做什么。我发现这个问题是因为我正在编写一个数据播种器,并且我不想重复 10 行 MERGE INTO 30 次。我不关心线程安全,CTE 对我不起作用。
  • 我认为这个答案,以及它是正确答案的断言,错过了这个问题正在寻找一个临时函数,而不是临时表。除非我遗漏了什么(并不少见)CTE 与临时表相当。
  • 函数可以接受参数,而 CTE 不能。
  • CTE 和临时存储过程之间存在许多差异(这是 IMO 的正确答案)。对于初学者来说,CTE 只存在于单个语句中,而临时变量可以在整个脚本中使用。其他差异包括:(1)CTE 不能容纳与 SP 相同的逻辑,(2)CTE 不能接受变量。 CTE 只是语法糖,让您可以更轻松地构建嵌套表表达式以在语句中使用。即使那样,如果您不了解这些警告,它们在性能方面也可能是危险的。
【解决方案4】:

我知道我可能会因为建议动态 SQL 而受到批评,但有时这是一个很好的解决方案。在考虑这一点之前,请确保您了解安全隐患。

DECLARE @add_a_b_func nvarchar(4000) = N'SELECT @c = @a + @b;';
DECLARE @add_a_b_parm nvarchar(500) = N'@a int, @b int, @c int OUTPUT';

DECLARE @result int;
EXEC sp_executesql @add_a_b_func, @add_a_b_parm, 2, 3, @c = @result OUTPUT;
PRINT CONVERT(varchar, @result); -- prints '5'

【讨论】:

    【解决方案5】:

    在脚本中,您有更多选择,并且可以更好地进行合理分解。查看 SQLCMD 模式(查询菜单 -> SQLCMD 模式),特别是 :setvar 和 :r 命令。

    在存储过程中,您的选择非常有限。您不能直接使用过程体创建定义函数。您能做的最好的事情就是这样,使用动态 SQL:

    create proc DoStuff
    as begin
    
      declare @sql nvarchar(max)
    
      /*
      define function here, within a string
      note the underscore prefix, a good convention for user-defined temporary objects
      */
      set @sql = '
        create function dbo._object_name_twopart (@object_id int)
        returns nvarchar(517) as
        begin
          return 
            quotename(object_schema_name(@object_id))+N''.''+
            quotename(object_name(@object_id))
        end
      '
    
      /*
      create the function by executing the string, with a conditional object drop upfront
      */
      if object_id('dbo._object_name_twopart') is not null drop function _object_name_twopart
      exec (@sql)
    
      /*
      use the function in a query
      */
      select object_id, dbo._object_name_twopart(object_id) 
      from sys.objects
      where type = 'U'
    
      /*
      clean up
      */
      drop function _object_name_twopart
    
    end
    go
    

    这近似于一个全局临时函数,如果存在这样的东西的话。它仍然对其他用户可见。您可以附加连接的@@SPID 以唯一化名称,但这将要求该过程的其余部分也使用动态 SQL。

    【讨论】:

      【解决方案6】:

      以下是我过去用来满足 MS SQL 中对标量 UDF 的需求:

      IF OBJECT_ID('tempdb..##fn_Divide') IS NOT NULL DROP PROCEDURE ##fn_Divide
      GO
      CREATE PROCEDURE ##fn_Divide (@Numerator Real, @Denominator Real) AS
      BEGIN
          SELECT Division =
              CASE WHEN @Denominator != 0 AND @Denominator is NOT NULL AND  @Numerator != 0 AND @Numerator is NOT NULL THEN
              @Numerator / @Denominator
              ELSE
                  0
              END
          RETURN
      END
      GO
      
      Exec ##fn_Divide 6,4
      

      这种为 PROCEDURE 使用全局变量的方法使您不仅可以在脚本中使用该函数,还可以在您的动态 SQL 需求中使用该函数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-17
        • 1970-01-01
        • 2014-03-01
        相关资源
        最近更新 更多