背景故事

在我们计划在我们的主要表中拆除天然钥匙栏的工作中。该项目由链接到此表/列的100多个应用程序组成; 400+存储的过程,直接引用此列;以及这些应用程序之间也引用本列的一系列常见表。

大爆炸和从头开始的方法不在图片中。我们将一次弃用本专栏的应用程序,证明更改并继续进行下一个……而且我们有一个漫长的目标目标来使这项工作实用。

我遇到的问题是,其中许多应用程序都共享了存储的过程和表。如果我完全转换了所有应用程序A的表/存储过程,则应用程序B和C将被打破直到转换为止。这些反过来可能打破申请D,E,F ...等。我已经为代码类和存储过程实施了策略,我坚持的一部分是数据库的过渡状态。

这是一个 基本的 我们拥有的示例:

Users
---------------------------
Code          varchar(32) natural key

Access
---------------------------
UserCode      varchar(32) foreign key
AccessLevel   int

我们现在只是为了这样的过渡状态:

Users
---------------------------
Code          varchar(32) 
Id            int         surrogate key

Access
---------------------------
UserCode      varchar(32)   
UserID        int         foreign key      
AccessLevel   int

这个想法是在过渡阶段的非移民应用程序和存储过程中的想法,仍然能够访问所有适当的数据,而新数据可以开始推向正确的列 - 一旦所有存储过程和应用程序都完成了迁移,我们最终可以最终可以放下额外的列。

我想使用SQL Server的触发器自动拦截任何新的插入/更新,并在每个受影响的表上执行以下操作:

CREATE TRIGGER tr_Access_Sync
ON Access
INSTEAD OF INSERT(, UPDATE)
AS
BEGIN
  DIM @code as Varchar(32)
  DIM @id as int

  SET @code = (SELECT inserted.code FROM inserted)
  SET @id = (SELECT inserted.code FROM inserted)

  -- This is a migrated application; find the appropriate legacy key
  IF @code IS NULL AND @id IS NOT NULL
     SELECT Code FROM Users WHERE Users.id = @id

  -- This is a legacy application; find the appropriate surrogate key
  IF @id IS NULL AND @code IS NOT NULL
     SELECT Code FROM Users WHERE Users.id = @id

  -- Impossible code:
  UPDATE inserted SET inserted.code=@code, inserted.id=@id
END

问题

到目前为止,我遇到的两个巨大问题是:

  1. 我不能执行“插入后”,因为无限制会使插入物失败。
  2. 我提到的“不可能的代码”是我想如何清洁原始查询。如果原始查询中有x,y,z列或仅x,我理想地希望同样的触发器来执行这些操作。而且,如果我添加/删除另一列,我希望触发器保持功能。

任何人都有一个代码示例,即使只有一个值传递给SQL,也可以在其中可能会正确填充这些列的替代解决方案?

有帮助吗?

解决方案 3

睡觉后,这似乎是我在SQL语法中提出的最通用/重复使用的解决方案。即使这两个列都没有零件的限制,即使您根本不引用插入中的“其他”列,它也可以正常工作。

CREATE TRIGGER tr_Access_Sync
ON Access
INSTEAD OF INSERT
AS 
BEGIN

    /*-- Create a temporary table to modify because "inserted" is read-only */
    /*-- "temp" is actually "#temp" but it throws off stackoverflow's syntax highlighting */
    SELECT * INTO temp FROM inserted

    /*-- If for whatever reason the secondary table has it's own identity column */
    /*-- we need to get rid of it from our #temp table to do an Insert later with identities on */
    ALTER TABLE temp DROP COLUMN oneToManyIdentity

    UPDATE temp 
    SET 
        UserCode = ISNULL(UserCode, (SELECT UserCode FROM Users U WHERE U.UserID = temp.UserID)),
        UserID = ISNULL(UserID, (SELECT UserID FROM Users U WHERE U.UserCode = temp.UserCode))

    INSERT INTO Access SELECT * FROM temp

END

其他提示

棘手的业务...

好,首先:这个触发器将 不是 在许多情况下工作:

SET @code = (SELECT inserted.code FROM inserted)
SET @id = (SELECT inserted.code FROM inserted)

可以在 Inserted 伪桌 - 您要在这里选择哪一个?您需要以一种方式编写触发器,即使您在其中获得10行,它也可以工作 Inserted 桌子。如果SQL语句插入10行,则您的触发器将 不是 被解雇十次 - 每行一次 - 但只有 一次 对于整个批次 - 您需要考虑到这一点!

第二点:我会尝试制作ID IDENTITY 字段 - 然后它们将始终获得一个价值 - 即使是“旧版”应用程序。那些“旧”应用程序应该提供一个旧密钥 - 因此您应该在那里没事。我看到的唯一问题,也不知道您如何处理这些问题是已经转换已转换的应用程序的插入 - 它们是否也提供了“老式”的旧密钥?如果没有 - 您需要多快才能拥有这样的钥匙?

我正在考虑的是“清理工作”,它将在桌子上运行,并用无效的遗产键获取所有行,然后为其提供一些有意义的价值。使其成为常规的存储程序,并在每天,四个小时30分钟的情况下执行它 - 无论您需求如何。然后,您不必处理触发器及其所拥有的所有限制。

是否可以使模式更改为“ BigBang”,而是可以在“隐藏”更改的表顶部创建视图?

我认为您可能会发现您只是将断点推迟到以后的时间点:“我们将一次贬低这一专栏的申请” - 这可能是我的天真,但我看不到这是怎么回事去工作。

当然,当不同的应用程序做不同的事情时,可能会发生更糟糕的混乱吗?

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top