J'exécute une fusion dans SQL Server. Dans ma mise à jour, je souhaite mettre à jour la ligne uniquement si les valeurs ont changé. Il existe une ligne de version qui s'incrémente à chaque mise à jour. Voici un exemple:
MERGE Employee as tgt USING
(SELECT Employee_History.Emp_ID
, Employee_History.First_Name
, Employee_History.Last_Name
FROM Employee_History)
as src (Emp_ID,First_Name,Last_Name)
ON tgt.Emp_ID = src.Emp_ID
WHEN MATCHED THEN
UPDATE SET
Emp_ID = src.Emp_ID,
,[VERSION] = tgt.VERSION + 1
,First_Name = src.First_Name
,Last_Name = src.Last_Name
WHEN NOT MATCHED BY target THEN
INSERT (Emp_ID,0,First_Name,Last_Name)
VALUES
(src.Emp_ID,[VERSION],src.First_Name,src.Last_Name);
Maintenant, si je voulais seulement mettre à jour la ligne, et donc incrémenter la version, UNIQUEMENT si le nom a changé.
WHEN MATCHED
Peut avoir AND
. De plus, pas besoin de mettre à jour EMP_ID
.
...
WHEN MATCHED AND (trg.First_Name <> src.First_Name
OR trg.Last_Name <> src.Last_Name) THEN UPDATE
SET
[VERSION] = tgt.VERSION + 1
,First_Name = src.First_Name
,Last_Name = src.Last_Name
...
Si Last_Name ou First_Name sont nullables, vous devez prendre soin des valeurs NULL
lors de la comparaison de trg.Last_Name <> src.Last_Name, par exemple ISNULL(trg.Last_Name,'') <> ISNULL(src.Last_Name,'')
Plutôt que d'éviter complètement une mise à jour, vous pouvez modifier votre [VERSION] + 1
code pour ajouter zéro lorsque les noms correspondent:
[VERSION] = tgt.VERSION + (CASE
WHEN tgt.First_Name <> src.First_Name OR tgt.Last_Name <> src.Last_Name
THEN 1
ELSE 0 END)
La réponse fournie par a1ex07 est la bonne réponse, mais je voulais juste développer la difficulté de comparer un grand nombre de colonnes, de surveiller les valeurs nulles, etc.
J'ai trouvé que je pouvais générer une somme de contrôle dans certains CTE avec des hashbytes, cibler ces CTE dans la fusion, puis utiliser la condition "update and ...." spécifiée ci-dessus pour comparer les hachages:
with SourcePermissions as (
SELECT 1 as Code, 1013 as ObjectTypeCode, 'Create Market' as ActionName, null as ModuleCode, 1 as AssignableTargetFlags
union all SELECT 2, 1013, 'View Market', null, 1
union all SELECT 3, 1013, 'Edit Market', null, 1
--...shortened....
)
,SourcePermissions2 as (
select sp.*, HASHBYTES('sha2_256', xmlcol) as [Checksum]
from SourcePermissions sp
cross apply (select sp.* for xml raw) x(xmlcol)
)
,TargetPermissions as (
select p.*, HASHBYTES('sha2_256', xmlcol) as [Checksum]
from Permission p
cross apply (select p.* for xml raw) x(xmlcol)
) --select * from SourcePermissions2 sp join TargetPermissions tp on sp.code=tp.code where sp.Checksum = tp.Checksum
MERGE TargetPermissions AS target
USING (select * from SourcePermissions2) AS source ([Code] , [ObjectTypeCode] , [ActionName] , [ModuleCode] , [AssignableTargetFlags], [Checksum])
ON (target.Code = source.Code)
WHEN MATCHED and source.[Checksum] != target.[Checksum] then
UPDATE SET [ObjectTypeCode] = source.[ObjectTypeCode], [ActionName]=source.[ActionName], [ModuleCode]=source.[ModuleCode], [AssignableTargetFlags] = source.[AssignableTargetFlags]
WHEN NOT MATCHED THEN
INSERT ([Code] , [ObjectTypeCode] , [ActionName] , [ModuleCode] , [AssignableTargetFlags])
VALUES (source.[Code] , source.[ObjectTypeCode] , source.[ActionName] , source.[ModuleCode] , source.[AssignableTargetFlags])
OUTPUT deleted.*, $action, inserted.[Code]
--only minor issue is that you can no longer do a inserted.* here since it gives error 404 (sql, not web), complaining about returning checksum which is included in the target cte but not the underlying table
,inserted.[ObjectTypeCode] , inserted.[ActionName] , inserted.[ModuleCode] , inserted.[AssignableTargetFlags]
;
Quelques notes: j'aurais pu simplifier énormément avec checksum ou binary_checksum, mais j'ai toujours des collisions avec celles-ci.
Quant au "pourquoi", cela fait partie d'un déploiement automatisé pour maintenir une table de recherche à jour. Le problème avec la fusion est cependant qu'il existe une vue indexée qui est complexe et très utilisée, donc les mises à jour des tables associées sont assez coûteuses.