Вставить / Обновить / Удалить с функцией в SQL Server - PullRequest
35 голосов
/ 27 мая 2011

Можем ли мы выполнить Insert/Update/Delete оператор с SQL Server Functions.Я пытался с, но ошибка SQL Server произошла.

Ошибка:

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

AnyBody есть идея, почему мы не можем использовать операторы Insert/Update/Delete с SQL Server functions.

В ожидании вашей хорошей идеи

Ответы [ 9 ]

69 голосов
/ 27 мая 2011

Нет , вы не можете.

Из электронной документации по SQL Server:

Пользовательские функции не могут быть использованы выполнять действия, которые изменяют состояние базы данных.

1012 * Ref *.

18 голосов
/ 27 мая 2011

Функции в SQL Server, как и в математике, нельзя использовать для изменения базы данных. Они предназначены только для чтения и могут помочь разработчику реализовать разделение команд и запросов . Другими словами, задание вопроса не должно изменить ответ. Когда вашей программе нужно изменить базу данных, используйте хранимую процедуру.

11 голосов
/ 28 октября 2016

Да, можно!))

Я нашел один способ сделать INSERT, UPDATE или DELETE в функции, используя 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
5 голосов
/ 15 августа 2012

Вы не можете обновить таблицы из функции, как если бы вы были хранимой процедурой, но вы МОЖЕТЕ обновить таблицы переменных.

Так, например, вы не можете сделать это в своей функции:

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'
3 голосов
/ 18 февраля 2016

Да , вы можете.

Однако для этого требуется SQL CLR с разрешением EXTERNAL_ACCESS или UNSAFE и указанием строки подключения.Это, очевидно, не рекомендуется .

Например, используя Eval SQL.NET (SQL CLR, позволяющий добавить синтаксис C # в 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)

Документация: Изменение состояния таблицы в функции SQL

Отказ от ответственности : я владелец проекта Eval SQL.NET

0 голосов
/ 18 апреля 2019

«Функции имеют только доступ к базе данных только для чтения» Если бы в функциях были разрешены операции DML, функция была бы похожа на хранимую процедуру.

0 голосов
/ 22 декабря 2017

Мы не можем сказать, что возможно, что существует какой-то другой способ выполнения операции обновления в пользовательской функции.Непосредственно DML в UDF невозможен, это точно.

Ниже Query работает отлично:

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
0 голосов
/ 04 июля 2017

Еще одна альтернатива с использованием 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;
0 голосов
/ 18 февраля 2016

если вам нужно запустить удаление / вставку / обновление, вы также можете запустить динамические операторы. i.e.:

declare 
    @v_dynDelete                 NVARCHAR(500);

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