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;
5 Réponses :
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>
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
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
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.
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.
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.
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
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.
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 ...