【问题标题】:SQL Server triggers "For Each Row" equivalentSQL Server 触发“For Each Row”等效项
【发布时间】:2009-08-09 20:10:03
【问题描述】:

当另一个表中的字段发生更改并且我想使用触发器时,我需要更新 Parts 表中的多行。触发的原因是许多现有应用程序使用和修改数据,而我无权访问所有这些数据。我知道有些数据库在触发器语句中支持 For Each Row,但我认为 Microsoft 不支持。

具体来说,我有两张表 Parts 和 Categories。

Parts 包含 Part#、Category_ID、Part_Name 和 Original 以及许多其他内容

Category 有 Category_ID 和 Category_name。

Original 是由“:”分隔的 Category_Name 和 Part_Name 的串联

例如手链:BB129090

如果有人更改 Category_Name(例如从 Bracelets 更改为 Bracelets),则必须在 Parts 表的每一行中更新 Original 字段。虽然这是一个罕见的事件,但它的发生足以引起麻烦。

没有 Web 和桌面应用程序使用 Original

所有会计应用程序仅使用原件

我的任务是让会计和其他应用程序保持同步。

我没有设计数据库,编写会计程序的公司不会更改它。

【问题讨论】:

    标签: sql sql-server triggers


    【解决方案1】:

    或者另一种选择:您为什么不为您的会计部门创建这两个表的视图,其中包含此连接列:

    CREATE VIEW dbo.AccountingView
    AS
      SELECT
        p.PartNo, p.Part_Name, p.Category_ID,
        c.Category_Name + ':' + p.PartName as 'Original'
      FROM
        Parts p
      INNER JOIN
        Category c ON p.Category_ID = c.Category_ID
    

    现在您的会计人员可以使用此视图进行报告,它始终是最新的,始终是最新的,您不必担心更新和插入触发器以及所有那些棘手的事情.....

    马克

    【讨论】:

      【解决方案2】:

      原始列违反了 1NF,这是一个非常糟糕的主意。你可以

      1. 完全跳过该列并将其连接到每个查询中(可能不是最佳解决方案,但我认为它可能比触发器更好)。
      2. 在表上创建一个视图并在视图中包含原始列(可能我会这样做),或者
      3. 将 Original 设为计算列,如果要在其上创建索引,这是最好的方法。

      【讨论】:

      • 取决于您对 1NF 的定义 - 我的是“在单个字段中不包含多个值”,这未经验证,IMO
      • 我的是“单个字段不包含多个值”和“多个字段不包含相同信息”。
      【解决方案3】:

      我猜你的情况不需要行级触发器。

      你可以这样做

      IF UPDATE(Category_Name)
          UPDATE Parts
          SET Original = inserted.Category_Name + ':' + Part_Name
          FROM Parts
          INNER JOIN inserted ON Parts.Category_ID = inserted.Category_ID
      

      作为 Category 表上的 UPDATE 触发器。

      如果您确实需要逐行处理(例如,存储过程),则需要插入 CURSOR 或 WHILE 循环。

      【讨论】:

      • IF UPDATE(Original) 永远不会起作用 - “Original” 列永远不会更新,对吧?可能会更新 Category_Name 列或 Part_Name 列,然后触发“原始”列的更新
      • 另外,您还需要在 Parts 表上触发,以防“Part_name”列发生变化 - 仅在 Category 表上触发是不够的
      • 固定 Category_Name。您的问题集中在 Category_Name,所以我为 Category_Name 绘制了解决方案。当然,您还需要在 Parts 上的更新触发器中使用类似的功能
      【解决方案4】:

      如果您可以更改表架构,那么您可以选择确保Original 列始终是最新的,无论如何,将Original 设为计算列 - 计算列根据需要从 Category_Name 加上 Part_Name。

      为此,您需要创建一个存储函数来为您执行该计算 - 如下所示:

      CREATE FUNCTION dbo.CreateOriginal(@Category_ID INT, @Part_Name VARCHAR(50))
      RETURNS VARCHAR(50)
      WITH SCHEMABINDING
      AS BEGIN
        DECLARE @Category_Name VARCHAR(50)
      
        SELECT @Category_Name = Category_Name FROM dbo.Category 
               WHERE Category_ID = @Category_ID
      
        RETURN @Category_Name + ': ' + @Part_Name
      END
      

      然后您需要在Parts 表中添加一列,该列将显示该函数对表中每一行的结果:

      ALTER TABLE Parts
        ADD Original AS dbo.CreateOriginal(Category_ID, Part_Name) 
      

      主要缺点是要显示列值,必须每次针对每一行调用该函数。

      另一方面,无论如何,您的数据始终是最新的,并且始终保证是正确的。也不需要触发器。

      看看这是否适合您 - 根据您的需求和您拥有的数据量,它可能对您来说效果很好。

      马克

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-08-03
        • 2021-02-21
        • 1970-01-01
        • 2013-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-12
        相关资源
        最近更新 更多