SQL Server中的功能与存储过程


829

我学习函数和存储过程已经有一段时间了,但是我不知道为什么以及何时应该使用函数或存储过程。在我看来,它们看起来一样,也许是因为我对此有些新手。

有人可以告诉我为什么吗?





3
速度如何?哪一个运行相同的查询更快?
AmiNadimi

Answers:


708

函数是计算值,不能对它们执行永久性的环境更改SQL Server(即,不执行INSERTUPDATE不允许执行任何声明)。

如果函数SQL返回标量值,则可以在语句中内联使用;如果返回结果集,则可以在函数中加入函数。

注释总结了答案,这一点值得一提。感谢@Sean K Anderson:

函数遵循计算机科学的定义,因为它们必须返回一个值,并且不能更改作为参数(自变量)接收的数据。函数不允许更改任何内容,必须至少具有一个参数,并且它们必须返回一个值。存储的proc不必具有参数,可以更改数据库对象,也不必返回值。

如何SQL从存储过程调用 函数以及何时使用函数而不是存储过程。

嗨,朋友们,今天我们将讨论何时使用存储过程以及何时使用函数。在简单团队中如果要计算一些值,它将返回一个值,因此不是必需的:

https://programmingtechtutorial.blogspot.com/2020/01/when-use-storeprocedure-and-when-use.html


13
基本上不允许DML吗?
david blaine

173
函数遵循计算机科学的定义,因为它们必须返回一个值,并且不能更改作为参数(自变量)接收的数据。函数不允许更改任何内容,必须至少具有一个参数,并且它们必须返回一个值。存储的proc不必具有参数,可以更改数据库对象,也不必返回值。
肖恩·安德森

23
实际上,您可以在函数中具有INSERT,UPDATE和DELETE语句,用于修改局部表变量。
2014年

14
@Ani-您可以在函数中实例化和修改任意数量的局部变量,但是不能修改函数范围以外的任何内容。
MyItchyChin 2014年

40
@SeanKAnderson函数“必须至少具有一个参数”不正确。

623

SP和UDF之间的区别如下:

+---------------------------------+----------------------------------------+
| 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.    |                                        |
+---------------------------------+----------------------------------------+

21
函数必须返回一个值或一组值。
拉法雷诺2015年

8
这是3年后才出现的,但应该是最重要的,因为它既可读又广泛。
DanteTheSmith

SP可能同时使用临时表和表变量,而UDF可能仅使用表变量。表变量又可能不使用索引。UDF可以像SP一样在CROSS APPLY中调用
Ludovic Aubert

190

函数和存储过程用于不同的目的。尽管这不是最好的类比,但函数可以从字面上看成是您在任何编程语言中都会使用的任何其他函数,但是存储的procs更像是单个程序或批处理脚本。

函数通常具有输出和可选的输入。然后可以将输出用作另一个函数(SQL Server内置的DATEDIFF,LEN等)的输入,或用作SQL Query的谓词-例如SELECT a, b, dbo.MyFunction(c) FROM tableSELECT a, b, c FROM table WHERE a = dbo.MyFunc(c)

存储的proc用于在事务中将SQL查询绑定在一起,并与外界交互。诸如ADO.NET等框架无法直接调用函数,但是它们可以直接调用存储的proc。

但是,函数的确存在隐患:它们可能会被滥用并导致相当讨厌的性能问题:请考虑以下查询:

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

其中MyFunction声明为:

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返回的每一行调用该函数。

因此,您确实需要谨慎编写函数。如果您从函数中的表中执行SELECT,则需要问自己:使用父存储过程中的JOIN还是其他一些SQL构造(例如CASE ... WHEN ... ELSE ...结束)。


2
您能否详细说明“ ADO.NET等框架无法直接调用函数”?我已经使用ADO.NET数据提供程序执行了功能,没有任何问题。
伊恩·肯普

24
您必须通过一些SELECT语句调用函数-函数本身不能被称为独立的代码段-必须将其作为某些较大的SQL语句的一部分来调用,即使该SQL语句仅此而已比SELECT * from dbo.MyTableValuedFunction()。另一方面,通过将设置SqlCommand.CommandType为,可以使用ADO.NET直接调用Sproc CommandType.StoredProcedure
克里斯J

60

存储过程和用户​​定义函数之间的区别:

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

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

1
在下面引用@curiousBoy。另一个未经信用的答案(通过@Ankit)(<-看看我是怎么做的;;)):“您应该提供了源参考。这来自(blogs.msdn.microsoft.com/pradeepsvs/2014/10 / 08 /…)。请尊重他人所做的工作!”
汤姆(Tom),

7
该博客自2014年10月8日以来撰写,此答案自2013年5月2日以来@Tom
Kumar Manish

