postgresql中的更新触发器后无法按预期工作

时间:2018-09-18 07:26:51

标签: postgresql plpgsql

在postgresql中创建以下触发器(以执行与以下代码中定义的sqlserver触发器相同的逻辑)

CREATE TABLE IF NOT EXISTS lookup_dbo.finlstatassetdesignation(
    finlstatassetdesignation CHAR(10) NOT NULL,
    finlstatassetdesignationdesc VARCHAR(50) NOT NULL,
    updoperation NUMERIC(5,0) NOT NULL DEFAULT (0),
    upddate TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT CLOCK_TIMESTAMP()
);
CREATE OR REPLACE FUNCTION TR_FinlStatAssetDesignation_U_TrFunc()
RETURNS TRIGGER LANGUAGE  plpgsql
   AS $$
   DECLARE
   AtDateTime  TIMESTAMP;
   SWV_error INTEGER;
   SWV_RowCount INTEGER;
BEGIN
   SWV_error := 0;
   GET DIAGNOSTICS SWV_RowCount = ROW_COUNT;
   IF (SWV_RowCount = 0) then 
      RETURN NULL;
   end if;                                  
   AtDateTime := LOCALTIMESTAMP;                                    

   if OLD.FinlStatAssetDesignation IS DISTINCT FROM NEW.FinlStatAssetDesignation then

      RAISE EXCEPTION 'Invalid attempt to update OID FinlStatAssetDesignation in FinlStatAssetDesignation';
      -- Rollback 
      RETURN NULL;
   end if;                                      

   if not OLD.UpdDate IS DISTINCT FROM NEW.UpdDate then

      SWV_error := 0;
      begin
         UPDATE lookup_dbo.finlstatassetdesignation
         SET UpdDate = AtDateTime
         WHERE a.FinlStatAssetDesignation = NEW.FinlStatAssetDesignation;
         EXCEPTION
         WHEN OTHERS
         THEN
            SWV_error := -1;
            RETURN NULL;
      end;
      if SWV_error <> 0 then

         -- RollBack 
         RETURN NULL;
      end if;

      SWV_error := 0;
   end if;

   RETURN NULL;
   END; $$;
CREATE Trigger tr_finlstatassetdesignation_u
    AFTER Update on lookup_dbo.finlstatassetdesignation FOR EACH ROW
    EXECUTE PROCEDURE lookup_dbo.tr_finlstatassetdesignation_u_trfunc();                                        

SQL Server原始触发代码:-

-- Add Update Trigger to FinlStatAssetDesignation                                       
CREATE Trigger TR_FinlStatAssetDesignation_U on FinlStatAssetDesignation for Update NOT FOR REPLICATION as                                      
    IF (@@RowCount = 0) return                                  
    DECLARE @AtDateTime datetime                                    

    SELECT @AtDateTime = GETDATE()                                  

if Update(FinlStatAssetDesignation)                                     
Begin                                       
    RaisError( 'Invalid attempt to update OID FinlStatAssetDesignation in FinlStatAssetDesignation', 16, 1 )                                    
    Rollback Tran                                   
    return                                  
end                                     


if not Update(UpdDate)                                      
begin                                       
    Update a                                    
    set UpdDate = @AtDateTime                                   
    from FinlStatAssetDesignation a, Inserted i                                 
    where a.FinlStatAssetDesignation = i.FinlStatAssetDesignation                                   
    if @@ERROR<>0                                   
    begin                                   
        RollBack tran                               
        return/* Execution stops here! */                               
    end                                 
end                                     
go  

在postgresql甚至原始sqlserver中转换的触发器有两个部分...对于第一个部分..似乎在postgresql中可以进行后期转换,但是第二部分似乎不起作用...请帮助

2 个答案:

答案 0 :(得分:2)

这是您的问题:

GET DIAGNOSTICS SWV_RowCount = ROW_COUNT;
IF (SWV_RowCount = 0) THEN 
   RETURN NULL;
END IF;

由于您是在函数开头执行此操作,并且函数中没有先前的SQL语句 ,因此该值将始终为零,并且触发器将立即退出。

您似乎假设ROW_COUNT将包含触发该函数的语句中修改的行数,但事实并非如此。它包含该函数本身中最后一个SQL语句修改的行数。

您只需删除此支票即可。触发函数将针对被修改的每一行进行调用,因此,如果未修改任何一行,则根本不会调用该函数。

最后,除非有充分的理由阻止在该函数上执行其他RETURN NEW;触发器,否则从触发器函数中AFTER UPDATE是一个好习惯。

答案 1 :(得分:1)

如果要阻止某些更新并且要更改更新(或插入)行的值,请不要使用AFTER触发器。使用BEFORE触发器,只需分配所需的值即可。另外,您不能真正在AFTER触发器中停止UPDATE。

在行级触发器中检查受影响的行数完全没有用。 如果触发了触发器,则该数字始终为 1

如果我正确理解了您的意图,则简化后的代码应为:

CREATE OR REPLACE FUNCTION tr_finlstatassetdesignation_u_trfunc()
   RETURNS TRIGGER 
   LANGUAGE plpgsql
AS 
$$
BEGIN
   if old.finlstatassetdesignation IS DISTINCT FROM new.finlstatassetdesignation then
      RAISE EXCEPTION 'Invalid attempt to update FinlStatAssetDesignation in FinlStatAssetDesignation';
      -- Rollback 
      RETURN NULL;
   end if;                                      

   if not old.upddate IS DISTINCT FROM new.upddate then
     new.upddate := clock_timestamp();  
   end if;

   -- this is important in a BEFORE trigger!
   RETURN new;
END
$$;

连同以下触发器定义:

CREATE Trigger tr_finlstatassetdesignation_u
    BEFORE Update on lookup_dbo.finlstatassetdesignation 
    FOR EACH ROW
    EXECUTE PROCEDURE lookup_dbo.tr_finlstatassetdesignation_u_trfunc();

在线示例:http://rextester.com/EWILW61724