web-dev-qa-db-fra.com

Insérer/Mettre à jour/Supprimer avec une fonction dans SQL Server

Pouvons-nous effectuer l'instruction Insert/Update/Delete avec SQL Server Functions. J'ai essayé avec mais une erreur SQL Server s'est produite.

Erreur:

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

AnyBody a la moindre idée pourquoi nous ne pouvons pas utiliser les instructions Insert/Update/Delete avec SQL Server functions.

En attente de vos bonnes idées

34
Pankaj Agarwal

Non, vous ne pouvez pas.

De la documentation en ligne de SQL Server:

Les fonctions définies par l'utilisateur ne peuvent pas être utilisées effectuer des actions qui modifient le état de la base de données.

Réf .

68
Mitch Wheat

Les fonctions de SQL Server, comme en mathématiques, ne peuvent pas être utilisées pour modifier la base de données. Ils sont destinés à être en lecture seule et peuvent aider le développeur à implémenter séparation commande-requête . En d'autres termes, poser une question ne devrait pas changer la réponse. Lorsque votre programme doit modifier la base de données, utilisez plutôt une procédure stockée.

18
Anthony Faull

Oui, vous pouvez!))

J'ai trouvé un moyen de rendre INSERT, UPDATE ou DELETE dans une fonction utilisant xp_cmdshell.

Il vous suffit donc de remplacer le code dans la variable @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
11
Yurii Tsurul

Vous ne pouvez pas mettre à jour des tables à partir d'une fonction comme une procédure stockée, mais vous POUVEZ mettre à jour des variables de table.

Ainsi, par exemple, vous ne pouvez pas faire cela dans votre fonction:

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

mais vous pouvez faire:

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

Update @myTable
set column1='My value'
4
FistOfFury

Oui, vous pouvez.

Toutefois, il nécessite SQL CLR avec l'autorisation EXTERNAL_ACCESS ou UNSAFE et la spécification d'une chaîne de connexion. Ceci est évidemment non recommandé.

Par exemple, en utilisant Eval SQL.NET (un CLR SQL qui permet d’ajouter une syntaxe C # dans 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: Modifier l'état de la table dans une fonction SQL

Disclaimer: je suis propriétaire du projet Eval SQL.NET

3
Jonathan Magnan

Juste une autre alternative en utilisant sp_executesql (testé uniquement dans SQL 2016 ) . Comme noté dans les messages précédents, l'atomicité doit être traitée ailleurs.

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
Vinicius

Nous ne pouvons pas dire qu'il est possible ou non qu'il existe un autre moyen d'exécuter l'opération de mise à jour dans une fonction définie par l'utilisateur. Directement, le format DML n’est pas possible dans UDF, c’est certain.

La requête ci-dessous fonctionne parfaitement:

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
Ajay Sharma