使用 SQL Server 中的函数插入/更新/删除

发布于 2024-11-10 11:59:58 字数 360 浏览 8 评论 0原文

我们可以使用SQL Server Functions执行Insert/Update/Delete语句吗?我已经尝试过,但发生了 SQL Server 错误。

错误:

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

任何人都知道为什么我们不能将Insert/Update/Delete语句与SQL Server函数一起使用。

等待您的好主意

Can we perform Insert/Update/Delete statement with SQL Server Functions. I have tried with but SQL Server error is occured.

Error:

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

AnyBody have any Idea why we can not use Insert/Update/Delete statements with SQL Server functions.

Waiting for your good idea's

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

芸娘子的小脾气 2024-11-17 11:59:59

这是使用 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;

Just another alternative using sp_executesql (tested only in SQL 2016).
As previous posts noticed, atomicity must be handled elsewhere.

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;
女中豪杰 2024-11-17 11:59:58

,你不能。

来自 SQL Server 在线书籍:

无法使用用户定义的函数
执行修改操作
数据库状态。

参考

No, you cannot.

From SQL Server Books Online:

User-defined functions cannot be used
to perform actions that modify the
database state.

Ref.

寂寞美少年 2024-11-17 11:59:58

是的,你可以!))

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

我找到了一种方法来进行 INSERTUPDATEDELETE 在函数中使用 xp_cmdshell

因此,您只需替换 @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

Yes, you can!))

Disclaimer: This is not a solution, it is more of a hack to test out something. User-defined functions cannot be used to perform actions that modify the database state.

I found one way to make INSERT, UPDATE or DELETE in function using xp_cmdshell.

So you need just to replace the code inside @sql variable.

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
趴在窗边数星星i 2024-11-17 11:59:58

SQL Server 中的函数与数学中的函数一样,不能用于修改数据库。它们是只读的,可以帮助开发人员实现命令查询分离。换句话说,提出问题不应该改变答案。当您的程序需要修改数据库时,请改用存储过程。

Functions in SQL Server, as in mathematics, can not be used to modify the database. They are intended to be read only and can help developer to implement command-query separation. In other words, asking a question should not change the answer. When your program needs to modify the database use a stored procedure instead.

方觉久 2024-11-17 11:59:58

您无法像存储过程那样从函数更新表,但可以更新表变量。

例如,您不能在函数中执行此操作:

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'

You can't update tables from a function like you would a stored procedure, but you CAN update table variables.

So for example, you can't do this in your function:

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

but you can do:

declare @myTable table
(
    ID int,
    column1 varchar(100)
)

Update @myTable
set column1='My value'
罗罗贝儿 2024-11-17 11:59:58

是的,可以。

但是,它需要具有 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)

文档: 在 SQL 函数中修改表状态

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

Yes, you can.

However, it requires SQL CLR with EXTERNAL_ACCESS or UNSAFE permission and specifying a connection string. This is obviously not recommended.

For example, using Eval SQL.NET (a SQL CLR which allow to add C# syntax in SQL)

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)

Documentation: Modify table state within a SQL Function

Disclaimer: I'm the owner of the project Eval SQL.NET

靑春怀旧 2024-11-17 11:59:58

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

即可达到结果。

You can have a table variable as a return type and then update or insert on a table based on that output.
In other words, you can set the variable output as the original table, make the modifications and then do an insert to the original table from function output.
It is a little hack but if you insert the @output_table from the original table and then say for example:
Insert into my_table
select * from my_function

then you can achieve the result.

耀眼的星火 2024-11-17 11:59:58

我们不能说他们可能不存在其他方式来在用户定义的函数中执行更新操作。可以肯定的是,在 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

We can't say that it is possible of not their is some other way exist to perform update operation in user-defined Function. Directly DML is not possible in UDF it is for sure.

Below Query is working perfectly:

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
雨落□心尘 2024-11-17 11:59:58

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

"Functions have only READ-ONLY Database Access"
If DML operations would be allowed in functions then function would be prety similar to stored Procedure.

小霸王臭丫头 2024-11-17 11:59:58

不,您不能执行插入/更新/删除操作。

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

此外:

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

No, you can not do Insert/Update/Delete.

Functions only work with select statements. And it has only READ-ONLY Database Access.

In addition:

  • Functions compile every time.
  • Functions must return a value or result.
  • Functions only work with input parameters.
  • Try and catch statements are not used in functions.
北城半夏 2024-11-17 11:59:58
  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
  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
染柒℉ 2024-11-17 11:59:58

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

Functions are not meant to be used that way, if you wish to perform data change you can just create a Stored Proc for that.

波浪屿的海角声 2024-11-17 11:59:58

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

declare 
    @v_dynDelete                 NVARCHAR(500);

 SET @v_dynDelete = 'DELETE some_table;'; 
 EXEC @v_dynDelete

if you need to run the delete/insert/update you could also run dynamic statements. i.e.:

declare 
    @v_dynDelete                 NVARCHAR(500);

 SET @v_dynDelete = 'DELETE some_table;'; 
 EXEC @v_dynDelete
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文