1
@Code Rider:啊,我很抱歉!不敢相信我没有注意到!那么,博客在没有信誉的情况下复制了您(或其他这样做的人)?
汤姆(Tom)

GETDATE()可以在功能中使用。在枢不确定性是不是一个好。
PerformanceDBA

56

当您要计算并返回供其他SQL语句使用的值时,编写一个用户定义的函数;当您想要对一组可能很复杂的SQL语句进行分组时,编写一个存储过程。毕竟,这是两个截然不同的用例!


18
用户定义函数有不同类型。标量值仅返回值;其他类型重新运行结果集。
AK

44
              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.

1
可以在CROSS APPLY中调用UDF,与SP不同
Ludovic Aubert

24

基本差异

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

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

函数采用一个输入参数,这是强制性的,但存储过程可能采用o到n个输入参数。

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

提前差异

过程允许SELECT以及DML(INSERT / UPDATE / DELETE)语句,而Function则仅允许SELECT语句。

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

在WHERE / HAVING / SELECT部分​​的任何地方都不能在SQL语句中使用存储过程,而可以使用Function。

返回表的函数可以视为另一个行集。可以在与其他表的JOIN中使用。

内联函数可以作为带有参数的视图,并且可以在JOIN和其他Rowset操作中使用。

异常可以由Procedure中的try-catch块处理,而try-catch块不能在Function中使用。

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

资源


25
您应该已经提供了源参考。这是从dotnet-tricks.com/Tutorial/sqlserver/…中获得的。请尊重他人所做的工作!
curiousBoy 2014年

16
没有理由不提供参考不是没有理由的。您可以在结尾处提及!
curiousBoy 2014年

2
回覆。“功能必须返回一个值,但在存储过程是可选的....”:我将澄清的是:“功能必须返回一个且仅一个值(这必须通过进行Returns关键字和必须是一个标量或表型) ,但存储过程可以有选择地返回:a)1 Int种通过Return语句的结果代码和/或b)1+ Cursor通过Output关键字的参数(包括类型)和/或c)1+通过Select语句的行集(如果只有1个行集)返回,它可用作“插入到”语句的“ execute_statement”参数。”
汤姆(Tom),

20

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

SELECT a, lookupValue(b), c FROM customers 

lookupValueUDF将在哪里。使用存储过程时,这种功能是不可能的。同时,您不能在UDF中执行某些操作。这里要记住的基本内容是UDF:

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

存储过程可以完成那些事情。

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


14

存储过程 用作脚本。它们为您运行一系列命令,您可以安排它们在特定时间运行。通常运行多个DML语句,例如INSERT,UPDATE,DELETE等,甚至执行SELECT。

函数 用作方法。您将其传递给它,它将返回结果。应该小而快速-即时进行。通常在SELECT语句中使用。


2
这是对两种思考方式的快速总结和总结。
埃里克·比斯哈德

2
确实是一个很好的总结。其他答案侧重于两者的理论差异,但仍使我不确定何时实际使用哪个。
jf328 '16

8

存储过程:

  • 就像SQL Server中的微型程序。
  • 可以像选择语句一样简单,也可以像从数据库中多个表中添加,删除,更新和/或读取数据的长脚本一样复杂。
  • (可以实现循环和游标,它们都允许您使用较小的结果或对数据进行逐行操作。)
  • 应该使用EXECEXECUTE语句来调用。
  • 返回表变量,但不能使用OUT参数。
  • 支持交易。

功能:

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

    SELECT dbo.functionname('Parameter1')

    要么

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

  • 使用OUT参数返回值。
  • 不支持交易。

8

用户定义的功能。

  1. 函数必须返回一个值。
  2. 将仅允许Select语句,不允许我们使用DML语句。
  3. 它将仅允许输入参数,不支持输出参数。
  4. 它将不允许我们使用try-catch块。
  5. 函数内不允许进行事务处理。
  6. 我们只能使用表变量,而不允许使用临时表。
  7. 无法从函数中调用存储过程。
  8. 可以从select语句中调用函数。
  9. UDF可以在join子句中用作结果集。

储存程序

  1. 存储过程可能会或不会返回值。
  2. 可以具有select语句以及DML语句,例如insert,update,delete等
  3. 它可以同时具有输入和输出参数。
  4. 对于异常处理,我们可以使用try catch块。
  5. 可以在存储过程中使用事务。
  6. 可以同时使用表变量和临时表。
  7. 存储过程可以调用函数。
  8. 无法从Select / Where / Having等语句中调用过程。Execute / Exec语句可用于调用/执行存储过程。
  9. 不能在Join子句中使用过程

