从NULL更新值时,SQL Server AFTER UPDATE触发器不起作用

时间:2014-07-10 09:15:56

标签: triggers sql-server-2008-r2 null

由于理由太愚蠢无法解释我在表中有三列保存日期和时间值。一列仅保存日期,第二列仅保留时间,第三列保留DATETIME值。看起来像这样:

OPPORTUNITYID | ... | ProductionDate | ProductionTime | PRODUCTIONDATETIME
-------------------------------------------------------------------------------
091798-324971 | ... | 12-07-2014     | 11:30 AM       | 2014-07-12 11:30:00:000

然后我有一个触发器,无论哪个值正在更新,这些值都保持同步。 这是触发器的一部分:

CREATE TRIGGER [dbo].[TBL_OPPORTUNITY_DUEDATES_TRU]
ON  [dbo].[TBL_OPPORTUNITY]
AFTER UPDATE
AS

BEGIN
    SET NOCOUNT ON;

    IF UPDATE ( PRODUCTIONDATETIME )
    BEGIN

        UPDATE TBL_OPPORTUNITY
        SET ProductionDate = CONVERT(VARCHAR(10), PRODUCTIONDATETIME, 105)
        , ProductionTime = REPLACE(REPLACE(RIGHT('0'+LTRIM(RIGHT(CONVERT(VARCHAR, PRODUCTIONDATETIME,100), 7)), 7), 'AM', ' AM'), 'PM', ' PM')
        WHERE OPPORTUNITYID IN ( SELECT i.OPPORTUNITYID FROM inserted i
            INNER JOIN deleted d ON i.OPPORTUNITYID = d.OPPORTUNITYID
            WHERE NOT i.PRODUCTIONDATETIME = d.PRODUCTIONDATETIME
            AND NOT ( i.PRODUCTIONDATETIME = '' OR i.PRODUCTIONDATETIME IS NULL )
        );
    END

    IF ( UPDATE ( ProductionDate ) OR UPDATE ( ProductionTime ) )
    BEGIN

        UPDATE TBL_OPPORTUNITY
        SET PRODUCTIONDATETIME = CONVERT(DATETIME, ProductionDate + ' ' + ProductionTime, 105)
        WHERE OPPORTUNITYID IN ( SELECT i.OPPORTUNITYID FROM inserted i
            INNER JOIN deleted d ON i.OPPORTUNITYID = d.OPPORTUNITYID
            WHERE ( NOT i.ProductionDate = d.ProductionDate
                OR NOT i.ProductionTime = d.ProductionTime )
            AND NOT ( i.ProductionDate = '' OR i.ProductionDate IS NULL )
            AND NOT ( i.ProductionTime = '' OR i.ProductionTime IS NULL )
        );

        UPDATE TBL_OPPORTUNITY
        SET PRODUCTIONDATETIME = CONVERT(DATETIME, ProductionDate + ' 12:00:00', 105)
        WHERE OPPORTUNITYID IN ( SELECT i.OPPORTUNITYID FROM inserted i
            INNER JOIN deleted d ON i.OPPORTUNITYID = d.OPPORTUNITYID
            WHERE ( NOT i.ProductionDate = d.ProductionDate
                OR NOT i.ProductionTime = d.ProductionTime )
            AND NOT ( i.ProductionDate = '' OR i.ProductionDate IS NULL )
            AND ( i.ProductionTime = '' OR i.ProductionTime IS NULL )
        );

    END

END
GO

每当更新任何值时,触发器都会按预期工作。但是,如果正在从NULL更新某个值,或者换句话说,列中的旧值为NULL,则触发器会失败(如不进行任何更改)值是,例如' 02-03-2014'。

为什么?

服务器是Microsoft SQL Server 2008 R2。

感谢您提供任何线索。

2 个答案:

答案 0 :(得分:1)

在编写时,触发器必须忽略旧值为NULL的行,因为涉及NULL的相等或不等式永远不能求值为true。如果d.ProductionDate为NULL,那么诸如NOT i.ProductionDate = d.ProductionDate之类的WHERE条件永远不会返回任何行,无论i.ProductionDate是什么。您需要明确检查d.ProductionDate IS NULL处理d.ProductionDate没有值的情况的可能性。

答案 1 :(得分:0)

为您要跟踪的每个列执行此操作

    DECLARE @ProductionDate Date;     
    DECLARE @ProductionDateOld Date;
    DECLARE @ProductionDateInd bit = 0;  // you can use the indicator later to determine which field changed. if = 0 it didn't change, if 1 it did change
    DECLARE @ProductionDateTime DateTime;     
    DECLARE @ProductionDateTimeOld DateTime;
    DECLARE @ProductionDateTimeInd bit = 0;

    SELECT @ProductionDate=ProductionDate FROM inserted i;
    SELECT @ProductionDateOld = d.ProductionDate FROM deleted d;
    if @ProductionDateOld <> @ProductionDate 
        @ProductionDateInd = 1;
    if @ProductionDateOld IS NULL AND @ProductionDate IS NOT NULL 
        @ProductionDateInd = 1; 

SELECT @ProductionDateTime=ProductionDateTime FROM inserted i;
SELECT @ProductionDateTimeOld = d.ProductionDateTime FROM deleted d;
if @ProductionDateTimeOld <> @ProductionDateTime 
  @ProductionDateTimeInd = 1;
if @ProductionDateTimeOld IS NULL AND @ProductionDateTime IS NOT NULL 
   @ProductionDateTimeInd = 1; 

然后检查值是否已更改(如果确实存在更改,我这样做只会写入历史/审计表)

if (@ProductionDateInd = 1 OR
@ProductionDateInd = 1)

INSERT INTO TBL_OPPORTUNITY_DUEDATES_TRU (
ProductionDate,
ProductionDateInd,
ProductionDateTime,
ProductionDateTimeInd,
.
.
.
)

VALUES (
@ProductionDate,
@ProductionDateInd,
@ProductionDateTime,
@ProductionDateTimeInd,
.
.
.
)