【问题标题】:Table not updated by trigger and procedure表未由触发器和过程更新
【发布时间】:2016-06-17 13:25:21
【问题描述】:

我有一个人们在上面下订单的系统,每个订单都有操作,并且存在一个名为 cm_ord_order_action 的表。有时操作会失败,所以我需要创建一个触发器来获取失败的订单操作的信息并填充一个名为 cm_ord_failed_order 的表。

触发器如下图:

CREATE OR REPLACE TRIGGER CM.TRGID_CM_ORD_FAILED_ORDER
AFTER UPDATE ON CM.CM_ORD_ORDER_ACTION
FOR EACH ROW
BEGIN 
   IF (:new.STATUS = 'FA') THEN
        CM.CM_FAILED_ORDER_MLT(:new.order_unit_id, :new.order_id, :new.action_type);
   END IF;
END;
/

此触发器将参数传递给更新表的过程:

CREATE OR REPLACE PROCEDURE CM_FAILED_ORDER_MLT(
v_order_unit_id NUMBER,
v_order_id in NUMBER,
v_action_type in VARCHAR)

AS
v_lob varchar(100);
v_step varchar(100);
v_error varchar(200);

BEGIN

SELECT
    ITEM.LOB_NAME, ST.STEP_NAME, ASS.STEP_ERROR
    INTO v_lob, v_step, v_error           
FROM
    CM.CM_ORD_ORDER_ACTION OA 
    INNER JOIN CM.CM_ORD_ASSIGNMENTS ASS 
        ON OA.ORDER_UNIT_ID = ASS.ORDER_ACTION_ID 
    INNER JOIN CM.CM_ORD_PROCESS_STEP ST
        ON ST.ORD_PROCESS_STEP_ID = ASS.STEP_ID 
    INNER JOIN CM.CM_ORD_AP_ITEM ITEM
        ON ITEM.AP_SUBSCRIBER_ID = OA.AP_SUBSCRIBER_ID
WHERE ASS.COMPLETION_STATUS = 'FA'
AND OA.ORDER_ID = v_order_id
AND OA.ORDER_UNIT_ID = v_order_unit_id
GROUP BY OA.ORDER_UNIT_ID, ITEM.LOB_NAME, ST.STEP_NAME, ASS.STEP_ERROR;

INSERT INTO CM_ORD_FAILED_ORDER (ORDER_ID, FAILED_DATE, ORDER_ACTION_ID, ACTION_TYPE, LOB, STEP, ERROR)
  VALUES (v_order_id, sysdate, v_order_unit_id, v_action_type, v_lob, v_step, v_error);

END CM_FAILED_ORDER_MLT;
/

这里可能有问题,因为: A - 即使触发器是在 cm_ord_order_action 上更新后,当触发器被启用时,状态不会被更新,但是当我禁用触发器时,状态会被更新。

B - 表 cm_ord_failed_order 未填充信息。

提前致谢。

【问题讨论】:

  • 为什么不使用更新前的?
  • 如何执行更新?该过程是否会引发触发器传递的异常(未找到数据?),但这可能会被正在执行更新的任何事物压扁/忽略?您是否尝试过手动更新以查看发生了什么或进行任何调试?
  • 我的猜测是您的触发器会引发错误。我的猜测是这是一个变异表错误。那是你实际看到的吗?您是否有一些代码正在吞噬这些错误?我希望您希望从您的过程中删除对CM_ORD_ORDER_ACTION 的引用——您需要从该表中传递您需要从触发器中获取的所有值。
  • @AlexPoole 正在通过脚本执行更新,一旦订单失败,该脚本会将 cm_ord_order_action 中的状态更新为“FA”。我没有收到任何错误,触发器只是未能将 cm_ord_order_action 状态更新为“FA”并且未能更新 cm_ord_failed_order。
  • @Iommiund - 该过程本身可能很好,但是当从触发器调用该过程时,它执行的查询将得到一个变异表错误,因为它引用了触发器所针对的表.如果您没有看到该错误,那么您的脚本中的某些内容正在捕获并忽略它,或者使用 when others 处理程序,或者您的脚本可能正在丢弃输出。

标签: sql oracle triggers procedure


【解决方案1】:

您可以通过直接在触发器中执行插入操作来避免脚本以某种方式忽略或丢弃的变异表错误,您可以在 :NEW 伪记录中获得正在更新的行的详细信息,而不必查询它再次。您也可以在不需要局部变量的情况下执行insert...select

