简体   繁体   English

Oracle SQL Trigger根据插入列值插入新记录

[英]Oracle SQL Trigger insert new records based on a insert column value

I'm trying to create a trigger for a Oracle table. 我正在尝试为Oracle表创建触发器。 Here are the requirements I have two tables Books, Copies (Books & copies have a 1 to n relationship. Each book can have 0 to n copies) 这是我有两个表的要求书籍,副本(书籍和副本之间存在1到n的关系。每本书可以具有0到n个副本)

Book Table: 图书表:

CREATE TABLE Book
  (
    book_id      INTEGER NOT NULL ,
    isbn         VARCHAR2 (20) NOT NULL,
    publisher_id INTEGER NOT NULL ,
    tittle       VARCHAR2 (100) NOT NULL ,
    cat_id       INTEGER NOT NULL ,
    no_of_copies INTEGER NOT NULL ,
    ....
    CONSTRAINT isbn_unique UNIQUE (isbn),
    CONSTRAINT shelf_letter_unique UNIQUE (shelf_letter, call_number) 
  ) ;

Copies Table 份数表

CREATE TABLE Copies
  (
    copy_id     INTEGER NOT NULL ,
    book_id     INTEGER NOT NULL ,
    copy_number INTEGER NOT NULL,
    constraint copy_number_unique unique(book_id,copy_number)
  ) ;

The trigger (on update,edit of a the Book table) should add its corresponding copy records into Copies table. 触发器(在更新,编辑Book表时)应将其相应的副本记录添加到Copies表中。 So if a insert into Books table has Book.no_of_copies as 5 then five new records should be inserted into the Copies table.. 因此,如果插入到Books表中的Book.no_of_copies为5,则应在Copies表中插入五个新记录。

Below code works for INSERT and UPDATE in table BOOK. 下面的代码适用于表BOOK中的INSERT和UPDATE。 It would insert rows in COPIES table only when either a new row is inserted in table BOOK, or an existing row in table BOOK is updated with no_of_copies greater than its current value. 仅当在表BOOK中插入新行或用大于当前值的no_of_copies更新表BOOK中的现有行时,才在COPIES表中插入行。

Table creation: 表创建:

CREATE TABLE Book
(
book_id      INTEGER NOT NULL ,
isbn         VARCHAR2 (20) NOT NULL,
publisher_id INTEGER NOT NULL ,
tittle       VARCHAR2 (100) NOT NULL ,
cat_id       INTEGER NOT NULL ,
no_of_copies INTEGER NOT NULL ,
CONSTRAINT isbn_unique UNIQUE (isbn) 
) ;

CREATE TABLE Copies
(
copy_id     INTEGER NOT NULL ,
book_id     INTEGER NOT NULL ,
copy_number INTEGER NOT NULL,
constraint copy_number_unique unique(book_id,copy_number)
);

CREATE SEQUENCE COPY_SEQ
MINVALUE 1
MAXVALUE 999999
START WITH 1
INCREMENT BY 1
NOCACHE;

Trigger: 触发:

CREATE OR REPLACE TRIGGER TR_TEST
BEFORE INSERT OR UPDATE ON BOOK
FOR EACH ROW
DECLARE
V_CURR_COPIES    NUMBER;
V_COUNT          NUMBER := 0;

BEGIN

 IF  :NEW.NO_OF_COPIES > NVL(:OLD.NO_OF_COPIES, 0) THEN
     SELECT COUNT(1)
     INTO   V_CURR_COPIES   --# of rows in COPIES table for a particular book.
     FROM   COPIES C
     WHERE  C.BOOK_ID = :NEW.BOOK_ID;

     WHILE  V_COUNT < :NEW.NO_OF_COPIES - V_CURR_COPIES
     LOOP
         INSERT INTO COPIES
         (
         COPY_ID,
         BOOK_ID,
         COPY_NUMBER
         )
         SELECT COPY_SEQ.NEXTVAL,
                :NEW.BOOK_ID,
                V_COUNT + V_CURR_COPIES + 1
         FROM   DUAL;

         V_COUNT := V_COUNT + 1;
     END LOOP;
 END IF;
END;

Testing: 测试:

INSERT INTO BOOK
VALUES (1, 'ABCDEF', 2, 'TEST BOOK', 1, 3);

UPDATE BOOK B
SET    B.NO_OF_COPIES = 4
WHERE  B.BOOK_ID = 1;

This is kinda long, but actually quite straightforward. 这有点长,但实际上非常简单。

