2014-10-06 103 views
2

最后的编辑更新 - 解决 - 解决方案下面从SQL Server表变量

我不能相信,我不知道这一点,但显然的问题是,该表变量和真表都有同样的名字:@CHECKERS和dbo.CHECKERS。触发器显然使两者混淆。我通过改变触发器中插入和更新操作的顺序来发现这一点,以便在从表变量更新后插入到真实表中。突然之间,更新开始没有问题。

当然,我只创建了dbo.CHECKERS来调试@CHECKERS,所以很明显存在一些原来存在的问题,但无论如何,问题已经解决,触发器工作得很好。我在下面发布它,如果您愿意,随时提供任何反馈。我不是专家(很清楚),并且总是乐于接受建设性的反馈。

感谢大家的帮助和评论。我希望这篇文章可以帮助别人那里避免头痛...

查询 - 解决方案

ALTER TRIGGER CatchChange ON [dbo].[Orders] 

-- Last Updated : 07 Oct 2014 

AFTER UPDATE 
NOT FOR REPLICATION 
-- Trigger not fired by agent updates 
AS 
BEGIN 

    IF UPDATE(UpdateRecordDate) 
    -- Trigger fired only by user updates that include a timestamp. 
    -- Does **NOT** fire from primary stored procedure (dictated by UpdateRecordDate). 
    BEGIN 

     -- Table variable for storing **multi-row updates** 
     -- Using scalar variables will not work! Look it up. 
     DECLARE @CHECKERS AS TABLE 
     ( OrderNum INT, 
      Old_DCB INT, 
      New_DCB INT, 
      Old_MC INT, 
      New_MC INT, 
      Old_P2 NUMERIC(28,3), 
      New_P2 NUMERIC(28,3), 
      Old_PQ NUMERIC(28,3), 
      New_PQ NUMERIC(28,3), 
      Old_QT NUMERIC(28,3), 
      New_QT NUMERIC(28,3) 
     ) 

     -- Old & new values populated to table variable. 
     -- We can add a table in the same format as the variable & 
     -- then check what values were passed, if necessary. 
     -- The INSERT for this scenario is commented out at the end. 
     INSERT INTO @CHECKERS 
     SELECT I.OrderNo, 
       D.DriverCB, 
       I.DriverCB, 
       D.MCode, 
       I.MCode, 
       D.Price2, 
       I.Price2, 
       D.PriceQuantity2, 
       I.PriceQuantity2, 
       D.Quantity1, 
       I.Quantity1 
     FROM Inserted I 
     JOIN Deleted D ON I.Orderno = D.OrderNo 
     -- These checks are crucial, as they cut down on reiteration. 
     -- Without them, the program would fire the trigger redundantly. 
     -- Here we check for changes to the relevant values & 
     -- ensure that the update was a new one, not a repetitive one. 
     WHERE I.OrderKind = 0 
     AND isnull(D.UpdateRecordDate,convert(DATETIME,0)) <> isnull(I.UpdateRecordDate,convert(DATETIME,0)) 
     AND (D.DriverCB <> I.DriverCB 
      OR D.MCode <> I.MCode 
      OR D.Price2 <> I.Price2 
      OR D.PriceQuantity2 <> I.PriceQuantity2 
      OR D.Price3 <> I.Price3 
      OR D.PriceQuantity3 <> I.PriceQuantity3 
      OR D.Quantity1 <> I.Quantity1) 

     -- Case conditions are preferable in this instance to IF conditions. 
     -- All relevant values are checked here against dictating conditions & 
     -- updated accordingly. 
     UPDATE O 
     SET  
     O.DriverCB =  CASE 
          WHEN C.New_DCB = 0 
          THEN NULL 
          ELSE O.DriverCB 
          END, 
          <...>, 
     O.Quantity4 =  CASE 
          WHEN C.New_QT > 0 
          THEN O.Quantity1 
          ELSE O.Quantity4 
          END 
     FROM Orders O 
     INNER JOIN @CHECKERS C ON O.OrderNo = C.OrderNum 

     -- This is the INSERT which populates the debug table, if you want. 
     -- Naturally, the name here must match the name of the created table! 
     -- And yes, DD, you need to create the ACTUAL TABLE outside of this trigger. :) 
/*  INSERT INTO CheckMe 
     SELECT C.ID, 
     C.OrderNum, 
     C.Old_DriverCodeBizua, 
     C.New_DriverCodeBizua, 
     C.Old_MaslulCode1, 
     C.New_MaslulCode1, 
     C.Old_Price2, 
     C.New_Price2, 
     C.Old_PriceQuntity2, 
     C.New_PriceQuntity2, 
     C.Old_Quntity, 
     C.New_Quntity 
     FROM @CHECKERS C */ 
    END 
END 
GO 

结束问题 - 解决方案

--------开始原创帖子--------

我有一位客户,他需要我修饰一堆旧的触发器pe改变各种功能。一般来说,我不赞成这一点,并认为他们的计划应该更新 - 但无论如何。

存在三个触发器,我只需要将它们组合成一个单一的触发器,它可以更平滑地工作,并且有更多的条件。 (目前,完全缺乏条件使得在每一个操作运行的触发,这是观赏痛苦的缓慢。)

