函数与存储过程


88

假设我必须实现一段T-SQL代码,该代码必须返回一个表作为结果。我可以实现一个表值函数或一个返回一组行的存储过程。我应该使用什么?

简而言之,我想知道的是:

函数和存储过程之间的主要区别是什么?使用其中一种时,我必须考虑哪些因素?


1
这似乎是一个完美的答案:stackoverflow.com/a/1179778/365188
Ozair Kafray 2015年

Answers:


51

如果您可能希望将这段代码的结果与其他表合并,那么显然,表值函数将使您可以在单个SELECT语句中组合结果。

通常,有一个层次结构(查看<电视功能<存储过程)。您可以在每一个中做更多的事情,但是随着功能的增加,组合输出的能力以及使优化器真正参与其中的能力会降低。

因此,只要使用最少的一项,即可表达您想要的结果。


50

函数必须是确定性的,不能用于对数据库进行更改,而存储过程则允许您进行插入和更新等操作。

您应该限制对函数的使用,因为它们会给大型,复杂的查询带来巨大的可伸缩性问题。它们成为查询优化器的“黑匣子”,您将看到使用函数和将代码简单地插入查询之间的性能差异。

但是,在非常特殊的情况下,它们对于表值收益绝对有用。

如果需要解析逗号分隔的列表,以模拟将数组传递给过程,则函数可以为您将列表变成表。这是Sql Server 2005的常见做法,因为我们还不能将表传递给存储过程(2008年可以)。


1
但是您可以将XML发送到存储过程:stackoverflow.com/questions/144550/…–
cllpse

2
错误的是,大多数SQL Server函数都是不确定的,例如MS-SQL Server中的getdate。只有ODBC函数是规范函数(=更快+可索引)...但是您是非常正确的,出于性能原因,应尽可能限制在查询中使用函数。
Stefan Steiger 2010年

45

从文档

如果存储过程满足以下条件,则可以将其重写为表值函数:

  • 该逻辑可以在单个SELECT语句中表示,但是仅由于需要参数,它才是存储过程,而不是视图。

  • 该存储过程除了表变量外不执行更新操作。

  • 不需要动态EXECUTE语句。

  • 该存储过程返回一个结果集。

  • 存储过程的主要目的是构建要装入临时表的中间结果,然后在SELECT语句中对其进行查询。


12

我将在存储过程和函数之间写一些有趣的区别。

  • 我们可以在选择查询中使用函数,但不能在选择查询中使用存储过程。
  • 我们不能在“函数”中使用非确定性函数,但可以在存储过程中使用非确定性函数。现在问题来了,什么是非确定性函数?Ans是:-

    非确定性函数是在不同时间针对相同输入值返回不同输出的函数,例如getdate()。每当运行时,它总是返回不同的值。

    例外:-

    早于sql 2000的sql server早期版本不允许在用户定义的函数中使用getdate()函数,但是从2005版开始,我们可以在用户定义的函数内使用getdate()函数。

    Newid()是非确定性函数的另一个示例,但是不能在用户定义的函数中使用,但是我们可以在存储过程中使用它。

  • 我们可以在存储过程中使用DML(插入,更新,删除)语句,但不能在物理表或永久表的函数中使用DML语句。如果要在函数中执行DML操作,则可以对表变量执行操作,而不是对永久表执行操作。

  • 我们不能在函数内使用错误处理,但可以在存储过程中进行错误处理。


MySQL函数如何支持DML操作?
Joey Pinto

@JoeyPinto。因为myNONsql不是SQL的投诉。当然,它有其他功能,但没有基础知识。
PerformanceDBA

