【问题标题】:Using default values in an INSTEAD OF INSERT trigger在 INSTEAD OF INSERT 触发器中使用默认值
【发布时间】:2011-01-13 03:08:56
【问题描述】:

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

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

【问题讨论】:

  • 知道所有具有默认值的字段都不能为空,这非常重要!如果我知道这些信息,我本可以为您提供解决方案。
  • 对不起 Emtucifor,我当时不知道我发布了我们在可空字段上没有任何默认值的问题。你的解决方案是什么?如果它很好,我仍然可以投票,也许会改变接受的答案!

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


【解决方案1】:

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

在 instead_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. 将 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. 删除临时表

【讨论】:

  • 快到了 - 你仍然无法区分(在步骤 5 中)因为未指定值(因此使用默认值)的 NULL 和因为 NULL 值而导致的 NULL已指定(因此不要使用默认值。但是,如果您有一个具有默认值的可空字段,这只是一个问题,我认为我们不会这样做,因此这个答案足以被接受。
  • 确实如此。这是一种相当复杂的方法,但可以在插入语句中需要进一步连接/处理的情况下工作。
【解决方案2】:

一些想法:

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

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

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

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

【讨论】:

  • 感谢这些 cmets,我无法指出任何特别有帮助的东西,但这让我们感觉不像是碰壁了!
【解决方案3】:

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

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

【讨论】:

  • 这将插入DEFAULT 值而不是真正的NULL
  • Incorrect syntax near the keyword 'DEFAULT'.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-26
  • 2021-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多