SQL更新触发器,它将旧值插入审核表中,并且仅当两个特定的列正在更新时才触发,否则返回错误

时间:2018-07-06 16:49:17

标签: sql sql-server triggers sql-server-2016

在客户表中更新它们时,我需要记录所有以前的地址和邮政编码

业务要求:不更改邮政编码也不能更改地址,反之亦然。

需要一种防止这种情况的机制以及相应的错误消息。

我已经创建了表格:

create table tblCustomerAudit
(
CustomerID int identity(1,1) not null,
CustomerName nvarchar(255) null,
CustomerAddress nvarchar(255) null,
CustomerPostcode nvarchar(255) null,
CardNumber nvarchar(255) null,
)
go

alter table tblCustomerAudit
add constraint FK_CustomerAudit
foreign key(CustomerID)
references CstmrEng.tblCustomer(CustomerID)

什么会触发?请帮忙!

3 个答案:

答案 0 :(得分:0)

也许您可以使用存储过程来做到这一点?请注意,存储过程不是魔术般的尘土,维护它们可能是一场噩梦。

您可以让触发器调用存储过程,该存储过程处理CustomerAddress和CustomerPostcode之间的特定约束。

此代码未经测试,可能无法正常工作。

CREATE PROCEDURE UpdateCustomerAddressAndPostcode @CustomerID INT, @CustomerAddress NVARCHAR(255), @CustomerPostcode NVARCHAR(255)
AS BEGIN

IF (@CustomerAddress IS NULL OR @CustomerAddress = '')
BEGIN
    PRINT 'Customer address must be present to modify Customer table.';
    THROW;
END

IF (@CustomerPostcode IS NULL OR @CustomerPostcode = '')
BEGIN
    PRINT 'Customer postcode must be present to modify Customer table.';
    THROW;
END

INSERT INTO tblCustomerAudit (CustomerID, CustomerName, CustomerAddress, CustomerPostcode, CardNumber)
    SELECT CustomerID, CustomerName, CustomerAddress, CustomerPostcode, CardNumber FROM Customer where CustomerID = @CustomerID;

-- just printing the table for example
SELECT * FROM tblCustomerAudit

-- make the change to Customer here or in the trigger

END
GO


CREATE TRIGGER CustomerTrigger 
   ON  [dbo].[Customer] 
   INSTEAD OF INSERT
AS 
DECLARE @CustomerID int
DECLARE @CustomerAddress nvarchar(255)
DECLARE @CustomerPostcode nvarchar(255)

BEGIN
    SET NOCOUNT ON;

    -- verify the data
    EXEC UpdateCustomerAddressAndPostcode @CustomerID, @CustomerAddress, @CustomerPostcode

    -- do the Customer change here or in the stored procedure

END

答案 1 :(得分:0)

我真的建议使用存储过程来控制更新,插入和删除,因此您将使用存储过程将值传递到表之前。如果使用触发器,则将实际更改值,然后将触发该触发器,如果​​满足条件,则必须从触发器中使用旧值重新更新表。因此,这对我来说是一种冗余,这就是为什么我建议使用存储过程在更改表值之前处理所有问题的原因。

无论如何,您仍然可以使用具有删除和插入表功能的触发器:

CREATE TRIGGER CustomerUpdate ON   tblCustomerAudit
   FOR UPDATE
AS 
BEGIN
    SET NOCOUNT ON;
  DECLARE 
        @CustomerID             INT
    ,   @New_CustomerAddress    nvarchar(255)
    ,   @New_CustomerPostcode   nvarchar(255)
    ,   @Old_CustomerAddress    nvarchar(255)
    ,   @Old_CustomerPostcode   nvarchar(255)

    SELECT 
        @CustomerID             = CustomerID 
    ,   @Old_CustomerAddress    = CustomerAddress
    ,   @Old_CustomerPostcode   = CustomerPostcode
    FROM 
        deleted

    SELECT 
        @New_CustomerAddress    =   CustomerAddress
    ,   @New_CustomerPostcode   =   CustomerPostcode
    FROM
        tblCustomerAudit 
    WHERE 
        CustomerID = @CustomerID


    IF @Old_CustomerAddress = @New_CustomerAddress OR @New_CustomerPostcode = @Old_CustomerPostcode 
    BEGIN 
        -- IF one of them matches return the old values
        UPDATE tblCustomerAudit
        SET 
            CustomerAddress  = @Old_CustomerAddress
        ,   CustomerPostcode = @Old_CustomerPostcode
        WHERE 
            CustomerID = @CustomerID

        -- display an error message 
        RAISERROR( 'You need to change both address and postcode to save the new values', 18 , 0);

    END






END

答案 2 :(得分:0)

类似这样的东西:

CREATE TRIGGER foo.bar ON foo.mytable
AFTER UPDATE  
AS  
IF (@@ROWCOUNT_BIG  = 0)
RETURN;
IF EXISTS (SELECT *  
           FROM foo.mytable AS t
           JOIN inserted AS i   
           ON t.mykey = i.mykey
           WHERE i.addr <> t.addr AND i.post = t.post -- address changes but post code doesn't
              OR i.post <> t.post AND i.addr = t.addr -- post code changes by address doesn't
          )  
BEGIN  
RAISERROR ('invalid changes', 16, 1);  
ROLLBACK TRANSACTION;  
RETURN   
END;  

GO