8
  1. 过程可以返回零或n个值,而函数可以返回一个必填的值。

  2. 过程可以具有输入/输出参数,而函数只能具有输入参数。

  3. 过程允许在其中选择以及DML语句,而函数仅在其中允许select语句。

  4. 可以从过程中调用函数,而不能从函数中调用过程。

  5. 异常可以由过程中的try-catch块处理,而try-catch块不能在函数中使用。

  6. 我们可以按顺序进行事务管理,而不能按功能进行事务管理。

  7. 在select语句中不能使用过程,而函数可以在select语句中嵌入。

  8. UDF(用户定义函数)可以在WHERE/ HAVING/ SELECT部分中的任何位置的SQL语句中使用,而存储过程则不能。

  9. 返回表的UDF可以视为另一个行集。可以JOIN与其他表一起使用。

  10. 内联UDF可以作为带有参数的视图,并且可以用于JOINs和其他行集操作。


6

如果您有一个函数,则可以将其用作SQL语句的一部分,例如

SELECT function_name(field1) FROM table

对于存储过程,这种方式不起作用。


1
我认为他在谈论返回表值的函数。
wcm

1
好吧,我一般来说。但是对于我的特定情况,我现在处于存储过程或表值函数之间。
奥龙

5

我运行了一些逻辑运行很长时间的测试,在表值函数和存储过程中运行了相同的代码(较长的SELECT语句),并执行了直接的EXEC / SELECT,并且每个测试的执行方式相同。

在我看来,请始终使用表值函数而不是存储过程来返回结果集,因为它使逻辑在随后加入它们的查询中更容易读取,并且使您可以重用相同的逻辑。为了避免过多的性能下降,我经常使用“可选”参数(即您可以将NULL传递给它们)来使函数返回更快的结果集,例如:

CREATE FUNCTION dbo.getSitePermissions(@RegionID int, @optPersonID int, optSiteID int)
AS
RETURN 
    SELECT DISTINCT SiteID, PersonID
    FROM dbo.SiteViewPermissions
    WHERE (@optPersonID IS NULL OR @optPersonID = PersonID)
    AND (@optSiteID IS NULL OR @optSiteID = SiteID)
    AND @RegionID = RegionID

这样,您可以在许多不同的情况下使用此功能,并且不会对性能造成重大影响。我相信这比事后过滤更有效:

SELECT * FROM dbo.getSitePermissions(@RegionID) WHERE SiteID = 1

我已经在多个函数中使用了此技术,有时还会列出一长串此类“可选”参数。


4

当我返回的只是一个没有任何影响的表时,我个人使用表值函数。基本上,我将它们视为参数化视图。

如果需要返回多个记录集,或者表中将更新值,则可以使用存储过程。

我的2美分


4

如上所述,函数更具可读性/可组合性/可自我记录,但总体而言性能较低,如果在诸如

SELECT *
FROM dbo.tvfVeryLargeResultset1(@myVar1) tvf1
INNER JOIN dbo.tvfVeryLargeResultset1(@myVar2) tvf2
    ON (tvf1.JoinId = tvf2.JoinId)

通常,您只需要接受tvf可以消除的代码冗余(以不可接受的性能代价)。

我还没有提到的另一点是,您不能在多语句电视中使用数据库状态更改临时表。 临时表在功能上最等效的机制是在内存表变量中进行非状态更改,对于大型数据集,临时表的性能可能比表变量更高。(其他替代方法包括动态表和公用表值表达式,但在某种程度的复杂性上,它们不再是IMO的好选择。)


1

我会同时进行性能测试。sp方法或派生表可能比函数快得多,如果有,则应使用该方法。通常,我会避免使用函数,因为它们可能会降低性能。


1

这取决于:)如果要在另一个过程中使用表值结果,最好使用TableValued函数。如果结果是针对客户端的,则通常使用存储的proc是更好的方法。


-1

存储过程是预先编译的查询,执行速度更快,并且可以从sql注入中保存。它们可以返回0或N个值。我们可以在存储过程中执行DML操作。我们可以在过程中使用函数,也可以在select查询中使用函数。函数用于返回函数中不可能的任何值和DML操作。函数有标量和表值两种类型。标量函数返回单个值,用于返回表行的表值函数。


这是一个非常老的问题,有很多答案,其中很多(包括已接受的答案)都被高度评价。在为这样的主题添加另一个答案之前,您应该问自己:“所有这些现有答案缺乏什么,需要我写另一个答案?”
APC
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.