Tested on a Oracle 10gR2 setup. 在Oracle 10gR2设置上进行了测试。

Table: 表:

CREATE TABLE books
(
  book_id INTEGER NOT NULL,
  no_of_copies INTEGER NOT NULL,
  CONSTRAINT pk_book_id PRIMARY KEY (book_id)
);

CREATE TABLE copies
(
  book_id INTEGER NOT NULL,
  copy_no INTEGER NOT NULL,
  CONSTRAINT fk_book_id FOREIGN KEY (book_id) REFERENCES books (book_id) ON DELETE CASCADE
);

Then trigger: 然后触发:

CREATE TRIGGER tri_books_add
  AFTER INSERT ON books
  FOR EACH ROW
DECLARE
  num INTEGER:=1;
BEGIN
  IF :new.no_of_copies>0 THEN
    WHILE num<=:new.no_of_copies LOOP
      INSERT INTO copies (book_id,copy_no) VALUES (:new.book_id,num);
      num:=num+1;
    END LOOP;
  END IF;
END;
/

CREATE TRIGGER tri_books_edit
  BEFORE UPDATE ON books
  FOR EACH ROW
DECLARE
  num INTEGER:=1;
BEGIN
  IF :new.no_of_copies<:old.no_of_copies THEN
    RAISE_APPLICATION_ERROR(-20001,'Decrease of copy number prohibited.');
  ELSIF :new.no_of_copies>:old.no_of_copies THEN
    SELECT max(copy_no)+1 INTO num FROM copies WHERE book_id=:old.book_id;
    WHILE num<=:new.no_of_copies LOOP
      INSERT INTO copies (book_id,copy_no) VALUES (:old.book_id,num);
      num:=num+1;
    END LOOP;
  END IF;
END;
/

What the trigger do: 触发器的作用是:

  • For the tri_books_add 对于tri_books_add
    1. use a num to "remember" copy_no ; 使用num “记住” copy_no
    2. use a WHILE-LOOP statement to add copies. 使用WHILE-LOOP语句添加副本。
  • For the tri_books_edit 对于tri_books_edit
    1. use a num to "remember" copy_no ; 使用num “记住” copy_no
    2. check if new no_of_copies is illegally decreased; 检查新的no_of_copies是否被非法减少; if so, raise a custom error ; 如果是这样,则引发自定义错误
    3. append copies. 追加副本。

The reason I separate books inserting and editing into two triggers, is because I employed a foreign key constraint , so after insert would be needed for inserting (correct me if I'm wrong on this, though). 之所以将书籍的插入和编辑分为两个触发器,是因为我采用了foreign key约束 ,因此after insert需要插入(但是如果我错了,请更正我)。

I then run some test: 然后,我运行一些测试:

INSERT INTO books (book_id,no_of_copies) VALUES (1,3);
INSERT INTO books (book_id,no_of_copies) VALUES (2,5);
SQL> select * from copies;

   BOOK_ID    COPY_NO
---------- ----------
         1          1
         1          2
         1          3
         2          1
         2          2
         2          3
         2          4
         2          5

8 rows selected.

SQL> update books set no_of_copies=5 where book_id=1;

1 row updated.

SQL> select * from copies;

   BOOK_ID    COPY_NO
---------- ----------
         1          1
         1          2
         1          3
         2          1
         2          2
         2          3
         2          4
         2          5
         1          4
         1          5

10 rows selected.

SQL> update books set no_of_copies=3 where book_id=1;
update books set no_of_copies=3 where book_id=1
       *
ERROR at line 1:
ORA-20001: Decrease of copy number prohibited.
ORA-06512: at "LINEQZ.TRI_BOOKS_EDIT", line 5
ORA-04088: error during execution of trigger 'LINEQZ.TRI_BOOKS_EDIT'

(I don't seem to be able to make sqlfiddle to work on triggers so no online demos, sorry.) (我似乎无法使sqlfiddle在触发器上工作,所以抱歉,没有在线演示。)

create or replace 
trigger BOOK_TRIGGER 
AFTER INSERT ON BOOK 
FOR EACH ROW 
DECLARE L_COPIES NUMBER:= :NEW.NO_OF_COPIES;
BEGIN
     FOR I IN 1..5
     LOOP
          INSERT
          INTO COPIES
               (
                    COPY_ID,
                    BOOK_ID,
                    COPY_NUMBER
               )
               VALUES
               (
                    1, -- your copy sequence
                    :new.book_id,
                    i
               );
     END LOOP;
END;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM