ChatGPT解决这个技术问题 Extra ChatGPT

SQL Server 中的函数与存储过程

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

有人能告诉我为什么吗?

速度怎么样?哪一个运行相同的查询更快?
值得一提的是,SP 可以创建交易,而功能不行

w
wha7ever

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

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

评论中值得注意的一点,总结了答案。感谢@Sean K Anderson:

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


基本上不允许DML?
函数遵循计算机科学的定义,因为它们必须返回一个值,并且不能更改它们作为参数(参数)接收的数据。函数不允许改变任何东西,必须至少有一个参数,并且它们必须返回一个值。存储过程不必有参数,可以更改数据库对象,也不必返回值。
事实上,您可以在函数中包含 INSERT、UPDATE 和 DELETE 语句,用于修改本地表变量。
@Ani - 您可以在函数中实例化和修改任意数量的局部变量,但是您不能修改函数范围之外的任何内容。
@SeanKAnderson 函数“必须至少有一个参数”不正确。
C
Cfun

SP和UDF的区别如下:

存储过程 (SP) 函数(UDF - 用户定义) SP 可以返回零、单个或多个值。函数必须返回单个值(可以是标量或表)。我们可以在 SP 中使用事务。我们不能在 UDF 中使用事务。 SP 可以有输入/输出参数。仅输入参数。我们可以从 SP 调用函数。我们不能从函数中调用 SP。我们不能在 SELECT/WHERE/HAVING 语句中使用 SP。我们可以在 SELECT/WHERE/HAVING 语句中使用 UDF。我们可以使用 SP 中的 Try-Catch 块进行异常处理。我们不能在 UDF 中使用 Try-Catch 块。


函数必须返回一个值或一组。
这是 3 年后出现的,但应该是最重要的,因为它既可读又广泛。
SP 可以同时使用临时表和表变量,而 UDF 只能使用表变量。反过来,表变量可能不使用索引。与 SP 不同,UDF 可以在 CROSS APPLY 中调用
T
Tom H

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

函数通常有一个输出和可选的输入。然后可以将输出用作另一个函数(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等框架不能直接调用函数,但是可以直接调用存储过程。

函数确实有一个隐患:它们可能被滥用并导致相当讨厌的性能问题:考虑这个查询:

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。
可以通过 EXECUTE 语句调用用户定义的函数。请参阅 Microsoft 的文章 Execute User-defined Functions
a
alex

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

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 的支点不是一个好的支点。
A
Alex Martelli

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


有不同类型的用户定义函数。标量只返回值;其他类型返回结果集。
K
Kolappan N

STORE PROCEDURE FUNCTION (USER DEFINED FUNCTION) 过程可以返回 0、单个或多个值 函数只能返回单个值 过程可以有输入、输出参数 函数只能有输入参数 不能从函数调用过程 可以从过程调用函数 过程允许在其中使用 select 以及 DML 语句 函数中只允许在其中使用 select 语句 异常可以由过程中的 try-catch 块处理 Try-catch 块不能在函数中使用 我们可以在过程中进行事务管理 我们不能用于函数中的事务管理 过程不能在 select 语句中使用 函数可以嵌入到 select 语句中 过程可以影响数据库的状态,意味着它可以对数据库执行 CRUD 操作 函数不能影响数据库的状态,意味着它不能执行 CRUD对数据库的操作 程序可以使用临时表 函数不能使用临时表 程序可以改变服务器环境参数 函数不能改变环境参数 当我们想要的时候可以使用过程而不是对一组可能很复杂的 SQL 语句进行分组 当我们想要计算并返回一个值以用于其他 SQL 语句时可以使用函数


与 SP 不同,UDF 可以在 CROSS APPLY 中调用
A
Adnan Ahmed

基本区别

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

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

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

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

提前差价

过程允许在其中使用 SELECT 以及 DML(INSERT/UPDATE/DELETE) 语句,而函数只允许在其中使用 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)通过 Select 语句的 1+ 行集。如果只返回 1 行集,它可以用作“插入到”的“执行语句”参数“ 陈述。”
M
Malachi

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

SELECT a, lookupValue(b), c FROM customers 

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

无法创建永久更改

无法更改数据

存储过程可以做这些事情。

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


T
Tigerjz32

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

函数用作方法。你传给它一些东西,它会返回一个结果。应该小而快 - 即时完成。通常在 SELECT 语句中使用。


