使用INSTEAD OF INSERT触发器中的默认值

时间:2010-02-04 15:13:56

标签: sql sql-server triggers legacy default-value

我们正在执行到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提供了一个值,它就会在触发器中使用,但如果不是,则使用默认值。

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

3 个答案:

答案 0 :(得分:1)

一些想法:

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

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

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

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

答案 1 :(得分:1)

保罗:我已经解决了这个问题;最终。有点肮脏的解决方案,可能不是每个人的口味,但我是SQL Server的新手,如:

在Rather_of_INSERT触发器中:

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

    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. 将已插入的虚拟表中的记录复制到视图的基础表中(原始插入的位置,触发器未阻止此操作),加入临时表以提供默认值。这需要使用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. 删除临时表

答案 2 :(得分:0)

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

insert into realtable
values inserted.a, isnull(inserted.b, DEFAULT), computedC
from inserted