【问题标题】:Execute Stored Procedure from a Function从函数执行存储过程
【发布时间】:2011-09-14 18:01:36
【问题描述】:

我知道这已经被问死了,我也知道为什么 SQL Server 不允许你这样做。

但是除了使用扩展存储过程之外,还有其他解决方法吗?

并且请不要告诉我将我的函数转换为过程...

所以我真正要问的是:有没有办法从函数中运行存储过程?

编辑:

事实证明:有办法绕过它,但它太错误我不会这样做。 我要把它改成存储过程并在别处执行。

【问题讨论】:

  • +1 表示反对这样做的一般建议可能会警告其他正在考虑这样做的人。
  • @Smur:我在下面的回答中描述了另一种方式,虽然在多行上下文中执行时仍然可能存在问题,但比使用 xp_cmdshell 调用“错误”要少得多osql(实际上应该是 SQLCMD ;-)。

标签: sql sql-server sql-server-2005 stored-procedures user-defined-functions


【解决方案1】:

我已经找到了解决这个问题的办法。我们可以在存储过程中使用“渲染”的 sql 构建函数或视图,然后可以正常执行。

1.创建另一个sproc

CREATE PROCEDURE [dbo].[usp_FunctionBuilder]
DECLARE @outerSql VARCHAR(MAX)
DECLARE @innerSql VARCHAR(MAX)

2.构建你想在你的函数中执行的动态sql(例如:你可以使用循环和联合,你可以读入另一个sproc,使用if语句和条件sql参数等)

SET @innerSql = 'your sql'

3.将@innerSql 包装在create function 语句中,并定义您在@innerSql 中使用的任何外部参数,以便将它们传递到生成的函数中。

SET @outerSql = 'CREATE FUNCTION [dbo].[fn_GeneratedFunction] ( @Param varchar(10))
RETURNS TABLE
AS
RETURN
' + @innerSql;


EXEC(@outerSql)

这只是伪代码,但解决方案解决了许多问题,例如链接服务器限制、参数、函数中的动态 sql、动态服务器/数据库/表名、循环等。

您需要根据需要对其进行调整,(例如:更改函数中的返回值)

