SQL Server中的函数与存储过程

Function vs. Stored Procedure in SQL Server

我学习函数和存储过程已经有一段时间了,但我不知道为什么以及何时应该使用函数或存储过程。对我来说,它们看起来是一样的,也许是因为我对这个有点陌生。

有人能告诉我为什么吗?


函数是计算值,不能对SQL Server执行永久环境更改(即不允许使用INSERT或UPDATE语句)。

如果函数返回标量值,则可以在SQL语句中以内联方式使用;如果函数返回结果集,则可以对其进行联接。

从评论中值得注意的一点,它概括了答案。感谢@Sean K Anderson:

Functions follow the computer-sciency definition in that they MUST return a value and cannot alter the data they receive as parameters
(the arguments). Functions are not allowed to change anything, must
have at least one parameter, and they must return a value. Stored
procs do not have to have a parameter, can change database objects,
and do not have to return a value.


SP与UDF的区别如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
+---------------------------------+----------------------------------------+
| Stored PROCEDURE (SP)           | FUNCTION (UDF - USER Defined           |
|                                 | FUNCTION)                              |
+---------------------------------+----------------------------------------+
| SP can RETURN zero , single OR  | FUNCTION must RETURN a single VALUE    |
| multiple VALUES.                | (which may be a scalar OR a TABLE).    |
+---------------------------------+----------------------------------------+
| We can USE TRANSACTION IN SP.   | We can't use transaction in UDF.       |
+---------------------------------+----------------------------------------+
| SP can have input/output        | Only input parameter.                  |
| parameter.                      |                                        |
+---------------------------------+----------------------------------------+
| We can call function from SP.   | We can'
t CALL SP FROM FUNCTION.        |
+---------------------------------+----------------------------------------+
| We can't use SP in SELECT/      | We can use UDF in SELECT/ WHERE/       |
| WHERE/ HAVING statement.        | HAVING statement.                      |
+---------------------------------+----------------------------------------+
| We can use exception handling   | We can'
t USE Try-Catch block IN UDF.   |
| USING Try-Catch block IN SP.    |                                        |
+---------------------------------+----------------------------------------+


函数和存储过程有不同的用途。虽然这不是最好的类比,但是函数可以从字面上看作是任何编程语言中使用的任何其他函数,但是存储过程更像是单个程序或批处理脚本。

函数通常有一个输出和可选的输入。然后,输出可以用作另一个函数(SQL Server内置的,如datediff、len等)的输入,也可以用作SQL查询的谓词,如SELECT a, b, dbo.MyFunction(c) FROM tableSELECT a, b, c FROM table WHERE a = dbo.MyFunc(c)

存储过程用于在事务中将SQL查询绑定在一起,并与外部世界进行接口。ADO.NET等框架不能直接调用函数,但可以直接调用存储过程。

不过,函数确实有一个潜在的危险:它们可能被误用并导致相当严重的性能问题:请考虑以下查询:

1
SELECT * FROM dbo.MyTable WHERE col1 = dbo.MyFunction(col2)

其中myfunction声明为:

1
2
3
4
5
6
7
8
9
10
11
CREATE FUNCTION MyFunction (@someValue INTEGER) RETURNS INTEGER
AS
BEGIN
   DECLARE @retval INTEGER

   SELECT localValue
      FROM dbo.localToNationalMapTable
      WHERE nationalValue = @someValue

   RETURN @retval
END

这里所发生的是为表myTable中的每一行调用函数myFunction。如果myTable有1000行,那么这是对数据库的另外1000个特别查询。同样,如果在列规范中指定函数时调用该函数,则将为select返回的每一行调用该函数。

所以你需要小心编写函数。如果您确实从函数中的表中进行选择,那么您需要问问自己,在父存储过程中使用join或其他一些SQL构造(例如case…什么时候?否则…结束)。


当需要计算时编写一个用户定义函数,并返回一个值以在其他SQL语句中使用;而当需要对可能复杂的一组SQL语句进行分组时编写一个存储过程。毕竟,这是两个完全不同的用例!


存储过程和用户定义函数之间的差异:

  • 不能在select语句中使用存储过程。
  • 存储过程支持延迟的名称解析。
  • 存储过程通常用于执行业务逻辑。
  • 存储过程可以返回任何数据类型。
  • 与用户定义的函数相比,存储过程可以接受更多的输入参数。存储过程最多可以有21000个输入参数。
  • 存储过程可以执行动态SQL。
  • 存储过程支持错误处理。
  • 存储过程中可以使用非确定性函数。


  • 用户定义的函数可以在select语句中使用。
  • 用户定义函数不支持延迟的名称解析。
  • 用户定义的函数通常用于计算。
  • 用户定义的函数应该返回一个值。
  • 用户定义的函数不能返回图像。
  • 用户定义函数接受的输入参数数量比存储过程少。UDF最多可以有1023个输入参数。
  • 不能在用户定义函数中使用临时表。
  • 用户定义函数无法执行动态SQL。
  • 用户定义的函数不支持错误处理。UDF中不允许使用RAISEERROR@@ERROR
  • 在UDF中不能使用非确定性函数。例如,GETDATE()不能在UDF中使用。


