4 votes

clause de sortie VS déclencheurs

Dans notre base de données, la plupart des tables ont un dbupddate qui indique le datetime de la dernière INSERT o UPDATE appliqué à la rangée.

Afin d'éviter que ce champ ait une valeur erronée, il existe des triggers (parfois AFTER parfois INSTEAD OF ) qui garantissent qu'en fin de compte, la valeur est correcte et non pas une autre valeur "manuelle" que quelqu'un pourrait essayer d'écrire dans ce champ.

Maintenant, j'exécute une déclaration de mise à jour (actuellement MERGE ) et je veux avoir un OUTPUT incluant ce champ. Comme je l'ai lu dans l'article MS approprié , OUTPUT ignore les déclencheurs.

Existe-t-il une solution de contournement pour avoir OUTPUT retourner la valeur dbupddate a après les déclenchements ? Je ne veux pas faire une autre requête pour obtenir l'information, car je ne suis pas sûr que dans la fraction de seconde qui sépare ces requêtes, une troisième requête d'un autre utilisateur n'ait pas tout changé.


Résultats après avoir suivi les suggestions de Larnu

J'ai exécuté les exemples fournis, à la seule exception de la modification de l'adresse de l'utilisateur. default des valeurs de l updatetime les champs à convert(datetime2,'1900-01-01') pour que je puisse avoir un peu de sens. J'ai exécuté chacune des 4 requêtes, suivies d'une sélection à partir de leur table respective et j'ai comparé les résultats suivants updatetime valeurs :

INSERT INTO dbo.Sample1 (Someint)
OUTPUT inserted.*
INTO @inserted
SELECT 1;

SELECT 'Sample1 INSERT',*
FROM @inserted; -- 1900-01-01 00:00:00.000000
select * from Sample1  -- 2018-11-05 13:12:13.141580

Je suppose que la sortie ici ignore le déclencheur et renvoie la valeur par défaut qui a été insérée avant la commande after Le déclencheur a pris effet.

DECLARE @inserted table (ID int, Someint int, updatedtime datetime2(6))
INSERT INTO dbo.Sample2 (Someint)
OUTPUT inserted.*
INTO @inserted
SELECT 1;

SELECT 'Sample2 INSERT',* --1900-01-01 00:00:00.000000
FROM @inserted;
select * from Sample2 --2018-11-05 13:12:35.580190

Même. Maintenant, la partie la plus folle arrive. J'ai dessiné les dates insérées et supprimées :

DECLARE @updated table (ID int, Someint int, ins_updatedtime datetime2(6),del_updatedtime datetime2(6))
UPDATE dbo.Sample1 
SET Someint = 2
OUTPUT Inserted.*,Deleted.updatetime
INTO @updated;

SELECT 'Sample1 UPDATE',*
FROM @updated;   --Sample1 UPDATE   1   2   2018-11-05 13:30:01.348490  2018-11-05 13:30:01.348490
select * from Sample1  -- 1 2   2018-11-05 13:31:31.851047

DECLARE @updated table (ID int, Someint int, ins_updatedtime datetime2(6),del_updatedtime datetime2(6))
UPDATE dbo.Sample2
SET Someint = 2
OUTPUT Inserted.*,Deleted.updatetime
INTO @updated;

SELECT 'Sample2 UPDATE',* -- Sample2 UPDATE 1   2   2018-11-05 13:30:20.286422  2018-11-05 13:30:20.286422
FROM @updated;
select * from Sample2 --1   2   2018-11-05 13:31:51.679726

Ainsi, dans le update Dans certains cas, la valeur par défaut n'est pas présente, mais j'ai des valeurs différentes dans la table actuelle et dans la sortie de la requête. Je ne sais ni comment rendre ces valeurs identiques, ni ce qui se passe exactement avec les dates dans le cas de la mise à jour.

4voto

Larnu Points 38828

Vous pouvez utiliser OUTPUT avec un TRIGGER mais vous devez également utiliser le INTO également. Prenez ces exemples de tables et de déclencheurs :

CREATE TABLE dbo.Sample1 (SomeID int IDENTITY(1,1),
                          Someint int,
                          updatetime datetime2(6) DEFAULT SYSDATETIME());
CREATE TABLE dbo.Sample2 (SomeID int IDENTITY(1,1),
                          Someint int,
                          updatetime datetime2(6) DEFAULT SYSDATETIME());

GO

CREATE TRIGGER dbo.AfterInsertUdpate ON dbo.Sample1
AFTER INSERT, UPDATE
AS
    UPDATE S
    SET S.updatetime = SYSDATETIME()
    FROM dbo.Sample1 S
         JOIN Inserted i ON S.SomeID = i.SomeID;
GO

CREATE TRIGGER dbo.InsteadInsert ON dbo.Sample2
INSTEAD OF INSERT
AS
    INSERT INTO dbo.Sample2 (Someint,
                             updatetime)
    SELECT Someint, SYSDATETIME()
    FROM Inserted;

GO

CREATE TRIGGER dbo.InsteadUpdate ON dbo.Sample2
INSTEAD OF UPDATE
AS
    UPDATE S
    SET S.Someint = i.Someint,
        S.updatetime = SYSDATETIME()
    FROM dbo.Sample2 S
         JOIN Inserted i ON S.SomeID = i.SomeID;

Si nous devions exécuter le SQL suivant, vous obtiendriez une erreur :

INSERT INTO dbo.Sample1 (Someint)
OUTPUT inserted.*
SELECT 1;

Msg 334, Niveau 16, État 1, Ligne 44 La table cible 'dbo.Sample1' de l'instruction DML ne peut pas avoir de déclencheurs activés si l'instruction contient une clause OUTPUT sans clause INTO.

L'erreur vous donne l'astuce ici, utilisez la fonction INTO clause. Vous pouvez donc, à la place, faire :

DECLARE @inserted table (ID int, Someint int, updatedtime datetime2(6))

INSERT INTO dbo.Sample1 (Someint)
OUTPUT inserted.*
INTO @inserted
SELECT 1;

SELECT 'Sample1 INSERT',*
FROM @inserted;

Cela fonctionne pour les deux INSERT y UPDATE peu importe si c'est AFTER o INSTEAD OF :

DECLARE @inserted table (ID int, Someint int, updatedtime datetime2(6))
INSERT INTO dbo.Sample2 (Someint)
OUTPUT inserted.*
INTO @inserted
SELECT 1;

SELECT 'Sample2 INSERT',*
FROM @inserted;

GO
DECLARE @updated table (ID int, Someint int, updatedtime datetime2(6))
UPDATE dbo.Sample1 
SET Someint = 2
OUTPUT Inserted.*
INTO @updated;

SELECT 'Sample1 UPDATE',*
FROM @updated;

GO
DECLARE @updated table (ID int, Someint int, updatedtime datetime2(6))
UPDATE dbo.Sample2
SET Someint = 2
OUTPUT Inserted.*
INTO @updated;

SELECT 'Sample2 UPDATE',*
FROM @updated;
GO

--Clean up
DROP TABLE dbo.Sample1;
DROP TABLE dbo.Sample2;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X