4
votes

Déclenchez AFTER INSERT, UPDATE, DELETE pour appeler une procédure stockée avec le nom de la table et la clé primaire

Pour un processus de synchronisation, ma base de données SQL Server doit enregistrer une liste des éléments qui ont changé - nom de table et clé primaire.

La base de données dispose déjà d'une table et d'une procédure stockée pour ce faire:

CREATE TABLE new_employees
(
    id_num INT IDENTITY(1,1),
    fname VARCHAR(20),
    minit CHAR(1),
    lname VARCHAR(30)
);
GO

IF OBJECT_ID ('dbo.sync_new_employees','TR') IS NOT NULL 
    DROP TRIGGER sync_new_employees;
GO

CREATE TRIGGER sync_new_employees
ON new_employees
AFTER INSERT, UPDATE, DELETE
AS
     DECLARE @Key Int;
     DECLARE @ErrCode Int;

     --  How to get the key???
     SELECT @Key = 12345; 

     EXEC @ErrCode = dbo.SyncQueueItem "new_employees", @key;
GO

Je voudrais ajouter des déclencheurs à une table pour appeler cette procédure stockée sur INSERT, UPDATE, DELETE. Comment obtenir la clé? Quelle est la chose la plus simple qui pourrait fonctionner?

EXEC @ErrCode = dbo.SyncQueueItem "tableName", 1234;


2 commentaires

Vous savez que le serveur SQL a intégré des mécanismes de suivi des modifications, de réplication, etc.


@Damien_The_Unbeliever Nous sommes tous intelligents ici. Vous allez devoir poser plus fort ...


5 Réponses :


0
votes

Pas la meilleure solution, mais juste une réponse directe à la question :

INSERT INTO [YourTableHistoryName] (id_num, fname, minit, lname, WhenHappened)
SELECT COALESCE(i.id_num,d.id_num) AS [id_num]
    ,i.fname,i.minit,i.lname,CURRENT_TIMESTAMP AS [WhenHeppened]
FROM inserted i
FULL JOIN deleted d ON d.id_num = i.id_num
WHERE ( COALESCE(i.fname,'')<>COALESCE(d.fname,'')
    OR COALESCE(i.minit,'')<>COALESCE(d.minit,'')
    OR COALESCE(i.lname,'')<>COALESCE(d.lname,'')
)
;