我认为这是一个粗略的翻译:

CREATE OR REPLACE TRIGGER CM.TRGID_CM_ORD_FAILED_ORDER
AFTER UPDATE ON CM.CM_ORD_ORDER_ACTION
FOR EACH ROW
WHEN (new.STATUS = 'FA')
BEGIN 

  INSERT INTO CM_ORD_FAILED_ORDER (ORDER_ID, FAILED_DATE, ORDER_ACTION_ID, ACTION_TYPE,
    LOB, STEP, ERROR)
  SELECT
    DISTINCT :new.ORDER_ID, sysdate, :new.Order_Unit_Id, :new.Action_Type,
      ITEM.LOB_NAME, ST.STEP_NAME, ASS.STEP_ERROR
  FROM
    CM.CM_ORD_ASSIGNMENTS ASS
    INNER JOIN CM.CM_ORD_PROCESS_STEP ST
        ON ST.ORD_PROCESS_STEP_ID = ASS.STEP_ID 
    CROSS JOIN CM.CM_ORD_AP_ITEM ITEM
  WHERE ASS.ORDER_ACTION_ID = :new.ORDER_UNIT_ID
  AND ASS.COMPLETION_STATUS = :new.STATUS
  AND ITEM.AP_SUBSCRIBER_ID = :new.AP_SUBSCRIBER_ID;

END CM_FAILED_ORDER_MLT;
/

DISTINCT(而不是分组)和CROSS JOIN 表明您在原始查询中缺少连接条件,但您的表结构和数据可能并非如此。

或者,您可以将插入保留在一个过程中,但将 :newAP_SUBSCRIBER_ID ` 作为另一个参数传递,因为这似乎是您需要的变异表中唯一尚未传入的列。

您的触发器也可以是BEFORE UPDATE 而不是AFTER UPDATE

【讨论】:

  • 我猜测 OP 首先在查询中包含 cm.cm_ord_order_action 表的原因是因为 cm_ord_ap_item 未加入该查询中的任何其他表。然而,给猫剥皮的方法不止一种! *{;-)
【解决方案2】:

避免交叉连接需要的 Alex 解决方案的替代方案是将过程更改为:

create or replace procedure cm_failed_order_mlt (v_order_unit_id number,
                                                 v_order_id in number,
                                                 v_action_type in varchar,
                                                 v_ap_subscriber_id in cm.cm_ord_order_action.ap_subscriber_id%type)

as
  v_lob varchar(100);
  v_step varchar(100);
  v_error varchar(200);
begin
  select distinct lob_name
  into   v_lob
  from   cm.cm_ord_ap_item
  where ap_subscriber_id = v_ap_subscriber_id;

  select distinct st.step_name, ass.step_error
  into   v_step, v_error           
  from   cm.cm_ord_assignments ass
         inner join cm.cm_ord_process_step st on st.ord_process_step_id = ass.step_id
  where  ass.completion_status = 'FA'
  and    ass.order_action_id = v_order_id
  and    oa.order_unit_id = v_order_unit_id;

  insert into cm_ord_failed_order (order_id, failed_date, order_action_id, action_type, lob, step, error)
    values (v_order_id, sysdate, v_order_unit_id, v_action_type, v_lob, v_step, v_error);

end cm_failed_order_mlt;
/

或者,要删除 Alex 解决方案中的交叉连接,只需将其替换为标量子查询,例如:

select (select distinct lob_name from cm.cm_ord_ap_item where ap_subscriber_id = v_ap_subscriber_id), ...

【讨论】:

  • 怀疑第一个查询将获得多行,这就是原始查询需要有一个 group-by 子句的原因 - 所以我在那里留下了 distinct .但是如果没有表格/数据,我会疯狂地推测。分组可能是多余的,或者重复的可能来自其他地方......
  • 啊,是的,好点。我也会在该查询中添加一个 distinct - 我假设从主查询中最多返回 1 行。而且,现在您提到它,我也会将第二个查询中的 group by 切换为 distinct。
【解决方案3】:

就像@JustinCave 说的,很明显你有变异表error

当我们尝试引用 从行级触发代码中触发查询中的表

CM_ORD_ORDER_ACTION 的触发器上,您正在从同一个表中进行选择。尝试在不引用CM_ORD_ORDER_ACTION 的情况下重做过程中的查询。

【讨论】:

    猜你喜欢
    • 2017-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-14
    • 2019-03-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多