基本差别

函数必须返回一个值,但在存储过程中它是可选的(过程可以返回零或n个值)。

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

函数接受一个输入参数这是必需的,但存储过程可能接受O到N个输入参数。

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

预付款差额

过程允许在其中使用select和dml(insert/update/delete)语句,而函数只允许在其中使用select语句。

过程不能用于select语句,而函数可以嵌入到select语句中。

存储过程不能在WHERE/HAVING/SELECT部分的任何位置的SQL语句中使用,而函数可以。

返回表的函数可以作为另一行集处理。这可以用于与其他表的联接。

内联函数可以是接受参数的as视图,也可以用于联接和其他行集操作。

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

我们可以在程序中进行事务管理,而不能在功能中进行。

来源


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
              STORE PROCEDURE                 FUNCTION (USER DEFINED FUNCTION)    
 * PROCEDURE can RETURN 0, single OR   | * FUNCTION can RETURN ONLY single VALUE  
   multiple VALUES.                    |
                                       |
 * PROCEDURE can have INPUT, output    | * FUNCTION  can have ONLY INPUT
   parameters.                         |   parameters.        
                                       |
 * PROCEDURE cannot be called FROM     | * Functions can be called FROM
   FUNCTION.                           |   PROCEDURE.
                                       |
 * PROCEDURE allows SELECT AS well AS  | * FUNCTION allows ONLY SELECT statement
   DML statement IN it.                |   IN it.
                                       |
 * Exception can be handled BY         | * Try-catch block cannot be used IN a
   try-catch block IN a PROCEDURE.     |   FUNCTION.
                                       |
 * We can GO FOR TRANSACTION management| * We can't go for transaction
   in procedure.                       |   management in function.
                                       |
 * Procedure cannot be utilized in a   | * Function can be embedded in a select
   select statement                    |   statement.
                                       |
 * Procedure can affect the state      | * Function can not affect the state
   of database means it can perform    |   of database means it can not    
   CRUD operation on database.         |   perform CRUD operation on
                                       |   database.
                                       |
 * Procedure can use temporary tables. | * Function can not use
                                       |   temporary tables.
                                       |
 * Procedure can alter the server      | * Function can not alter the  
   environment parameters.             |   environment parameters.
                                       |  
 * Procedure can use when we want      | * Function can use when we want
   instead is to group a possibly-     |   to compute and return a value
   complex set of SQL statements.      |   for use in other SQL
                                           statements.

用户定义函数是SQL Server程序员可用的重要工具。您可以在这样的SQL语句中内联使用它

1
SELECT a, lookupValue(b), c FROM customers

其中,lookupValue将是一个自定义项。当使用存储过程时,这种功能是不可能的。同时,你不能在UDF中做某些事情。这里要记住的基本点是,UDF的:

  • 无法创建永久更改
  • 无法更改数据

存储过程可以执行这些操作。

对我来说,UDF的内联用法是UDF最重要的用法。


存储过程用作脚本。它们为您运行一系列命令,您可以安排它们在特定时间运行。

函数用作方法。你给它传递了一些东西,它会返回一个结果。应该是又小又快的-在飞行中。


存储过程:

  • 就像SQL Server中的微型程序。
  • 可以像select语句一样简单,也可以像long语句一样复杂添加、删除、更新和/或从多个数据库中的表。
  • (可以实现循环和光标,两者都允许您使用较小的结果或对数据进行逐行操作。)
  • 应该使用EXECEXECUTE语句调用。
  • 返回表变量,但不能使用OUT参数。
  • 支持事务处理。

功能:

  • 不能用于更新、删除或向数据库添加记录。
  • 只返回单个值或表值。
  • 只能用于选择记录。但是,它可以被称为很容易从标准SQL中获得,例如:

    1
    SELECT dbo.functionname('Parameter1')

    1
    SELECT Name, dbo.Functionname('Parameter1') FROM sysObjects
  • 对于简单的可重用选择操作,函数可以简化代码。注意在函数中使用JOIN子句。如果你函数有一个JOIN子句,您可以从另一个select调用它返回多个结果的语句,该函数调用将对于结果集中返回的每一行,这些表都在一起。所以尽管它们有助于简化某些逻辑,但它们也可以是如果使用不当,会造成性能瓶颈。

  • 使用OUT参数返回值。
  • 不支持事务。

