SQL Server 中的函数与存储过程
Posted
技术标签:
【中文标题】SQL Server 中的函数与存储过程【英文标题】:Function vs. Stored Procedure in SQL Server 【发布时间】:2009-07-24 19:40:06 【问题描述】:我学习函数和存储过程已经有一段时间了,但我不知道为什么以及何时应该使用函数或存储过程。它们对我来说看起来一样,也许是因为我对此有点陌生。
谁能告诉我为什么?
【问题讨论】:
venkatsqlinterview.blogspot.com/2011/05/… wiki.answers.com/Q/… searchsqlserver.techtarget.com/tip/… 速度怎么样?哪一个运行相同的查询更快? 值得一提的是SP可以创建交易而功能不行 【参考方案1】:函数是计算值,不能对SQL Server
执行永久性环境更改(即不允许使用INSERT
或UPDATE
语句)。
如果函数返回标量值,则可以在 SQL
语句中内联使用,如果返回结果集,则可以加入函数。
cmets 中值得注意的一点,它总结了答案。感谢@Sean K Anderson:
函数遵循计算机科学定义,因为它们必须返回一个值,并且不能更改它们作为参数接收的数据 (论点)。函数不允许改变任何东西,必须 至少有一个参数,并且它们必须返回一个值。已存储 procs不必有参数,可以改变数据库对象, 并且不必返回值。
【讨论】:
基本上不允许DML? 函数遵循计算机科学的定义,因为它们必须返回一个值,并且不能更改它们作为参数(参数)接收的数据。函数不允许改变任何东西,必须至少有一个参数,并且它们必须返回一个值。存储过程不必有参数,可以改变数据库对象,也不必返回值。 事实上你可以在一个函数中包含 INSERT、UPDATE 和 DELETE 语句,用于修改本地表变量。 @Ani - 您可以在函数中实例化和修改任意数量的局部变量,但是您不能修改函数范围之外的任何内容。 @SeanKAnderson 函数“必须至少有一个参数”不正确。【参考方案2】:SP和UDF的区别如下:
Stored Procedure (SP) | Function (UDF - User Defined) |
---|---|
SP can return zero, single or multiple values. | Function must return a single value (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 parameter. | Only input parameter. |
We can call function from SP. | We can't call SP from function. |
We can't use SP in SELECT/ WHERE/ HAVING statement. | We can use UDF in SELECT/ WHERE/ HAVING statement. |
We can use exception handling using Try-Catch block in SP. | We can't use Try-Catch block in UDF. |
【讨论】:
函数必须返回一个值或一个集合。 这是 3 年后出现的,但应该是最重要的,因为它既可读又广泛。 SP 可以同时使用临时表和表变量,而 UDF 只能使用表变量。反过来,表变量可能不使用索引。与 SP 不同,UDF 可以在 CROSS APPLY 中调用【参考方案3】:函数和存储过程有不同的用途。虽然这不是最好的类比,但函数可以从字面上视为您在任何编程语言中使用的任何其他函数,但存储过程更像是单个程序或批处理脚本。
函数通常有一个输出和可选的输入。然后可以将输出用作另一个函数(SQL Server 内置,如 DATEDIFF、LEN 等)的输入,或用作 SQL 查询的谓词 - 例如,SELECT a, b, dbo.MyFunction(c) FROM table
或 SELECT a, b, c FROM table WHERE a = dbo.MyFunc(c)
。
存储过程用于在事务中将 SQL 查询绑定在一起,并与外部世界交互。 ADO.NET等框架不能直接调用函数,但可以直接调用存储过程。
函数确实有一个隐患:它们可能被滥用并导致相当严重的性能问题:考虑这个查询:
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
这里发生的是函数 MyFunction 为表 MyTable 中的每一行调用。如果 MyTable 有 1000 行,那么这就是针对数据库的另外 1000 个即席查询。同样,如果在列规范中指定时调用该函数,则将为 SELECT 返回的每一行调用该函数。
所以你确实需要小心编写函数。如果您在函数中从表中执行 SELECT,您需要问自己是否可以使用父存储过程中的 JOIN 或其他一些 SQL 构造(例如 CASE ... WHEN ... ELSE ...结束)。
【讨论】:
能否详细说明“ADO.NET等框架不能直接调用函数”?我已经使用 ADO.NET 数据提供程序执行了功能,没有任何问题。 你必须通过一些 SELECT 语句来调用一个函数——一个函数不能作为一个独立的代码段来调用——它必须作为一些更大的 SQL 语句的一部分来调用,即使那个 SQL 语句只不过是SELECT * from dbo.MyTableValuedFunction()
。另一方面,可以通过将 SqlCommand.CommandType
设置为 CommandType.StoredProcedure
直接使用 ADO.NET 调用 Sprocs。【参考方案4】:
存储过程和用户定义函数的区别:
不能在 Select 语句中使用存储过程。 存储过程支持延迟名称解析。 存储过程通常用于执行业务逻辑。 存储过程可以返回任何数据类型。 与用户定义的函数相比,存储过程可以接受更多数量的输入参数。存储过程最多可以有 21,000 个输入参数。 存储过程可以执行动态 SQL。 存储过程支持错误处理。 非确定性函数可用于存储过程。用户定义的函数可以在 Select 语句中使用。 用户定义的函数不支持延迟名称解析。 用户定义的函数通常用于计算。 用户定义的函数应该返回一个值。 用户定义的函数不能返回图像。 用户定义的函数接受的输入参数数量少于存储过程。 UDF 最多可以有 1,023 个输入参数。 临时表不能用于用户定义的函数。 用户定义的函数无法执行动态 SQL。 用户定义的函数不支持错误处理。 UDF 中不允许使用
RAISEERROR
或 @@ERROR
。
非确定性函数不能在 UDF 中使用。例如,GETDATE()
不能用于 UDF。
【讨论】:
在下面引用@curiousBoy。另一个不可信的答案(@Ankit)(blogs.msdn.microsoft.com/pradeepsvs/2014/10/08/…)。请尊重其他人所做的工作! " 这个博客是从 2014 年 10 月 8 日开始写的,这个答案是从 2013 年 5 月 2 日开始写的 @Tom @Code Rider:啊,对不起!不敢相信我没有注意到!那么,该博客在没有信用的情况下抄袭了您(或抄袭的其他人)?GETDATE()
可以在函数中使用。 Non-deterministic 的支点不是一个好的支点。【参考方案5】:
当你想计算并返回一个值以供其他SQL语句使用时,编写一个用户定义的函数;当您需要时编写存储过程是对一组可能很复杂的 SQL 语句进行分组。毕竟,这是两个截然不同的用例!
【讨论】:
有不同类型的用户定义函数。标量只返回值;其他类型返回结果集。【参考方案6】:STORE PROCEDURE | FUNCTION (USER DEFINED FUNCTION) |
---|---|
Procedure can return 0, single or multiple values | Function can return only single value |
Procedure can have input, output parameters | Function can have only input parameters |
Procedure cannot be called from a function | Functions can be called from procedure |
Procedure allows select as well as DML statement in it | Function allows only select statement in it |
Exception can be handled by try-catch block in a procedure | Try-catch block cannot be used in a function |
We can go for transaction management in procedure | We can not go for transaction management in function |
Procedure cannot be utilized in a select statement | Function can be embedded in a select statement |
Procedure can affect the state of database means it can perform CRUD operation on database | Function can not affect the state of database means it can not perform CRUD operation on database |
Procedure can use temporary tables | Function can not use temporary tables |
Procedure can alter the server environment parameters | Function can not alter the environment parameters |
Procedure can use when we want instead is to group a possibly- complex set of SQL statements | Function can use when we want to compute and return a value for use in other SQL statements |
【讨论】:
UDF 可以在 CROSS APPLY 中调用,不像 SP【参考方案7】:基本区别
函数必须返回一个值,但在存储过程中它是可选的(过程可以返回零或 n 个值)。
函数只能有输入参数,而过程可以有输入/输出参数。
函数需要一个输入参数,这是强制性的,但存储过程可能需要 o 到 n 个输入参数..
函数可以从过程中调用,而过程不能从函数中调用。
提前差
Procedure 允许在其中使用 SELECT 以及 DML(INSERT/UPDATE/DELETE) 语句,而 Function 只允许在其中使用 SELECT 语句。
过程不能在 SELECT 语句中使用,而函数可以嵌入在 SELECT 语句中。
不能在 WHERE/HAVING/SELECT 部分的 SQL 语句中使用存储过程,而函数可以。
返回表的函数可以被视为另一个行集。这可以在与其他表的 JOIN 中使用。
内联函数可以看作是接受参数的视图,并且可以用于 JOIN 和其他 Rowset 操作。
异常可以由过程中的try-catch块处理,而try-catch块不能在函数中使用。
我们可以在过程中进行事务管理,而不能在函数中进行。
source
【讨论】:
你应该已经给出了来源参考。这是来自dotnet-tricks.com/Tutorial/sqlserver/…。请尊重他人所做的工作! 这不是不提供来源参考的理由。可以在文末提及! 回复。 “函数必须返回一个值,但在存储过程中它是可选的......”:我要澄清的是:“函数必须返回一个且只有一个值(必须通过Returns
关键字并且必须是标量或表类型),但存储过程可以可选地返回:a) 1 Int
通过 Return
语句和/或 b) 1+ 参数(包括.Cursor
类型)通过Output
关键字和/或 c)1+ 行集通过Select
语句。如果只返回 1 行集,它可以用作“插入”的“执行语句”参数“声明。”【参考方案8】:
用户定义函数是 sql server 程序员可用的重要工具。您可以像这样在 SQL 语句中内联使用它
SELECT a, lookupValue(b), c FROM customers
lookupValue
将是一个 UDF。使用存储过程时,这种功能是不可能的。同时你不能在 UDF 中做某些事情。这里要记住的基本内容是 UDF:
存储过程可以做这些事情。
对我来说,UDF 的内联用法是 UDF 最重要的用法。
【讨论】:
【参考方案9】:存储过程 用作脚本。它们为您运行一系列命令,您可以安排它们在特定时间运行。通常运行多个 DML 语句,如 INSERT、UPDATE、DELETE 等,甚至 SELECT。
函数 被用作方法。你传递给它一些东西,它会返回一个结果。应该小而快 - 即时完成。通常用于 SELECT 语句中。
【讨论】:
这是对两者的一个很好的总结,快速而肮脏的思考方式。 确实是一个很好的总结。其他答案集中在两者的理论差异上,同时仍然让我不确定何时在实践中使用哪一个。【参考方案10】:SQL Server 函数(如游标)旨在用作您的最后武器!它们确实存在性能问题,因此应尽可能避免使用表值函数。谈论性能就是谈论在中级硬件的服务器上托管的具有超过 1,000,000 条记录的表;否则你不必担心函数造成的性能损失。
-
切勿使用函数将结果集返回给外部代码(如 ADO.Net)
尽可能使用视图/存储过程组合。您可以使用 DTA(数据库调优顾问)给您的建议(例如索引视图和统计数据)从未来的增长性能问题中恢复过来——有时!
更多参考请见:http://databases.aspfaq.com/database/should-i-use-a-view-a-stored-procedure-or-a-user-defined-function.html
【讨论】:
谢谢。今天编写了一个函数来在查询中调用以填充一列的值。 Execute 在我停止之前运行了 3 多分钟。想出了一个 JOIN 方法来做到这一点。执行在 15 秒内完成。 (数据集为 3456 行)。性能差异很大。 edit:执行在 15 到 50 秒之间完成,具体取决于我“ORDER BY”的列(数据集为 3456 行)。巨大的性能差异。 性能差异可能源于您排序结果所依据的那些列的不同类型。 SQL Server 处理数字比处理字符数据要好得多。您可以对该 50 秒查询使用 DTA,看看它是否可以提出某种统计/索引建议,以使查询运行得更快。 我不确定是否有足够的证据表明它应该是最后的手段。您可以将函数视为可以进一步操作的参数化视图。例如,您想将客户加入到订单中,但仅限于密歇根州。您创建一个 customerOrders(@StateCode) 函数,该函数将只加入一个州的客户价值。然后,我可以在这个集合上进一步操作 Select FirstName, LastName, OrderTotal, StoreName From CustomerOrders('MI') INNER JOIN Stores ON Stores.StoreID = Orders.StoreID WHERE OrderTotal > 100;这对 SP 来说会很痛苦,因为您必须临时复制。 该表中有多少条记录?如果您的硬件处理得当,您将无需担心选择武器。当很难折断剑时,勺子就可以完成这项工作;这种硬度叫做HARDWARE!【参考方案11】:存储过程:
就像是 SQL Server 中的一个微型程序。 可以像 select 语句一样简单,也可以像 long 语句一样复杂 添加、删除、更新和/或读取多个数据的脚本 数据库中的表。 (可以实现循环和游标,它们都允许您使用 较小的结果或对数据的逐行操作。) 应使用EXEC
或EXECUTE
语句调用。
返回表变量,但我们不能使用OUT
参数。
支持事务。
功能:
不能用于更新、删除或向数据库添加记录。 仅返回单个值或表值。只能用于选择记录。但是,它可以称为 在标准 SQL 中非常容易,例如:
SELECT dbo.functionname('Parameter1')
或
SELECT Name, dbo.Functionname('Parameter1') FROM sysObjects
对于简单的可重用选择操作,函数可以简化代码。
请注意在您的函数中使用 JOIN
子句。如果你的
函数有一个JOIN
子句,你从另一个选择中调用它
返回多个结果的语句,该函数调用将JOIN
结果集中返回的每行行的这些表。所以
尽管它们有助于简化某些逻辑,但它们也可以是
如果使用不当,则会出现性能瓶颈。
OUT
参数返回值。
不支持事务。
【讨论】:
【参考方案12】:用户定义函数。
-
函数必须返回一个值。
只允许 Select 语句,不允许我们使用 DML 语句。
它将只允许输入参数,不支持输出参数。
它不允许我们使用 try-catch 块。
函数内不允许事务。
我们只能使用表变量,它不允许使用临时表。
无法从函数调用存储过程。
可以从 select 语句中调用函数。
可以在连接子句中使用 UDF 作为结果集。
存储过程
-
存储过程可能会或不会返回值。
可以有select语句以及插入、更新、删除等DML语句
它可以有输入和输出参数。
对于异常处理,我们可以使用 try catch 块。
可以在存储过程中使用事务。
既可以使用表变量,也可以使用其中的临时表。
存储过程可以调用函数。
不能从 Select/Where/Having 等语句调用过程。 Execute/Exec语句可用于调用/执行存储过程。
过程不能在 Join 子句中使用
【讨论】:
【参考方案13】:决定何时使用以下几点可能会有所帮助-
存储过程无法返回表变量,而 as 函数可以做到这一点。
您可以使用存储过程来更改服务器环境参数,而使用函数则不能。
干杯
【讨论】:
【参考方案14】:从返回单个值的函数开始。好处是您可以将常用代码放入函数中,并将它们作为结果集中的列返回。
然后,您可以将函数用于城市的参数化列表。 dbo.GetCitiesIn("NY") 返回一个可以用作连接的表。
这是一种组织代码的方式。只有通过反复试验和经验才能知道什么时候可以重复使用,什么时候浪费时间。
此外,函数在 SQL Server 中也是一个好主意。它们速度更快,并且可以非常强大。内联和直接选择。注意不要过度使用。
【讨论】:
【参考方案15】:这里有一个实际的理由是更喜欢函数而不是存储过程。如果您有一个存储过程需要另一个存储过程的结果,则必须使用 insert-exec 语句。这意味着您必须创建一个临时表并使用exec
语句将存储过程的结果插入到临时表中。很乱。一个问题是insert-execs cannot be nested。
如果您遇到调用其他存储过程的存储过程,您可能会遇到这种情况。如果嵌套存储过程只是返回一个数据集,则可以将其替换为表值函数,您将不再出现此错误。
(这是我们应该将业务逻辑排除在数据库之外的另一个原因)
【讨论】:
【参考方案16】:我意识到这是一个非常古老的问题,但我没有看到任何答案中提到的一个关键方面:内联查询计划。
函数可以...
标量:
CREATE FUNCTION ... RETURNS scalar_type AS BEGIN ... END
多语句表值:
CREATE FUNCTION ... RETURNS @r TABLE(...) AS BEGIN ... END
内联表值:
CREATE FUNCTION ... RETURNS TABLE AS RETURN SELECT ...
查询优化器将第三种(内联表值)本质上视为(参数化)视图,这意味着从查询中引用函数类似于复制粘贴函数的 SQL 主体(实际上没有复制粘贴),带来以下好处:
查询规划器可以像优化任何其他子查询一样优化内联函数的执行(例如,消除未使用的列、下推谓词、选择不同的 JOIN 策略等)。 组合多个内联函数不需要在将第一个函数的结果提供给下一个函数之前将其具体化。上述情况可能会显着降低性能,尤其是在组合多个级别的功能时。
注意:SQL Server 2019 似乎也将引入某种形式的 scalar function inlining。
【讨论】:
【参考方案17】:Mssql 存储过程与函数:
【讨论】:
【参考方案18】: Function 必须返回一个值,而不是存储过程。 Select 语句仅在 UDF 中接受,而 DML 语句不需要。 存储过程接受任何语句以及 DML 语句。 UDF 只允许输入,不允许输出。 存储过程允许输入和输出。 Catch 块不能在 UDF 中使用,但可以在存储过程中使用。 UDF 中的函数中不允许有事务,但在存储过程中则允许。 在 UDF 中只能使用表变量,而不能在临时表中使用。 存储过程允许表变量和临时表。 UDF 不允许从函数调用存储过程,而存储过程允许调用函数。 连接子句使用UDF,连接子句不能使用存储过程。 存储过程将始终允许归零。相反,UDF 具有必须返回到预定点的值。【讨论】:
【参考方案19】:函数可以在选择语句中使用,而 as 过程不能。
存储过程接受输入和输出参数,但函数只接受输入参数。
函数不能返回文本、ntext、图像和时间戳类型的值,而过程可以。
函数可以用作创建表中的用户定义数据类型,但过程不能。
***例如:-创建table <tablename>(name varchar(10),salary getsal(name))
这里的getsal是一个用户定义的函数,它返回一个salary类型,当创建表时没有为salary类型分配存储空间,getsal函数也没有执行,但是当我们从这个表中获取一些值时,getsal函数get的执行并返回 类型作为结果集返回。
【讨论】:
【参考方案20】:通常使用存储过程对性能更好。 例如,在以前版本的 SQL Server 中,如果将函数置于 JOIN 条件下,基数估计为 1(SQL 2012 之前)和 100(SQL 2012 之后和 SQL 2017 之前),引擎可能会生成错误的执行计划。
如果你把它放在 WHERE 子句中,SQL 引擎可能会生成一个错误的执行计划。
Microsoft 在 SQL 2017 中引入了称为交错执行的功能,以便产生更准确的估计,但存储过程仍然是最佳解决方案。
有关更多详细信息,请参阅 Joe Sack 的以下文章 https://techcommunity.microsoft.com/t5/sql-server/introducing-interleaved-execution-for-multi-statement-table/ba-p/385417
【讨论】:
【参考方案21】:在 SQL Server 中,函数和存储过程是两种不同类型的实体。
函数:在SQL Server数据库中,函数用于执行一些动作,动作立即返回结果。 函数有两种:
系统定义
用户定义
存储过程: 在 SQL Server 中,存储过程存储在服务器中,可以返回零值、单个值和多个值。 存储过程有两种:
-
系统存储过程
用户定义的程序
【讨论】:
以上是关于SQL Server 中的函数与存储过程的主要内容,如果未能解决你的问题,请参考以下文章
为啥我们不能在 SQL Server 中的函数内执行存储过程