Aussi pas la meilleure façon (sinon la pire) (n'essayez pas cela à la maison), mais au moins cela vous aidera avec plusieurs valeurs :

DECLARE @Key INT;
DECLARE triggerCursor CURSOR LOCAL FAST_FORWARD READ_ONLY 
    FOR SELECT COALESCE(i.id_num,d.id_num) AS [id_num]
        FROM inserted i
        FULL JOIN deleted d ON d.id_num = i.id_num
        WHERE (
                COALESCE(i.fname,'')<>COALESCE(d.fname,'')
             OR COALESCE(i.minit,'')<>COALESCE(d.minit,'')
             OR COALESCE(i.lname,'')<>COALESCE(d.lname,'')
        )
;

OPEN triggerCursor;
FETCH NEXT FROM triggerCursor INTO @Key;
WHILE @@FETCH_STATUS = 0
BEGIN
    EXEC @ErrCode = dbo.SyncQueueItem 'new_employees', @key;
    FETCH NEXT FROM triggerCursor INTO @Key;
END

CLOSE triggerCursor;
DEALLOCATE triggerCursor;

Meilleure façon d'utiliser le déclencheur "value-change-tracker":

SELECT @Key = COALESCE(deleted.id_num,inserted.id_num);

La meilleure façon (à mon avis) de suivre les changements est d'utiliser des tables temporelles (SQL Server 2016 +) fort>


4 commentaires

Il est considéré comme une mauvaise pratique de codage de ne gérer que le cas d'une seule insertion / mise à jour / suppression d'enregistrement.


Je sais. C'était probablement une mauvaise idée de répondre directement à la question posée.


@MichaelCole - J'espère que vous savez que ce n'est pas une bonne solution - si jamais vous opérez sur plus d'une seule ligne, cela ne fonctionnera pas.


Mis à jour ma réponse



1
votes

Vous devez utiliser la Table magique pour obtenir les données. Habituellement, les tableaux insérés et supprimés sont appelés tableaux magiques dans le contexte d'un déclencheur. Il existe des tables magiques insérées et supprimées dans SQL Server. Ces tables sont automatiquement créées et gérées par SQL Server en interne pour contenir les valeurs récemment insérées, supprimées et mises à jour lors des opérations DML (insertion, mise à jour et suppression) sur une table de base de données.

Table magique insérée p>

Le tableau Inséré contient les valeurs récemment insérées, en d'autres termes, les nouvelles valeurs de données. Par conséquent, les enregistrements récemment ajoutés sont insérés dans le tableau Inséré.

Tableau magique supprimé

Le tableau Supprimé contient les valeurs récemment supprimées ou mises à jour, en d'autres termes, anciennes valeurs de données. Par conséquent, les anciens enregistrements mis à jour et supprimés sont insérés dans la table Supprimé.

** Vous pouvez utiliser la table magique insérée et supprimée pour obtenir la valeur de id_num **

 SELECT top 1 @Key = id_num from inserted  


4 commentaires

Je suppose que vous supposez / impliquez l'utilisation d'une forme de boucle pour gérer plusieurs enregistrements, n'est-ce pas?


@Dale Burrell, oui, merci, laissez-moi mettre à jour ma réponse.


Il est considéré comme une mauvaise pratique de codage de ne gérer que le cas d'une seule insertion / mise à jour / suppression d'enregistrement.


D'accord, ceci est juste un exemple de code pour l'expliquer, j'ai donné mon vote positif à votre réponse.



4
votes

Le moyen d'accéder aux enregistrements modifiés par l'opération consiste à utiliser les pseudo-tables Inséré et Supprimé qui vous sont fournis par SQL Server.

Inséré contient tous les enregistrements insérés, ou tous les enregistrements mis à jour avec leurs nouvelles valeurs.

Deleted contient tous les enregistrements supprimés ou tous les enregistrements mis à jour avec leurs anciennes valeurs.

Plus d'infos

Lors de l'écriture d'un déclencheur, pour être sûr, il faut toujours coder pour le cas où plusieurs enregistrements sont utilisés. Malheureusement, si vous devez appeler un SP, cela signifie une boucle - ce qui n'est pas idéal.

Le code suivant montre comment cela pourrait être fait pour votre exemple, et inclut une méthode pour détecter si l'opération est une Insérer / Mettre à jour / Supprimer.

declare @Key int, @ErrCode int, @Action varchar(6);

declare @Keys table (id int, [Action] varchar(6));

insert into @Keys (id, [Action])
  select coalesce(I.id, D.id_num)
    , case when I.id is not null and D.id is not null then 'Update' when I.id is not null then 'Insert' else 'Delete' end
  from Inserted I
  full join Deleted D on I.id_num = D.id_num;

while exists (select 1 from @Keys) begin
  select top 1 @Key = id, @Action = [Action] from @Keys;
  exec @ErrCode = dbo.SyncQueueItem 'new_employees', @key;
  delete from @Keys where id = @Key;
end

En outre: en plus de résoudre le problème que vous avez spécifié, il convient de noter quelques points concernant la situation dans son ensemble.

  1. Comme le souligne @Damien_The_Unbeliever, il existe des mécanismes intégrés pour effectuer le suivi des modifications qui fonctionneront bien mieux.
  2. Si vous souhaitez toujours gérer votre propre suivi des modifications, il serait plus efficace si vous pouviez l'organiser de telle sorte que vous gériez l'ensemble du jeu d'enregistrements en une seule fois plutôt que d'effectuer une opération ligne par ligne. Il existe 2 façons d'accomplir cela a) Déplacez votre code de suivi des modifications à l'intérieur du déclencheur et n'utilisez pas de SP. b) Utilisez un "Type de table défini par l'utilisateur" pour transmettre le jeu d'enregistrements des modifications au SP.


0 commentaires

1
votes

Un déclencheur DML doit activer les données set sinon une seule ligne sera traitée. Cela peut être quelque chose comme ça. Et bien sûr, utilisez les tables magiques insérées et supprimées .

CREATE TRIGGER dbo.tr_employees 
   ON  dbo.employees --the table from Northwind database
   AFTER INSERT,DELETE,UPDATE
AS 
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;
        declare @tbl table (id int identity(1,1),delId int,insId int)

        --Use "magic tables" inserted and deleted
        insert @tbl(delId, insId)
        select d.EmployeeID, i.EmployeeID
        from inserted i --empty when "delete"
        full join deleted d --empty when "insert"
            on i.EmployeeID=d.EmployeeID

        declare @id int,@key int,@action char

        select top 1 @id=id, @key=isnull(delId, insId),
            @action=case 
                when delId is null then 'I' 
                when insId is null then 'D' 
                else 'U' end --just in case you need the operation executed
        from @tbl
        --do something for each row
        while @id is not null --instead of cursor
        begin
            --do the main action
            --exec dbo.sync 'employees', @key, @action
            --remove processed row
            delete @tbl where id=@id
            --refill @variables
            select top 1 @id=id, @key=isnull(delId, insId),
                @action=case 
                    when delId is null then 'I' 
                    when insId is null then 'D' 
                    else 'U' end --just in case you need the operation executed
            from @tbl
        end
END


0 commentaires

0
votes

inséré / supprimé dans les déclencheurs générera autant de lignes que touché et l'appel d'un processus stocké par touche nécessiterait un curseur ou une approche similaire par ligne. Vous devez vérifier l'horodatage / rowversion dans SQL Server. Vous pouvez ajouter cela à toutes les tables en question (non nulles, incrémentées automatiquement, uniques dans la base de données pour chaque table / ligne, etc.). Vous pouvez ajouter un index unique sur cette colonne à toutes les tables auxquelles vous avez ajouté la colonne. @@ DBTS est l'horodatage actuel, vous pouvez stocker le @@ DBTS d'aujourd'hui et demain vous analyserez toutes les tables de celui-ci au @@ DBTS actuel. timestamp / rowversion sera incrémenté pour toutes les mises à jour et inserts, mais pour les suppressions, il ne sera pas suivi, pour les suppressions, vous pouvez avoir un déclencheur de suppression uniquement et insérer des clés dans une table différente. La capture de données modifiées ou le suivi des modifications pourraient le faire plus facilement, mais s'il y a de gros volumes sur le serveur ou un grand nombre de charges de données, les commutateurs de partition analysant le journal des transactions deviennent un goulot d'étranglement et dans certains cas, vous devrez supprimer la capture des données modifiées pour enregistrer le journal des transactions ne se développe pas indéfiniment.


0 commentaires