我可以在脚本或存储过程中创建一次性功能吗?


109

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

只是好奇。


没有功能,可能是更好的方法。也许您应该发布要转换为功能的代码片段?
DForck42 2009年

您是否在动态生成函数,所以每次都不同?如果你的功能是始终不变的只是将它留在数据库
KM。

1
我试图这样做是为了使查询更具可读性。创建庞大查询的想法很难维护。
Jp_

Answers:


65

您可以CREATE Function在脚本开头DROP Function附近和结尾附近进行调用。


6
我打算建议这个。只要注意您的脚本完成即可;如果中止,您仍然可以在数据库中使用该功能。
chocojosh 2009年

6
您可以在每次运行之前进行IF EXISTS检查,如果发现任何错误,则将其删除。
阿德里安·高东

7
@chocojosh,如果将其包装在事务中应该没问题。如果交易发生爆炸,则该函数不应在数据库中。
杰夫·拉法

12
@JoelCoehoorn:这仍然需要写权限。
user2284570 2015年

2
请注意,这在函数内部不起作用-不允许在函数内部使用临时函数。请参阅:technet.microsoft.com/zh-cn/library/ms191320.aspx#Restrictions
Daniel Neel 2015年

95

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

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

在SQL脚本中,但不起作用。您可以将proc的结果存储在临时表中,然后稍后在脚本中使用该信息..


7
这应该是答案。如果仅连接作用域是临时的(单号),则这是真正的一次性使用,并且具有规避sql用户限制的优点。
托德

那怎么用呢?选择表达式中使用的过程名称不是错字吗?
jgomo3 2013年

当我删除BEGIN关键字并将END关键字替换为时,我可以从您的示例存储过程中获取结果GO
约瑟夫·迪克斯特拉

OP要求使用临时功能,至少SQL Server 2012不允许对函数使用#语法。只有程序。
Erk,2018年

那不是脚本内的功能,可能仍然需要权限。为了避免重复段,SQL唯一的选择是WITH语句。
alex.peter

25

通用表表达式使您可以定义本质上仅在选择,插入,更新和删除语句范围内有效的视图。根据您需要执行的操作,它们可能会非常有用。


5
这应该被接受为正确答案。接受的答案不是线程安全的。
kalyan 2014年

11
取决于您要执行的操作。我发现了这个问题,因为我正在写一个数据播种器,并且我不想将10行MERGE INTO重复30次。我不在乎线程安全,CTE对我不起作用。
solipsicle

16
我认为这个答案以及它是正确答案的断言都错过了这个问题正在寻找临时函数而不是临时表的想法。除非我缺少某些东西(不少见),否则CTE与临时表相当。
JD Long

8
函数可以接受参数,而CTE不能。
拉兹万·弗拉维斯·熊猫

4
CTE和临时存储过程之间有很多区别(这是IMO的正确答案)。对于初学者,CTE仅针对单个语句存在,而临时变量可以在整个脚本中使用。其他差异包括:(1)CTE无法容纳SP可以容纳的相同逻辑;(2)CTE无法接受变量。CTE只是语法糖,它使您可以更轻松地构建嵌套表表达式以在语句中使用。即使这样,如果您不注意警告,它们也会对性能造成危险。
停泊

12

我知道我可能会因为建议使用动态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'

4

在脚本中,您有更多选择,并且可以更好地进行理性分解。查看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。


3

以下是我过去用来满足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使用全局变量的方法不仅使您可以在脚本中使用功能,而且可以在Dynamic SQL需求中使用该功能。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.