【讨论】:

    【解决方案2】:

    除了使用 OPENQUERY 和 xp_cmdshell 之外,另一个选择是使用 SQLCLR(SQL Server 的“CLR 集成”功能)。 SQLCLR 选项不仅比其他两种方法更安全,而且还有一个潜在的好处是能够在当前会话中调用存储过程,这样它就可以访问任何会话-基于对象或设置,例如:

    • 临时表
    • 临时存储过程
    • CONTEXT_INFO

    这可以通过使用“context connection = true;”来实现作为连接字符串。请记住,对 T-SQL 用户定义函数的所有其他限制都将被强制执行(即不能有任何副作用)。

    如果您使用常规连接(即不使用上下文连接),那么它将作为独立调用运行,就像使用 OPENQUERY 和 xp_cmdshell 方法时一样。

    但是,请记住,如果您将在影响超过 1 行的语句中使用调用存储过程的函数(无论您使用 3 种方法中的哪一种) ,则不能期望该行为每行运行一次。正如@MartinSmith 在对@MatBailie 答案的评论中提到的那样,查询优化器不保证函数执行的时间或数量。但如果你在SET @Variable = function(); 语句或SELECT * FROM function(); 查询中使用它,那么应该没问题。

    使用 .NET / C# SQLCLR 用户定义函数执行存储过程的示例如下文(我写的)所示:

    Stairway to SQLCLR Level 2: Sample Stored Procedure and Function

    【讨论】:

      【解决方案3】:

      这是另一种可能的解决方法:

      if exists (select * from master..sysservers where srvname = 'loopback')
          exec sp_dropserver 'loopback'
      go
      exec sp_addlinkedserver @server = N'loopback', @srvproduct = N'', @provider = N'SQLOLEDB', @datasrc = @@servername
      go
      
      create function testit()
          returns int
      as
      begin
          declare @res int;
          select @res=count(*) from openquery(loopback, 'exec sp_who');
          return @res
      end
      go
      
      select dbo.testit()
      

      它没有xp_cmdshell那么可怕,但也有too many implications实用。

      【讨论】:

      • 你会如何添加参数。
      • @SiyabongaDube,我的答案中的链接用一个例子来说明。摘录:“OPENQUERY 的第二个参数是要在远程服务器上运行的查询,您可能希望在这里可以使用变量,但您不能。查询字符串必须是常量,因为 SQL Server 需要能够在编译时确定结果集的形状。这意味着您的查询一旦有了参数值,就需要使用动态 SQL。”。
      【解决方案4】:

      编辑:我没有试过这个,所以我不能保证!你已经知道你不应该这样做,所以请不要这样做。但是...

      试试看这里:http://sqlblog.com/blogs/denis_gobo/archive/2008/05/08/6703.aspx

      关键位是我为您的目的尝试调整的位:

      DECLARE @SQL varchar(500)
      
      SELECT @SQL = 'osql -S' +@@servername +' -E -q "exec dbName..sprocName "'
      
      EXEC master..xp_cmdshell @SQL
      

      【讨论】:

      • 当我读到这篇文章时,我的一部分流口水了。当我的大部分人惊恐地尖叫并像鸡一样在房间里叽叽喳喳地跑来跑去时……说真的,如果你想为任何人提供一个可用的、友好的环境,就不要找到让函数产生副作用的黑客行为。比,比方说,明天……
      • 如果 OP 这样做你想让我被解雇!?这太苛刻了;(
      • 我?冷静的!?我很平静!听着,我几乎没有颤抖……你是什么意思像鸡一样叽叽喳喳?不是我告诉你的!不是我!别再这样盯着我了!!!
      • 大声笑。我应该保留还是删除它?
      • 另外人们会嘲笑这些 cmets。保留它。
      【解决方案5】:

      函数不允许有改变表格内容等副作用。

      存储过程是。

      如果一个函数调用一个存储过程,这个函数就会变得能够产生副作用。


      所以,抱歉,您不能从函数中调用存储过程。

      【讨论】:

      • 您的回答缺乏解释。要点是为什么 函数是不允许的,你只是将同一个句子改写成三种不同的形式。如果是关于引用透明度,那么state。如果是关于效率,请说明,但不要让人们感到疑惑。
      • @LukaRamishvili - 我会在那里与您略有矛盾。 Smoking causes cancer because smoking is carcenogenic 确实是循环的。但是Functions can't call StoredProcedures because Functions are not allowed to cause side-effects 不是循环的。 Why can't FN's cause side-effects? 可能有一个额外的 问题,但它确实是一个额外的问题。 Why? 几乎可以在任何级别询问,最后详细说明优化器的设计和实现。在我看来,这不是一个论坛。
      • 我们不要争论这个,你是对的。 OP没有问为什么,你的回答是直截了当的——“不”。但这听起来像是一种教义,即 [T-SQL] 函数不允许有副作用。重要的是要弄清楚是什么限制了它,为什么首先不允许函数产生副作用的原因,它可以解释为什么也不允许使用 storprocs。
      • @LukaRamishvili 可能的原因是执行的确切数量和执行时间取决于执行计划。例如,相同的查询可以具有不同数量的函数执行,具体取决于它们是在过滤器之前还是之后评估的。它甚至可以随着冷缓存和热缓存的相同计划而变化。 example
      • 鉴于其他答案中显示的 3 种执行此操作的方法,此答案绝对不正确。一个应该是否这样做是分开的,这取决于上下文(即该函数是否像SET @Variable = function一样运行一次,或者由于是查看许多可能行的查询的一部分而可能运行多次,即使它只返回一行)。此外,“如果一个函数调用了存储过程,该函数将能够产生副作用”这不是真的,因为 SQL Server 可以阻止那些被禁止的操作,就像从 SQLCLR 函数调用 proc 时一样。跨度>
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-07
      • 2012-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多