【问题标题】:How to create the correct trigger instead of insert or update? ORACLE. Trigger. pl/sql如何创建正确的触发器而不是插入或更新?甲骨文。扳机。 pl/sql
【发布时间】:2022-01-21 18:25:52
【问题描述】:

帮助为不可更新的视图创建触发器。

表格如下所示。

 CREATE TABLE Users(id_user INTEGER GENERATED ALWAYS AS IDENTITY(START with 1 INCREMENT by 1 NOCACHE) PRIMARY KEY,
                surname VARCHAR2(30) NOT NULL,
                name VARCHAR2(30) NOT NULL,
                patronymic VARCHAR2(30) NULL,
                tel_no VARCHAR2(17) NOT NULL,
              
                CONSTRAINT ch_telno_users
                           CHECK(REGEXP_LIKE(tel_no, '^\+375\(\d{2}\)\d{3}-\d{2}-\d{2}$')),
                CONSTRAINT uni_telno_users
                           UNIQUE(tel_no)
                 );


CREATE TABLE Software(id_sw INTEGER GENERATED ALWAYS AS IDENTITY(START with 1 INCREMENT by 1 NOCACHE) PRIMARY KEY,
                  name_sw VARCHAR2(40) NOT NULL,
                  version VARCHAR2(10) NULL,
                  license_period VARCHAR2(10) NOT NULL,
                  id_mfr INTEGER NOT NULL,
                  release_date DATE NOT NULL,
                  price NUMBER(9,2),
                  total_amount INTEGER NOT NULL,
                CONSTRAINT id_mfr_fk
                           FOREIGN KEY(id_mfr)
                           REFERENCES Manufacturer,           
                  );

    CREATE SYNONYM sw for Software;


CREATE TABLE Sales(id_sale INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
               id_user INTEGER NOT NULL,
               id_sw INTEGER NOT NULL,
               quantity INTEGER NOT NULL,
               total_cost NUMBER(9,2),
               order_date DATE NOT NULL CHECK (order_date >= to_date('01/01/2019', 'dd/mm/yyyy')),
               expiration_date DATE NOT NULL,
             CONSTRAINT id_user_fk
                        FOREIGN KEY(id_user)
                        REFERENCES Users,
             CONSTRAINT id_sw_fk
                        FOREIGN KEY(id_sw)
                        REFERENCES Software
            );

视图本身看起来像这样

    CREATE OR REPLACE VIEW SalesView AS
    SELECT Sales.id_sale,sw.name_sw||' '||sw.version sw_full_name,        
    Users.surname||' '||Users.name||' '||Users.patronymic user_full_name,  
    sales.quantity,         sales.order_date
    FROM sw INNER JOIN (Users INNER JOIN Sales ON Sales.id_user = Users.id_user)  ON sw.id_sw = sales.id_sw;

我尝试创建这样的触发器。但由于某种原因,它没有被创建。( 第 86 行错误:PL/SQL:SQL 语句被忽略 第 94 行错误:PL/SQL: ORA-00917: 缺少逗号)

在我看来,我走得很艰难。帮助修复此代码。

    CREATE OR REPLACE TRIGGER sales_view_instead_of_trig INSTEAD OF
    UPDATE OR INSERT ON salesview
    FOR EACH ROW
      DECLARE
      sw_new        sw.id_sw%TYPE;
      user_new      Users2.id_user%TYPE;
      check_excep_sw   VARCHAR2(51);
      check_excep_user VARCHAR2(92);
    BEGIN
        IF updating THEN --* update
    IF :new.sw_full_name != :old.sw_full_name THEN
        SELECT
            sw.name_sw||' '||sw.version
        INTO check_excep_sw
        FROM
            sw
        WHERE
            sw.name_sw||' '||sw.version = :new.sw_full_name;
        SELECT
            id_sw
        INTO sw_new
        FROM
            sw
        WHERE
            sw.name_sw||' '||sw.version = :new.sw_full_name;
      ELSE
        SELECT
            id_sw
        INTO sw_new
        FROM
            sw
        WHERE
            sw.name_sw||' '||sw.version = :old.sw_full_name;
       END IF;
       IF :new.user_full_name != :old.user_full_name THEN
        SELECT
            Users2.surname||' '||Users2.name||' '||Users2.patronymic
        INTO check_excep_user
        FROM
            Users2
        WHERE
            Users2.surname||' '||Users2.name||' '||Users2.patronymic = :new.user_full_name;
        SELECT
            id_user
        INTO user_new
        FROM
            Users2
        WHERE
            Users2.surname||' '||Users2.name||' '||Users2.patronymic = :new.user_full_name;
       ELSE
        SELECT
            id_user
        INTO user_new
        FROM
            Users2
        WHERE
            Users2.surname||' '||Users2.name||' '||Users2.patronymic = :old.user_full_name;
        END IF;
       UPDATE sales
       SET
        id_user = user_new,
        id_sw = sw_new,
        quantity = :new.quantity,
        order_date = :new.order_date,
        expiration_date = :new.expiration_date,
        total_cost = :new.total_cost
    WHERE
        id_sale = :old.id_sale;
   END IF;
    IF inserting THEN --* insert
   SELECT sw.name_sw||' '||sw.version INTO check_excep_sw
        FROM sw
        WHERE sw.name_sw||' '||sw.version = :new.sw_full_name;
        SELECT id_sw
        INTO sw_new
        FROM sw
        WHERE sw.name_sw||' '||sw.version = :new.sw_full_name;
    SELECT
        Users2.surname||' '||Users2.name||' '||Users2.patronymic
    INTO check_excep_user
    FROM
        Users2
    WHERE
        Users2.surname||' '||Users2.name||' '||Users2.patronymic = :new.user_full_name;
     SELECT id_user
        INTO user_new
        FROM Users2
        WHERE Users2.surname||' '||Users2.name||' '||Users2.patronymic = :new.user_full_name;
    INSERT INTO sales(
        id_user,
        id_sw,
        quantity,
        order_date,
        expiration_date,
        total_cost
     ) VALUES (
        id_user = user_new,
        id_sw = sw_new,
        quantity = :new.quantity,
        order_date = :new.order_date,
        expiration_date = :new.expiration_date,
        total_cost = :new.total_cost
     );
        END IF;
    EXCEPTION
        WHEN no_data_found THEN
    dbms_output.put_line('-------------------------------------------');
    dbms_output.put_line('| ERROR!   |');
    dbms_output.put_line('-------------------------------------------');
    END;
    /

