T-SQL -带有默认参数的函数

184

我有这段脚本:

CREATE FUNCTION dbo.CheckIfSFExists(@param1 INT, @param2 BIT = 1 )
RETURNS BIT
AS
BEGIN
    IF EXISTS ( bla bla bla )
        RETURN 1;
    RETURN 0;
END
GO

我想以以下方式在程序中使用它:

IF dbo.CheckIfSFExists( 23 ) = 0
    SET @retValue = 'bla bla bla';

但是我收到了以下错误信息:

未为dbo.CheckIfSFExists过程或函数提供足够的参数。

为什么它不能工作?


必须提供用户定义函数的所有参数,这是正确的答案。截至2022年,无论函数是否给定默认参数,调用它时仍必须提供一个参数或使用DEFAULT关键字以获得默认值的好处。 - Display name
4个回答

267

你必须像这样调用它

SELECT dbo.CheckIfSFExists(23, default)

来自Technet

当函数的参数有默认值时,在调用函数时必须指定关键字DEFAULT才能检索默认值。这种行为与在存储过程中使用具有默认值的参数不同,省略参数也意味着默认值。唯一的例外是通过使用EXECUTE语句调用标量函数。在使用EXECUTE时,不需要DEFAULT关键字。


104
看到这个我感到沮丧。我在这里没有得到“默认”概念的好处……现在我需要去改变所有的地方。 - LCJ
8
@Lijo,你仍然可以获得不在每次调用时重复使用具体默认值的优势。 - Frédéric
17
由于我们不能使用叠加技术,因此“默认”选项的可用性受到限制,通常最好的方法是创建一个带有后缀的新扩展版本(例如在这里称为CheckIfSFExistsEX),带有额外参数,并将原始函数更改为只调用具有“默认”参数的扩展版本。这样一来,所有现有的代码都可以正常运行,而且您只需要维护一个地方。 - Eske Rahn
19
哎呀,微软啊。如果你必须要传递一个值,那么这个参数就不是真正的“可选”的参数了。希望他们能在未来版本中改变这一点。 - Ben
3
@Ben 同意,但为他们辩护,他们没有说这是一个可选参数。它是一个带有默认值的参数。它的唯一好处是使函数调用者不必知道要将参数设置为什么值。我猜他们觉得让调用者确认参数的存在很重要 - 即选择退出。但我也更喜欢可选参数。 - xr280xr
更让人生气的是,函数也不允许你给参数起名字。所以你要么得去找定义,要么就坐在那儿想“23”和“default”到底代表什么。这相当烦人,特别是考虑到存储过程(一个更老的规范)一直支持命名参数。 - Daniel Liuzzi

46

你可以用三种方式进行调用 - 带参数、带默认值和通过执行

SET NOCOUNT ON;

DECLARE
@Table  SYSNAME = 'YourTable',
@Schema SYSNAME = 'dbo',
@Rows   INT;

SELECT dbo.TableRowCount( @Table, @Schema )

SELECT dbo.TableRowCount( @Table, DEFAULT )

EXECUTE @Rows = dbo.TableRowCount @Table

SELECT @Rows

19
为什么在select语句中需要使用DEFAULT关键字,但是在execute语句中可以省略?这很糟糕:/ 希望有一天能够解决这个问题。 - Misiu
1
@Misiu,这不是需要“修复”的事情。这是有意设计的。我看了很多实现目标为仅需调用函数而无需指定每个参数的替代方法,但我没有看到清晰的解释为什么需要这样做。代码应该清晰易懂,其中一个策略是要求编码人员始终意识到“嘿,你正在调用具有这些其他参数的函数,这些参数恰好具有默认值。 不要忘记默认值可能会被更改”。因此,在我看来,这是一个良好的“坏事”。 - Gustavo Pinsard
3
@GustavoPinsard 允许这样做的一个好理由是当你向函数添加新参数时。如果你不必指定默认值,函数可以继续在现有应用程序中工作。如果你现在想要这样做,你需要创建一个带有额外参数的新函数,并从原始(现在已弃用)函数中调用它。这是有一定价值的,但在我看来它会在数据库中产生额外的噪音。新函数通常比原始函数命名更糟糕,因为函数重载也是不可能的。 - Jamie

21

使用用户定义的函数时,即使参数有默认值,也必须声明每个参数。

以下代码将成功执行:

IF dbo.CheckIfSFExists( 23, default ) = 0
    SET @retValue = 'bla bla bla;

这是最好的答案。 - Display name

-3

解决这个问题的方法之一是使用带有输出参数的存储过程。

执行 sp_mysprocname @returnvalue 输出, @firstparam = 1, @secondparam=2

你不传递的值默认为存储过程中设置的默认值。并且您可以从输出变量中获取结果。


4
一般来说,将您的函数更改为存储过程并不是一个好的解决方案,因为存储过程不能从查询中调用,但函数可以。 - Blade
真的,但并不是所有的代码块都需要从查询中调用。已经证明,SQL没有一个很好的处理函数默认值的方法(使用default关键字几乎和添加一个值一样费力)。这不是一个好的通用解决方案,但在某些用例中表现得非常出色。 - Jereme
人们一直在贬低这个,但我仍然坚持。如果您需要一个可重用的代码块,不会在查询中被调用,并且希望具有真正可选参数和默认值的灵活性,则存储过程仍然比函数更好。 - Jereme
1
使用存储过程并不意味着使用它们来替代函数 - 它可以意味着将存储过程用作函数的包装器。我经常使用这种技术;现在,默认关键字被隐藏在proc内部。我认为这个想法很好。它还让我可以更轻松地创建更复杂的默认值 - 与函数分开,从而可以保持更纯洁的状态。 - J Bryan Price

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接