【问题标题】:Using SQL Server triggers, how to keep data in two identical tables the same without going in a infinite loop?使用 SQL Server 触发器,如何在不进入无限循环的情况下保持两个相同表中的数据相同?
【发布时间】:2021-12-29 12:40:54
【问题描述】:

我知道实现这一点的最佳方法可能是在应用程序代码中进行一些更改以保存两个表中的所有更改,但该公司下令通过数据库逻辑使用触发器来实现这一点。因此,有两个不同的数据库,都有一个名为 User 的表,并且它们都具有相同的模型。我在数据库 X 上的用户中插入/更新的内容必须在数据库 Y 上的用户表中插入/更新,反之亦然

我设法使插入和更新触发器朝一个方向(数据库 X -> 数据库 Y)进行,但现在我在想,当我在数据库 Y 上创建触发器时,会发生循环。缺少什么或我能做些什么来使触发循环不发生? 这是我现在在其中一个数据库上创建的:


---insert trigger
USE [DATABASE_Y]
CREATE TRIGGER [dbo].[TR_USER_OnInsert] ON [dbo].[USER]
AFTER INSERT
AS 

BEGIN

    SET NOCOUNT ON 

        INSERT INTO [DATABASE_X].[dbo].[USER] (
                usu_id,
                usu_name,
                usu_block,
                usu_login,
                usu_password
        )
        SELECT 
                usu_id,
                usu_name,
                usu_block,
                usu_login,
                usu_password
        FROM INSERTED

    SET NOCOUNT OFF

END

---update trigger
USE [DATABASE_Y]
ALTER TRIGGER [dbo].[TR_USER_OnUpdate] ON [dbo].[USER]
AFTER UPDATE
AS

开始

SET NOCOUNT ON 

    UPDATE X
    SET 
        X.usu_id        = INSERTED.usu_id,
        X.usu_name      = INSERTED.usu_name,
        X.usu_block     = INSERTED.usu_block,
        X.usu_login     = INSERTED.usu_login,
        X.usu_password  = INSERTED.usu_password
    FROM [DATABASE_X].[dbo].[USER] X
    INNER JOIN inserted ON X.usu_login = inserted.usu_login

SET NOCOUNT OFF

结束

【问题讨论】:

  • 为什么首先需要两个相同的表?这感觉就像XY Problem
  • 为什么需要两份表格?有很多更好的方法可以做到这一点,而无需尝试维护两个副本(链接服务器上的视图或同义词、缓存层、更智能的应用程序)或根据上下文(复制、日志传送、可用性组)的自动方法。一家公司要求你完全这样做似乎有点短视。 “使用三轮车,从迈阿密到洛杉矶最快的方式是什么?”
  • 也许您应该使用SYNONYM
  • 添加一个 where not exists() 用于插入和一个 where not all columns equal for the update
  • 澄清一下,相同的表位于两个不同的数据库中,用于不同的应用程序。基本上,他们最初的要求是让这些应用程序拥有相同的用户(登录名、密码等)并始终保持他们的身份。但是因为他们总是赶时间,所以他们不想对代码进行更改,而是使用我在问题中讲述的这种方法(使用触发器)“解决”问题。我知道这不是推荐的方式,但我仍然对他们的决定束手无策。

标签: sql-server triggers


【解决方案1】:

有趣的方法。有许多不同的方法可以解决这个问题,其中大多数都比使用触发器更好。

也就是说……

作为触发代码的一部分,在内存中创建一些变量,然后在目标数据库上运行选择以填充它们,然后再运行更新/插入。使用一些简单的逻辑来检查这些值是否尚未设置为相同的值,并避免陷入循环。

类似这样的更新触发器:

USE [DATABASE_Y]
ALTER TRIGGER [dbo].[TR_USER_OnUpdate] ON [dbo].[USER]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON 

DECLARE @id as varchar(100)
DECLARE @name as varchar(100)
DECLARE @block as varchar(100)
DECLARE @login as varchar(100)
DECLARE @password as varchar(100)

SELECT @id = X.usu_id,
       @name = X.usu_name,
       @block = X.usu_block,
       @login = X.usu_login,
       @password = X.usu_password
  FROM [DATABASE_X].[dbo].[USER] X
 WHERE X.usu_login = inserted.usu_login

    IF @id <> INSERTED.usu_id
    OR @name <> INSERTED.usu_name
    OR @block <> INSERTED.usu_block
    OR @login <> INSERTED.usu_login
    OR @password <> INSERTED.usu_password
 BEGIN
         UPDATE X
        SET 
            X.usu_id        = INSERTED.usu_id,
            X.usu_name      = INSERTED.usu_name,
            X.usu_block     = INSERTED.usu_block,
            X.usu_login     = INSERTED.usu_login,
            X.usu_password  = INSERTED.usu_password
        FROM [DATABASE_X].[dbo].[USER] X
        INNER JOIN inserted ON X.usu_login = inserted.usu_login
   END

SET NOCOUNT OFF

END

我不知道你的数据类型是什么,所以我只是假设所有内容都是 varchar(100)。

确保所有列都不为 NULL。如果您有任何 Nullable 列,请确保将 ISNULL 逻辑添加到变量比较中。

插入触发器更容易一些,因为您只需要检查您尝试插入的用户 ID 是否不存在:

USE [DATABASE_Y]
CREATE TRIGGER [dbo].[TR_USER_OnInsert] ON [dbo].[USER]
AFTER INSERT
AS 

BEGIN

    SET NOCOUNT ON 

     IF NOT EXISTS(SELECT 1 FROM [DATABASE_X].[dbo].[USER] WHERE usu_login = INSERTED.usu_login)
  BEGIN
  
        INSERT INTO [DATABASE_X].[dbo].[USER] (
                usu_id,
                usu_name,
                usu_block,
                usu_login,
                usu_password
        )
        SELECT 
                usu_id,
                usu_name,
                usu_block,
                usu_login,
                usu_password
        FROM INSERTED
    END

    SET NOCOUNT OFF

END

【讨论】:

    猜你喜欢
    • 2013-11-25
    • 1970-01-01
    • 1970-01-01
    • 2017-10-31
    • 2016-08-15
    • 1970-01-01
    • 1970-01-01
    • 2013-09-29
    • 1970-01-01
    相关资源
    最近更新 更多