7

我们正在执行向 SQL Server 的数据库迁移,为了支持旧版应用程序,我们在 SQL Server 表上定义了视图,这些视图按照旧版应用程序的预期呈现数据。

但是,当字段可能具有默认值时,我们现在遇到了在这些视图上定义的 INSTEAD OF INSERT 触发器的问题。

我会试着举个例子。

数据库中的一个表有 3 个字段,a、b 和 c。c 是全新的,旧版应用程序不知道它,所以我们还有一个包含 2 个字段的视图,a 和 b。

当遗留应用程序尝试在其视图中插入一个值时,我们使用 INSTEAD OF INSERT 触发器来查找应该进入字段 c 的值,如下所示:

INSERT INTO realTable(a, b, c) SELECT Inserted.a, Inserted.b, Calculated.C FROM...

(查找的详细信息不相关。)

此触发器运行良好,除非字段 b 具有默认值。这是因为如果查询

INSERT INTO legacyView(a) VALUES (123)

被执行,那么在触发器中,Inserted.b 为NULL,不是b 的默认值。现在我有一个问题,因为我无法区分上面的查询,它将默认值放入 b,并且:

INSERT INTO legacyView(a,b) VALUES (123, NULL)

即使 b 是非 NULLABLE 的,我也不知道如何在触发器中编写 INSERT 查询,这样如果为 b 提供了一个值,它将在触发器中使用,但如果不是,则使用默认值。

编辑:补充说我宁愿不复制触发器中的默认值。默认值已经在数据库模式中,我希望我可以直接使用它们。

4

3 回答 3

1

一些想法:

  • 如果遗留应用程序正在为 INSERT 指定列列表,并且命名列而不是使用 SELECT *,那么您不能将默认值绑定到列 c 并让应用程序使用您的原始(修改)表吗?

  • 如果有任何方法可以使旧版应用程序对其 INSERT 使用与 SELECT 或 DELETE 不同的视图或表,则可以将所需的默认值放在该表上并使用常规后触发器将新列移动到真正的桌子。

  • 如何不理会原始表并将其他列添加到与原始表具有 1-1 关系的单独表中?然后创建一个组合这两个表的视图,并在这个新视图上放置适当的替代触发器,以处理跨两个表拆分的所有数据操作。我意识到这会影响性能,但这可能是解决问题的唯一方法。这对于物化视图来说是一个理想的情况,它会减慢更新速度,但会使结果的执行与读取表完全一样。(物化视图最适合内部连接并且不需要聚合。它们还在源表上放置模式锁。)

  • 我遇到了类似的问题,我无法区分视图上的而不是 UPDATE 触发器中故意 NULL 值和跳过的列之间的区别。我最终在视图上创建了一个代替 INSERT 触发器以将插入转换为更新(如果密钥已经存在,则它是一个更新,否则它是一个插入)。虽然这不会直接帮助您,但它可能会激发您或其他人的一些想法。

于 2010-02-04T22:19:33.803 回答
1

保罗:我已经解决了这个问题;最终。有点肮脏的解决方案,可能不符合每个人的口味,但我对 SQL Server 很陌生,例如:

在 instead_of_INSERT 触发器中:

  1. 将 Inserted 虚拟表的数据结构复制到临时表中:

    SELECT * INTO aTempInserted FROM Inserted WHERE 1=2
    
  2. 创建一个视图以确定视图基础表(来自系统表)的默认约束,并使用它们来构建将复制临时表中的约束的语句:

    SELECT  'ALTER TABLE dbo.aTempInserted
                   ADD CONSTRAINT ' + dc.name + 'Temp' +
                   ' DEFAULT(' + dc.definition + ') 
                   FOR ' + c.name AS Cmd, OBJECT_NAME(c.object_id) AS Name
      FROM  sys.default_constraints AS dc
     INNER  JOIN sys.columns AS c
              ON dc.parent_object_id = c.object_id 
             AND dc.parent_column_id = c.column_id
    
  3. 使用游标遍历检索到的集合并执行每个语句。这将为您留下一个与要插入的表具有相同默认值的临时表。

  4. 将默认记录插入临时表(所有字段都可以为空,因为从插入的虚拟表创建):

    INSERT INTO aTempInserted DEFAULT VALUES
    
  5. 将 Inserted 虚拟表中的记录复制到视图的基础表中(如果触发器没有阻止,它们最初会被插入其中),加入临时表以提供默认值。这需要使用 COALESCE 函数,以便仅默认未提供的值:

    INSERT INTO realTable([a], [b], 
                SELECT COALESCE(I.[a], T.[a]),
                       COALESCE(I.[a], T.[b])
                FROM   Inserted      AS I,
                       aTempInserted AS T
    
  6. 删除临时表

于 2010-02-08T10:39:09.923 回答
0

使用这样的东西怎么样???:

insert into realtable
values inserted.a, isnull(inserted.b, DEFAULT), computedC
from inserted
于 2011-12-15T17:02:10.467 回答