这是对两者的一个很好的总结,快速而肮脏的思考方式。
确实是一个很好的总结。其他答案集中在两者的理论差异上,同时仍然让我不确定何时在实践中使用哪一个。
A
Achilles

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 行)。性能差异大。
编辑:执行在 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!
m
mhenry1384

存储过程:

就像 SQL Server 中的一个微型程序。

可以像选择语句一样简单,也可以像从数据库中的多个表中添加、删除、更新和/或读取数据的长脚本一样复杂。

(可以实现循环和游标,它们都允许您处理较小的结果或对数据进行逐行操作。)

应使用 EXEC 或 EXECUTE 语句调用。

返回表变量,但我们不能使用 OUT 参数。

支持交易。

功能:

不能用于更新、删除或向数据库添加记录。

只需返回单个值或表值。

只能用于选择记录。但是,它可以很容易地从标准 SQL 中调用,例如:SELECT dbo.functionname('Parameter1') 或 SELECT Name, dbo.Functionname('Parameter1') FROM sysObjects

对于简单的可重用选择操作,函数可以简化代码。请注意在函数中使用 JOIN 子句。如果您的函数有一个 JOIN 子句,并且您从另一个返回多个结果的 select 语句中调用它,则该函数调用将为结果集中返回的每一行将这些表连接在一起。因此,尽管它们有助于简化某些逻辑,但如果使用不当,它们也可能成为性能瓶颈。

使用 OUT 参数返回值。

不支持事务。


A
Arnkrishn

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

存储过程不能返回一个表变量,而 as 函数可以做到这一点。您可以使用存储过程来更改服务器环境参数,而使用您不能使用的功能。

干杯


A
Andrew

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

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

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

此外,函数在 SQL Server 中也是一个好主意。它们速度更快,并且可以非常强大。内联和直接选择。注意不要过度使用。


u
user2023861

这是更喜欢函数而不是存储过程的实际原因。如果您有一个存储过程需要另一个存储过程的结果,则必须使用 insert-exec 语句。这意味着您必须创建一个临时表并使用 exec 语句将存储过程的结果插入到临时表中。很乱。一个问题是insert-execs cannot be nested

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

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


B
Branko Dimitrijevic

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

函数可以...

标量:CREATE FUNCTION ... RETURNS scalar_type AS BEGIN ... END 多语句表值:CREATE FUNCTION ... RETURNS @r TABLE(...) AS BEGIN ... END 内联表值:CREATE FUNCTION。 .. 返回表作为返回选择 ...

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

查询计划器可以像优化任何其他子查询一样优化内联函数的执行(例如,消除未使用的列、下推谓词、选择不同的 JOIN 策略等)。

组合几个内联函数不需要在将第一个函数的结果提供给下一个函数之前将其具体化。

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

注意:看起来 SQL Server 2019 也将引入某种形式的 scalar function inlining


B
Brad Larson

函数必须返回一个值,而不是存储过程。

Select 语句仅在 UDF 中接受,而 DML 语句不需要。

存储过程接受任何语句以及 DML 语句。

UDF 只允许输入,不允许输出。

存储过程允许输入和输出。

Catch 块不能在 UDF 中使用,但可以在存储过程中使用。

UDF 中的函数中不允许有事务,但在存储过程中是允许的。

在 UDF 中只能使用表变量,而不能在临时表中使用。

存储过程允许表变量和临时表。

UDF 不允许从函数调用存储过程,而存储过程允许调用函数。

UDF 用于连接子句,而存储过程不能用于连接子句。

存储过程将始终允许归零。相反,UDF 具有必须返回到预定点的值。


B
Brian J

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

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

函数不能像过程一样返回文本、ntext、图像和时间戳类型的值。

函数可以在 create table 中用作用户定义的数据类型,但过程不能。

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

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


4
4 revs

通常使用存储过程对性能更好。例如,在以前版本的 SQL Server 中,如果将函数置于 JOIN 条件下,基数估计为 1(SQL 2012 之前)和 100(SQL 2012 之后和 SQL 2017 之前),并且引擎可能会生成错误的执行计划。

此外,如果将其放在 WHERE 子句中,SQL 引擎可能会生成错误的执行计划。

在 SQL 2017 中,Microsoft 引入了称为交错执行的功能,以产生更准确的估计,但存储过程仍然是最佳解决方案。

有关更多详细信息,请参阅 Joe Sack https://techcommunity.microsoft.com/t5/sql-server/introducing-interleaved-execution-for-multi-statement-table/ba-p/385417 的以下文章


J
Jason Clark

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

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

系统定义 用户定义

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

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