SQL Server函数和光标一样,是用来作为最后的武器的!它们确实存在性能问题,因此应尽量避免使用表值函数。谈论性能是指一个包含超过1000000条记录的表托管在一个服务器上的一个中产阶级硬件上;否则,您不需要担心函数对性能的影响。

  • 从不使用函数将结果集返回到外部代码(如ADO.NET)
  • 尽可能使用视图/存储过程组合。您可以使用DTA(数据库调优顾问)提供的建议(如索引视图和统计信息)从将来的增长性能问题中恢复—有时!
  • 如需进一步参考,请参阅:http://databases.aspfaq.com/database/should-i-use-a-view-a-stored-procedure-or-a-user-defined-function.html


    决定何时使用以下几点可能有帮助-

  • 存储过程无法返回AS函数可以执行此操作的表变量。

  • 您可以使用存储过程更改服务器环境参数,因为使用的函数您不能。

  • 干杯


    从返回单个值的函数开始。好的是,您可以将经常使用的代码放入函数中,并将它们作为列返回到结果集中。

    然后,您可以使用一个函数来参数化城市列表。getcitiesin("ny"),返回可以用作联接的表。

    这是一种组织代码的方法。知道什么时候东西是可重复使用的,什么时候它是浪费时间,这只是通过尝试、错误和经验获得的。

    此外,函数在SQL Server中也是一个好主意。它们速度更快,而且威力也很大。内联和直接选择。小心不要过度使用。


    • 当函数不用于存储过程时,它必须返回一个值。
    • select语句仅在UDF中接受,而不需要dml语句。
    • 存储过程接受任何语句以及DML语句。
    • UDF只允许输入,不允许输出。
    • 存储过程允许输入和输出。
    • catch块不能在UDF中使用,但可以在存储过程中使用。
    • 在UDF函数中不允许任何事务,但在存储过程中允许这些事务。
    • 在UDF中只能使用表变量,不能使用临时表。
    • 存储过程同时允许表变量和临时表。
    • UDF不允许从函数调用存储过程,而存储过程允许调用函数。
    • UDF用于join子句,而存储过程不能用于join子句。
    • 存储过程将始终允许返回零。相反,UDF的值必须返回到预定的点。

    这里有一个实际的原因,它比存储过程更喜欢函数。如果有一个存储过程需要另一个存储过程的结果,则必须使用insert exec语句。这意味着您必须创建一个临时表,并使用exec语句将存储过程的结果插入临时表中。太乱了。其中一个问题是不能嵌套insert execs。

    如果您一直使用调用其他存储过程的存储过程,则可能会遇到这种情况。如果嵌套存储过程只返回一个数据集,则可以用表值函数替换它,您将不再得到此错误。

    (这也是我们应该将业务逻辑排除在数据库之外的另一个原因)


    我意识到这是一个非常古老的问题,但是我没有看到任何答案中提到的一个关键方面:内联到查询计划中。

    函数可以是…

  • Scalar:

    CREATE FUNCTION ... RETURNS scalar_type AS BEGIN ... END

  • 多语句表值:

    CREATE FUNCTION ... RETURNS @r TABLE(...) AS BEGIN ... END

  • 内联表值:

    CREATE FUNCTION ... RETURNS TABLE AS RETURN SELECT ...

  • 第三类(内联表值)被查询优化器本质上视为(参数化)视图,这意味着从查询中引用函数类似于复制粘贴函数的SQL主体(而不实际复制粘贴),从而带来以下好处:

    • 查询规划器可以像优化任何其他子查询一样优化内联函数的执行(例如,删除未使用的列、向下推谓词、选择不同的联接策略等)。
    • 组合多个内联函数不需要在将结果提供给下一个函数之前将第一个函数的结果具体化。

    以上内容可能导致潜在的显著性能节约,尤其是在组合多个功能级别时。

    注意:看起来SQLServer2019也将引入某种形式的标量函数内联。


    • 函数可以在select语句中使用,而as过程不能。

    • 存储过程同时接受输入和输出参数,但函数只接受输入参数。

    • 函数不能返回类型为text、ntext、image×tamp的值,而过程可以返回这些值。

    • 函数可以用作创建表中用户定义的数据类型,但过程不能。

    ***例如:创建table (name varchar(10),salary getsal(name))

    这里getsal是一个用户定义的函数,它返回一个薪金类型,当创建表时,没有为薪金类型分配存储空间,也没有执行getsal函数,但是当我们从这个表中获取一些值时,getsal函数get的执行和返回类型作为结果集返回。


    在SQL Server中,函数和存储过程是两种不同类型的实体。

    函数:在SQL Server数据库中,函数用于执行某些操作,该操作立即返回结果。函数有两种类型:

  • 系统定义

  • 用户定义的

  • 存储过程:在SQL Server中,存储过程存储在服务器中,可以返回零、单和多个值。存储过程有两种类型:

  • 系统存储过程
  • 用户定义的过程