【问题讨论】:

    标签: sql oracle plsql view triggers


    【解决方案1】:

    “INSERT INTO sales”语句的 VALUES 中缺少冒号:

    id_user = :user_new,
    id_sw = :sw_new,
    

    【讨论】:

    • 这些是局部变量,所以不会有冒号。如果它们需要以冒号作为前缀,它们将成为 :new:old 伪记录的一部分,因此,您必须指定要使用的伪记录。
    【解决方案2】:

    您说创建触发器时出错。但是如果我尝试复制您的问题,我不能因为Software 表的定义无效。首先,外键引用了您的示例中不存在的表 Manufacturer。其次,外键约束定义的末尾多了一个逗号。

    如果我完全删除外键约束

    CREATE TABLE Software(id_sw INTEGER GENERATED ALWAYS AS IDENTITY(START with 1 INCREMENT by 1 NOCACHE) PRIMARY KEY,
                      name_sw VARCHAR2(40) NOT NULL,
                      version VARCHAR2(10) NULL,
                      license_period VARCHAR2(10) NOT NULL,
                      id_mfr INTEGER NOT NULL,
                      release_date DATE NOT NULL,
                      price NUMBER(9,2),
                      total_amount INTEGER NOT NULL
                      );
    

    这允许脚本尽可能地尝试创建触发器。此时,您将收到错误,包括(但不限于)您提到的 ORA-00917 错误。

    Errors: TRIGGER SALES_VIEW_INSTEAD_OF_TRIG
    Line/Col: 62/27 PLS-00049: bad bind variable 'NEW.EXPIRATION_DATE'
    Line/Col: 63/22 PLS-00049: bad bind variable 'NEW.TOTAL_COST'
    Line/Col: 86/5 PL/SQL: SQL Statement ignored
    Line/Col: 94/17 PL/SQL: ORA-00917: missing comma
    Line/Col: 98/27 PLS-00049: bad bind variable 'NEW.EXPIRATION_DATE'
    Line/Col: 99/22 PLS-00049: bad bind variable 'NEW.TOTAL_COST'
    

    出现PLS-00049: bad bind variable 错误是因为视图定义中没有名为expiration_datetotal_cost 的列。因此,不存在 :new.expiration_date:new.total_cost 这样的东西,因为针对视图的 DML 无法为这些列指定值。我不知道你想如何处理——要么省略值,提供默认值,从其他地方读取值,将列​​添加到视图并在 DML 中针对视图提供它们,等等。事实上@ 987654331@ 被定义为not null 似乎限制了您的选择。

    如果您查看触发器的第 86 行,您会看到这条语句

    INSERT INTO sales(
            id_user,
            id_sw,
            quantity,
            order_date,
            expiration_date,
            total_cost
         ) VALUES (
            id_user = user_new,
            id_sw = sw_new,
            quantity = :new.quantity,
            order_date = :new.order_date,
            expiration_date = :new.expiration_date,
            total_cost = :new.total_cost
         );
    

    在语法上无效。

    INSERT INTO sales(
            id_user,
            id_sw,
            quantity,
            order_date,
            expiration_date,
            total_cost
         ) VALUES (
            user_new,
            sw_new,
            :new.quantity,
            :new.order_date,
            :new.expiration_date,
            :new.total_cost
         );
    

    如果:new.expiration_date:new.total_cost 是有效标识符,则该标识符有效。但是,如上所述,它们无效。而且,您希望插入的值是什么并不明显。

    这是一个liveSQL example,您可以使用它来确保您拥有的是可重现的测试用例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-27
      • 2017-01-24
      • 1970-01-01
      • 2017-03-21
      • 1970-01-01
      • 2019-10-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多