6

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

  1. 存储过程无法返回表变量,而函数可以做到这一点。

  2. 您可以使用存储过程来更改服务器环境参数,而不能使用函数。

干杯


6

SQL Server函数(例如游标)将用作最后的武器!它们确实存在性能问题,因此应尽可能避免使用表值函数。谈论性能是指一个表,该表在中型硬件的服务器上托管着超过1,000,000条记录。否则,您不必担心这些功能导致的性能下降。

  1. 切勿使用函数将结果集返回到外部代码(如ADO.Net)
  2. 尽可能使用视图/存储的proc组合。您有时可以使用DTA(数据库调整顾问)提供的建议(例如索引视图和统计信息)从将来的增长性能问题中恢复过来-有时!

有关更多参考,请参见:http : //databases.aspfaq.com/database/should-i-use-a-view-a-stored-procedure-or-a-user-defined-function.html


1
谢谢。今天编写了一个函数以在查询中调用以填充一列的值。在我停止执行之前,执行运行了3分钟以上。找出一种JOIN方式来做到这一点。执行在15秒内完成。(数据集为3456行)。性能差异大。
VISQL 2012年

编辑:执行完成15到50秒之间,具体取决于我“ ORDER BY”(数据集为3456行)的列。性能差异很大。
VISQL 2012年

性能差异可能源于排序结果所依据的那些列的不同类型。SQL Server使用数字比使用字符数据要好得多。您可以在50秒的查询中使用DTA,看看它是否可以提供某种统计/索引建议以使查询运行更快。
阿喀琉斯(Achilles)

1
我不确定是否已提供足够的证据说这应该是最后的手段。您可以将函数视为可以进一步操作的参数化视图。例如,您想加入客户的订单,但仅适用于密歇根州。您创建一个customerOrders(@StateCode)函数,该函数将仅加入单个州的客户价值。然后,我可以对该集合进行进一步操作,例如从CustomerOrders('MI')中选择FirstName,LastName,OrderTotal,StoreName。INNER JOIN在Stores.StoreID = Orders.StoreID上存储,其中OrderTotal> 100; 对于SP来说,这很麻烦,因为您必须临时复制。
MPavlak 2012年

您在该表中有多少记录?如果您的硬件能够正确处理,则无需担心选择武器。当一把勺子很难折断时,汤匙就可以完成这项工作。这种硬度叫做硬件!
阿基里斯(Achilles)

3

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

然后,您可以将函数用于城市的参数化列表。dbo.GetCitiesIn(“ NY”)返回一个可用作联接的表。

这是组织代码的一种方式。只有通过反复试验和经验,才能知道何时可以重用,何时可以浪费时间。

另外,在SQL Server中,函数是一个好主意。它们速度更快,并且功能非常强大。内联和直接选择。小心不要过度使用。


3

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

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

这是我们应该将业务逻辑保留在数据库之外的另一个原因


2
  • Function必须返回一个值,而对于存储过程则必须返回一个值。
  • 选择仅UDF接受的语句,而不需要DML语句。
  • 存储过程接受任何语句以及DML语句。
  • UDF仅允许输入,而不允许输出。
  • 存储过程允许输入和输出。
  • 捕获块不能在UDF中使用,但可以在存储过程中使用。
  • UDF中的函数中不允许任何事务,但是在存储过程中是允许的。
  • UDF中只能使用表变量,而临时表中不能使用。
  • 存储过程允许使用表变量和临时表。
  • UDF不允许从函数中调用存储过程,而存储过程则允许调用函数。
  • 在join子句中使用UDF,而在join子句中不能使用存储过程。
  • 存储过程将始终允许返回零。相反,UDF具有必须返回的值-返回到预定点。

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

  • 存储过程同时接受输入和输出参数,而功能仅接受输入参数。

  • 函数无法在过程可以的地方返回text,ntext,image和timestamps类型的值。

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

***例如:-建立 table <tablename>(name varchar(10),salary getsal(name))

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


1

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

功能可以是...

  1. 标量:

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

  2. 多语句表值:

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

  3. 内联表值:

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

查询优化器将第三种类型(内联表值)本质上视为(参数化的)视图,这意味着从查询中引用该函数类似于复制粘贴该函数的SQL主体(实际上没有复制粘贴),导致有以下好处:

  • 查询计划程序可以像其他子查询一样优化内联函数的执行(例如,消除未使用的列,向下推谓词,选择不同的JOIN策略等)。
  • 组合多个内联函数不需要将第一个结果具体化,然后再将其提供给下一个。

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


注意:SQL Server 2019似乎也会引入某种形式的标量函数内联


-2

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

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

  1. 系统定义

  2. 用户自定义

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

  1. 系统存储过程
  2. 用户定义的过程
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.