【问题标题】:Insert/Update/Delete with function in SQL Server使用 SQL Server 中的函数插入/更新/删除
【发布时间】:2011-09-03 07:12:17
【问题描述】:

我们可以用SQL Server Functions 执行Insert/Update/Delete 语句吗?我试过了,但是出现了 SQL Server 错误。

错误:

Invalid use of side-effecting or time-dependent operator in 'DELETE' within a function.

AnyBody 知道为什么我们不能将 Insert/Update/Delete 语句与 SQL Server functions. 一起使用

等待你的好主意

【问题讨论】:

标签: sql-server sql-server-2008 sql-server-2012


【解决方案1】:

函数并不意味着以这种方式使用,如果您希望执行数据更改,您可以为此创建一个存储过程。

【讨论】:

    【解决方案2】:
      CREATE FUNCTION dbo.UdfGetProductsScrapStatus
    (
    @ScrapComLevel INT
    ) 
    RETURNS @ResultTable TABLE
    ( 
    ProductName VARCHAR(50), ScrapQty FLOAT, ScrapReasonDef VARCHAR(100), ScrapStatus VARCHAR(50)
    ) AS BEGIN
            INSERT INTO @ResultTable
                SELECT PR.Name, SUM([ScrappedQty]), SC.Name, NULL
                    FROM [Production].[WorkOrder] AS WO
                            INNER JOIN 
                            Production.Product AS PR
                            ON Pr.ProductID = WO.ProductID
                            INNER JOIN Production.ScrapReason AS SC
                            ON SC.ScrapReasonID = WO.ScrapReasonID
                    WHERE WO.ScrapReasonID IS NOT NULL
                    GROUP BY PR.Name, SC.Name
    UPDATE @ResultTable
                SET ScrapStatus = 
                CASE WHEN ScrapQty > @ScrapComLevel THEN 'Critical'
                ELSE 'Normal'
                END
            
    RETURN
    END
    

    【讨论】:

      【解决方案3】:

      是的,你可以!))

      免责声明:这不是一个解决方案,它更像是一个 hack 来测试一些东西。用户定义的函数不能用于执行修改数据库状态的操作。

      我找到了一种使用xp_cmdshell 在函数中生成INSERTUPDATEDELETE 的方法。

      所以你只需要替换 @sql 变量中的代码。

      CREATE FUNCTION [dbo].[_tmp_func](@orderID NVARCHAR(50))
      RETURNS INT
      AS
      BEGIN
      DECLARE @sql varchar(4000), @cmd varchar(4000)
      SELECT @sql = 'INSERT INTO _ord (ord_Code) VALUES (''' + @orderID + ''') '
      SELECT @cmd = 'sqlcmd -S ' + @@servername +
                    ' -d ' + db_name() + ' -Q "' + @sql + '"'
      EXEC master..xp_cmdshell @cmd, 'no_output'
      RETURN 1
      END
      

      【讨论】:

      • +1 为答案,因为它似乎是唯一的解决方案,但组件 xp_cmdshell 必须打开。这在很多情况下都会成为问题,因为出于安全原因,系统管理员不允许这样做。
      • 虽然很有创意,但我会尽量记住将来是否绝对必要,但这种方法没有交易覆盖范围。
      • 需要先开启cmdshell EXEC sp_configure 'show advanced options', 1 GO RECONFIGURE GO EXEC sp_configure 'xp_cmdshell', 1 GO RECONFIGURE GO
      【解决方案4】:

      您可以将表变量作为返回类型,然后根据该输出更新或插入表。 换句话说,您可以将变量输出设置为原始表,进行修改,然后从函数输出中插入原始表。 这是一个小技巧,但是如果您从原始表中插入 @output_table 然后例如说: 插入 my_table select * from my_function

      那么你就可以达到效果了。

      【讨论】:

        【解决方案5】:

        不,您不能进行插入/更新/删除。

        函数仅适用于 select 语句。而且它只有只读数据库访问权限。

        另外:

        • 函数每次都编译。
        • 函数必须返回一个值或结果。
        • 函数仅适用于输入参数。
        • 函数中不使用 Try 和 catch 语句。

        【讨论】:

          【解决方案6】:

          “函数只有只读数据库访问” 如果函数中允许 DML 操作,那么函数将与存储过程非常相似。

          【讨论】:

          • 和执行过程不同
          【解决方案7】:

          是的,你可以。

          但是,它需要具有 EXTERNAL_ACCESS 或 UNSAFE 权限并指定连接字符串的 SQL CLR。这显然是不推荐

          例如,使用Eval SQL.NET(允许在 SQL 中添加 C# 语法的 SQL CLR)

          CREATE FUNCTION [dbo].[fn_modify_table_state]
              (
                @conn VARCHAR(8000) ,
                @sql VARCHAR(8000)
              )
          RETURNS INT
          AS
              BEGIN
                  RETURN SQLNET::New('
          using(var connection = new SqlConnection(conn))
          {
              connection.Open();
          
              using(var command = new SqlCommand(sql, connection))
              {
                  return command.ExecuteNonQuery();
              }
          }
          ').ValueString('conn', @conn).ValueString('sql', @sql).EvalReadAccessInt()
          
              END
          
              GO
          
          DECLARE @conn VARCHAR(8000) = 'Data Source=XPS8700;Initial Catalog=SqlServerEval_Debug;Integrated Security=True'
          DECLARE @sql VARCHAR(8000) = 'UPDATE [Table_1] SET Value = -1 WHERE Name = ''zzz'''
          
          DECLARE @rowAffecteds INT =  dbo.fn_modify_table_state(@conn, @sql)
          

          文档:Modify table state within a SQL Function

          免责声明:我是项目的所有者Eval SQL.NET

          【讨论】:

            【解决方案8】:

            我们不能说在用户自定义函数中执行更新操作的方式可能不是他们的其他方式。在 UDF 中直接 DML 是不可能的,这是肯定的。

            以下查询运行良好:

            create table testTbl
            (
            id int identity(1,1) Not null,
            name nvarchar(100)
            )
            GO
            
            insert into testTbl values('ajay'),('amit'),('akhil')
            Go
            
            create function tblValued()
            returns Table
            as
            return (select * from testTbl where id = 1)
            Go
            
            update tblValued() set name ='ajay sharma' where id = 1
            Go
            
            select * from testTbl 
            Go
            

            【讨论】:

              【解决方案9】:

              使用 sp_executesql 的另一种选择(仅在 SQL 2016 中测试)。 正如之前的帖子所指出的,原子性必须在其他地方处理。

              CREATE FUNCTION [dbo].[fn_get_service_version_checksum2]
              (
                  @ServiceId INT
              )
              RETURNS INT
              AS
              BEGIN
              DECLARE @Checksum INT;
              SELECT @Checksum = dbo.fn_get_service_version(@ServiceId);
              DECLARE @LatestVersion INT = (SELECT MAX(ServiceVersion) FROM [ServiceVersion] WHERE ServiceId = @ServiceId);
              -- Check whether the current version already exists and that it's the latest version.
              IF EXISTS(SELECT TOP 1 1 FROM [ServiceVersion] WHERE ServiceId = @ServiceId AND [Checksum] = @Checksum AND ServiceVersion = @LatestVersion)
                  RETURN @LatestVersion;
              -- Insert the new version to the table.
              EXEC sp_executesql N'
              INSERT INTO [ServiceVersion] (ServiceId, ServiceVersion, [Checksum], [Timestamp])
              VALUES (@ServiceId, @LatestVersion + 1, @Checksum, GETUTCDATE());',
              N'@ServiceId INT = NULL, @LatestVersion INT = NULL, @Checksum INT = NULL',
              @ServiceId = @ServiceId,
              @LatestVersion = @LatestVersion,
              @Checksum = @Checksum
              ;
              RETURN @LatestVersion + 1;
              END;
              

              【讨论】:

              • 错误:只能在函数内执行函数和一些扩展存储过程。
              • 这是在 SQL 2016 上运行的生产代码,如帖子中所述。不同版本的 SQL Server 会有不同的功能和限制。
              【解决方案10】:

              如果您需要运行删除/插入/更新,您也可以运行动态语句。即:

              declare 
                  @v_dynDelete                 NVARCHAR(500);
              
               SET @v_dynDelete = 'DELETE some_table;'; 
               EXEC @v_dynDelete
              

              【讨论】:

              • EXEC 不能在函数中使用。
              【解决方案11】:

              您不能像使用存储过程那样通过函数更新表,但可以更新表变量。

              例如,你不能在你的函数中这样做:

              create table MyTable
              (
                  ID int,
                  column1 varchar(100)
              )
              update [MyTable]
              set column1='My value'
              

              但你可以这样做:

              declare @myTable table
              (
                  ID int,
                  column1 varchar(100)
              )
              
              Update @myTable
              set column1='My value'
              

              【讨论】:

              • 请记住,如果您从临时内存表中更新/删除,Microsoft Management Studio 可能会给您一个误报,因为遗憾的是,即使在版本 14 上,智能感知仍然无法识别数据库之间表和内存变量表。
              • 是的,如果您只是按照建议使用更新语句,它就可以正常工作。有没有办法使用 update-from 语法?在函数内部使用 update-from 语法时,出现“在函数中无效使用副作用运算符 'update'”错误。
              【解决方案12】:

              SQL Server 中的函数,如数学中的函数,不能用于修改数据库。它们是只读的,可以帮助开发人员实现command-query separation。换句话说,问一个问题不应该改变答案。当您的程序需要修改数据库时,请改用存储过程。

              【讨论】:

                【解决方案13】:

                ,你不能。

                来自 SQL Server 联机丛书:

                不能使用用户自定义函数 执行修改 数据库状态。

                Ref.

                【讨论】:

                  猜你喜欢
                  • 2013-11-07
                  • 2011-05-15
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-12-17
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-02-07
                  相关资源
                  最近更新 更多