【问题标题】:How to know the type of :new in a trigger?如何知道触发器中 :new 的类型?
【发布时间】:2021-03-30 00:09:24
【问题描述】:

假设我有这些虚拟类型和这些类型的表:

create or replace type Tariff_Plan_TY as object(
  tariff_id INTEGER,
  call_price DECIMAL(3,2),
  sms_price DECIMAL(3,2),
)FINAL;

create or replace type Contract_TY as object(
  telephone_no VARCHAR(10),
  tariff_plan REF Tariff_Plan_TY
)NOT FINAL;

create or replace
type Operation_TY as object(
  id INTEGER,
  contract REF Contract_TY
)NOT FINAL;

我们还假设我有不同类型的操作:

create or replace type Call_TY UNDER Operation_TY(
  called_no VARCHAR(10),
  end_time TIMESTAMP WITH LOCAL TIME ZONE
)FINAL;

create or replace type SMS_TY UNDER Operation_TY(
  receiver_no VARCHAR(10)
)FINAL;

我想在Operation_TY 的表上创建一个触发器,它会告诉我操作成本是多少。 为了得到这些信息,当一个操作被插入到表中时,我想检查该操作的类型,然后进入合约的tariff_plan 找到该操作的价格。

我试过了:

create or replace
TRIGGER CHECK_OPERATION
BEFORE INSERT ON OPERATION_TB
FOR EACH ROW
DECLARE
  contract contract_ty;
  tariff_plan tariff_plan_ty;
  operation ref operation_ty;
BEGIN
  operation = ref :new;
  SELECT deref(:new.contract) into contract from dual;
  SELECT deref(contract.tariff_plan) into tariff_plan from dual;

  if (operation is of (sms_ty)) then
    NULL; -- go search the price
  elsif (operation is of (call_ty)) then
    NULL; -- go search the price
  else
    NULL;
  end if;
END;

这不起作用,我想我错过了一些东西。这个操作应该如何进行?

我在这里留下一个小脚本来随机填充表格:

create table Contract_TB of Contract_TY;
/
create table Tariff_Plan_TB of Tariff_Plan_TY;
/
create table Operation_TB of Operation_TY;
/
create or replace PROCEDURE POPULATE_TARIFF_PLAN AS
idx INTEGER;  
BEGIN 
  idx := 0;
  LOOP 
    INSERT INTO tariff_plan_tb
    VALUES (
      idx,
      DBMS_RANDOM.VALUE,
      DBMS_RANDOM.VALUE
      ); 
  idx := idx + 1;
  EXIT WHEN idx = 10;
  end loop;
END POPULATE_TARIFF_PLAN;
/
create or replace
PROCEDURE populate_contract AS
idx INTEGER;
phone_no VARCHAR(10);
ref_tariff_plan REF Tariff_Plan_TY;
BEGIN
  idx := 0;
  SELECT TO_CHAR(
    TRUNC(DBMS_RANDOM.VALUE(1000000000, 9999999999))
  ) INTO phone_no FROM DUAL;

  SELECT * INTO ref_tariff_plan FROM(
    SELECT REF(T)
    FROM tariff_plan_tb T
    ORDER BY DBMS_RANDOM.VALUE
  ) WHERE rownum <= 1;

  INSERT INTO contract_tb
  VALUES (phone_no,
        ref_tariff_plan);
  idx := idx + 1;
  EXIT WHEN idx = 500;
  END LOOP;
END;
/

create or replace
procedure populate_operation as
idx NUMBER;
ref_contract ref Contract_TY;
begin
idx := 0;
loop 
  SELECT * into ref_contract FROM(SELECT ref(ct) FROM CONTRACT_TB ct ORDER BY dbms_random.value) WHERE rownum <= 1;
  INSERT INTO OPERATION_TB values(
      SMS_TY(
        idx,
        ref_contract
      ));
      idx := idx + 1;
    exit when idx = 1000;
end loop;
end;
/

【问题讨论】:

    标签: oracle plsql oracle11g triggers oracle-objects


    【解决方案1】:

    使用OBJECT_VALUE pseudo-column 获取行对象。

    CREATE TRIGGER CHECK_OPERATION
    BEFORE INSERT ON OPERATION_TB
    FOR EACH ROW
    DECLARE
      v_contract    contract_ty;
      v_tariff_plan tariff_plan_ty;
    BEGIN
      SELECT DEREF( :NEW.contract )
      INTO   v_contract
      FROM   DUAL;
    
      SELECT DEREF( v_contract.tariff_plan )
      INTO   v_tariff_plan
      FROM   DUAL;
    
      IF :NEW.OBJECT_VALUE IS OF ( SMS_TY ) THEN
        INSERT INTO log_what_happened ( id, type, price )
          VALUES ( :NEW.ID, 'SMS', v_tariff_plan.sms_price );
      ELSIF :NEW.OBJECT_VALUE IS OF ( CALL_TY ) THEN
        INSERT INTO log_what_happened ( id, type, price )
          VALUES ( :NEW.ID, 'CALL', v_tariff_plan.call_price );
      ELSE
        INSERT INTO log_what_happened ( id, type, price )
          VALUES ( :NEW.ID, 'CONTRACT', NULL );
      END IF;
    END;
    /
    

    如果你有桌子的话:

    CREATE TABLE log_what_happened (
      id    INTEGER,
      type  VARCHAR2(20),
      price DECIMAL(3,2)
    );
    

    并将数据插入您的表中:

    INSERT INTO Tariff_Plan_TB
    SELECT 1, 0.10, 0.01 FROM DUAL UNION ALL
    SELECT 2, 0.20, 0.02 FROM DUAL;
    
    INSERT INTO Contract_TB
    SELECT '1', ( SELECT REF(t) FROM Tariff_Plan_TB t WHERE tariff_id = 1 ) FROM DUAL UNION ALL
    SELECT '2', ( SELECT REF(t) FROM Tariff_Plan_TB t WHERE tariff_id = 2 ) FROM DUAL;
    
    INSERT INTO Operation_TB
    SELECT Call_TY( 1, ( SELECT REF(c) FROM Contract_TB c WHERE telephone_no = '1' ), '555', SYSTIMESTAMP ) FROM DUAL UNION ALL
    SELECT OPERATION_TY( 2, ( SELECT REF(c) FROM Contract_TB c WHERE telephone_no = '2' ) ) FROM DUAL UNION ALL
    SELECT SMS_TY( 3, ( SELECT REF(c) FROM Contract_TB c WHERE telephone_no = '1' ), '777' ) FROM DUAL;
    

    然后:

    SELECT * FROM log_what_happened;
    

    输出:

    身份证 |类型 |价钱 -: | :------- | ----: 1 |电话 | .1 2 |合同 | 3 |短信 | .01

    db小提琴here

    【讨论】:

    • 嗨!感谢您的回答!如果我想访问新的列怎么办?我应该以某种方式将其转换为子类型吗?
    • @juuso 如果你想获取超类型的属性,那么你不需要做任何类型转换,可以直接访问属性(就像我在答案中使用:NEW.contract )。如果要获取子类型的列,请使用:NEW.OBJECT_VALUE 伪列上的TREAT 函数将其转换为子类型,然后访问属性。例如TREAT( :NEW.OBJECT_VALUE AS SMS_TY ).receiver_no 获取receiver_noSMS_TY 类型。
    • 你太棒了,你
    猜你喜欢
    • 2014-02-14
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    • 2020-06-24
    • 2011-08-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多