总之,我已经创建了我声明表变量(以下查询)一个新的触发。我这样做是为了直接控制列名,而不是简单地使用SELECT X INTO #temp。此后,我试图使用CASE检查来更新主表中的各个字段,这取决于我的表变量中记录的更改。

问题是更新没有做任何事情。我没有得到任何错误,但主表中的值保持不变。为了确保表变量获取值并且可以使用,我向测试数据库添加了一个新表,并且在触发器的每次触发时,我将当前包含在表变量中的值插入到新表中,如下所示:

 DECLARE @CHECKERS AS TABLE 
     ( OrderNum INT, 
      Old_DCB INT, 
      New_DCB INT, 
      Old_MC INT, 
      New_MC INT, 
      Old_QT NUMERIC(28,3), 
      New_QT NUMERIC(28,3) 
     ) 

     INSERT INTO @CHECKERS 
     SELECT I.OrderNo, 
       D.DriverCB, 
       I.DriverCB, 
       D.MCode, 
       I.MCode, 
       D.Quantity, 
       I.Quantity 
     FROM Inserted I 
     JOIN Deleted D ON I.OrderNo = D.OrderNo 
     WHERE I.OrderKind = 0 
     AND (D.DriverCB <> I.DriverCB 
      OR D.MCode1 <> I.MCode1 
      OR D.Quantity <> I.Quantity) 

     INSERT INTO CHECKERS 
     SELECT OrderNum, 
      Old_DCB, 
      New_DCB, 
      Old_MC, 
      New_MC, 
      Old_QT, 
      New_QT 
     FROM @CHECKERS 
     -- An actual table created to check functionality 
     -- Works up until this point 

     UPDATE Orders 
     SET Quantity2 = CASE 
       WHEN New_QT > 0 THEN New_QT 
       ELSE Quantity2 
       END 
     FROM @CHECKERS C 
     WHERE OrderNo = C.OrderNum 

当然,也有实际触发更多的更新,但你的想法。

任何想法为什么更新订单没有通过?可能是小小的,愚蠢的,尴尬的 - 但我再说一遍,);谢谢。

--------结束的原贴--------

- 编辑:

我也曾尝试写入更新为这样:

 UPDATE O 
     SET Quantity2 = CASE 
       WHEN C.New_QT > 0 THEN C.New_QT 
       ELSE Quantity2 
       END 
     FROM @CHECKERS C 
     INNER JOIN Orders O on O.OrderNo = C.OrderNum 

这种方法给出了相同的结果:没有错误消息(或任何其他消息,对于这个问题),并没有更新......

- 2第二编辑:

如果我用实际的表跳棋,而不是表变量@CHECKERS,更新通行证:

  UPDATE O 
      SET Quantity2 = CASE 
        WHEN C.New_QT > 0 THEN C.New_QT 
        ELSE Quantity2 
        END 
      FROM CHECKERS C -- Actual table, not a table variable! 
      INNER JOIN Orders O on O.OrderNo = C.OrderNum 

这也是在JOIN工程订单的存在,不管:

UPDATE Orders 
      SET Quantity2 = CASE 
        WHEN C.New_QT > 0 THEN C.New_QT 
        ELSE Quantity2 
        END 
      FROM CHECKERS C -- Actual table, not a table variable! 
      WHERE OrderNo = C.OrderNum 

现在唯一的问题是,在客户的数据库中,我不能添加真正的表CHECKERS。我将它添加到我的测试数据库中,只是为了澄清这些值是否传入表变量!这是我不熟悉的表变量的限制吗?我很难在联机文档中发现任何此类限制...

+0

如果您选择而不是更新查询,您是否看到应该更新的行? – 2014-10-06 10:02:15

+0

你在'UPDATE'语句后看到什么信息?也许'更新0'?或者也许有些不同? – 2014-10-06 10:12:14

+0

@DanBracuk是的,如果我切换到select语句,我会看到要更新的行。 – 2014-10-06 10:16:07

回答

0

您需要在FROM语句中将订单添加为INNER JOIN。 试试这个:

​​
+0

我很高兴你指出了这一点,但不幸的是,这是触发器的原始结构。我已更新该帖子以表明这一点。即使使用INNER JOIN(或任何其他JOIN,甚至包括完整的OUTER JOIN),更新仍然不会影响订单。尽管如此,我更喜欢显式连接到隐式连接,所以非常感谢你的回应。 – 2014-10-06 12:01:57

0

表变量(@CHECKERS)和实际表(dbo.CHECKERS)具有相同的名称。触发器让他们感到困惑。通过改变触发器中的操作顺序,这变得很明显。然后我改变了实际桌子的名字,所有的事情都开始顺利进行。

我不知道这个关于表变量的信息,也没有在搜索解决方案的几个小时内在网上找到它。

有关一般信息:此触发器是在Microsoft SQL Server 2005中创建的。也许问题已解决,或许这是一种侥幸。无论哪种方式,将dbo.CHECKERS的名称更改为dbo.CHECKME解决了我的问题。