删除来自源 table 的 ID 的所有记录后使用触发器删除记录

Deleting Records Using a trigger once all records for an ID from the source table are deleted

我有两个 table。由于我无法共享 table 的名称,因为它太具体了,我将尝试使用通用 tables.

来解释我的查询

两个table是Score_table;它存储学生的分数,让我们说另一个 table 是一个分数仪表板 (Student_Score_Table),它提供了一个视图分数。

Score_table
|------------|------------|-------|
| Student ID | Subject ID | Score |  
|------------|------------|-------|
|     12     |      1     |   50  |
|------------|------------|-------|
|     12     |      2     |   70  |

Student_Score_Table
|--------------|------------|----------|
| Student Name | Subject A  | Subject B|  
|--------------|------------|----------|
|     Daniel   |      50    |    90    |
|--------------|------------|----------|
|     James    |      70    |   45     |

学生和科目有各自的查找 tables 从那里我得到学生姓名和科目。

有一项服务可以更新和删除 Score_table

中的记录

我在考虑如何编写删除时遇到问题 trigger.How 我可以在这里编写一个触发器吗:- 1. 删除分数 Student_Score_Table 即当我更新 Score_table 中的值时将其设置为空 2. 当从 Score_table

中删除特定学生的所有记录时,删除 Student_Score_Table 中的整个记录

我尝试在 Score_table 上触发后写一个删除,并在使用计数查询删除 Student_Score_Table 之前检查学生是否有任何记录,但我得到的结果是“1”计数为“0”。

虽然不能 100% 确定您需要什么,但我能够使用此触发器完成我认为您正在寻求完成的事情:

CREATE OR REPLACE TRIGGER student_score_trig
  AFTER
   DELETE OR UPDATE of score
  ON score_table
 REFERENCING NEW AS NEW OLD AS OLD
  FOR EACH ROW

 BEGIN

  DELETE student_score_table
  WHERE student_id = :old.student_id
  ;

 exception
   when others then
     raise;
END;

Full Setup below... creating tables... inserting data and compiling trigger is all below:

CREATE TABLE score_table 
( student_id  NUMBER,
  subject_id  NUMBER,
  score       NUMBER  
)
;
/

INSERT INTO score_table VALUES (12, 1, 50);
INSERT INTO score_table VALUES (12, 2, 70);
INSERT INTO score_table VALUES (10, 5, 60);

/

CREATE TABLE student_score_table 
( student_id    NUMBER,
  student_name  VARCHAR2(250),
  subject_a     NUMBER,
  subject_b     NUMBER
)
;

INSERT INTO student_score_table VALUES (12, 'Daniel', 50, 90);
INSERT INTO student_score_table VALUES (10, 'James', 70, 45);

/

set define off; /* Depending on your interface this line might need to be removed */

CREATE OR REPLACE TRIGGER student_score_trig
  AFTER
   DELETE OR UPDATE of score
  ON score_table
 REFERENCING NEW AS NEW OLD AS OLD
  FOR EACH ROW

 BEGIN

  DELETE student_score_table
  WHERE student_id = :old.student_id
  ;

 exception
   when others then
     raise;
END;

See it in Action

我不会使用单独的 table 和触发器,而是使用简单的数据透视查询:

select *
  from (
    select subject_name, student_name, score
      from score 
      join students using (student_id)
      join subjects using (subject_id))
  pivot (max(score) for subject_name in ('Subject A', 'Subject B'))

dbfiddle demo

制作一个视图,用它代替 table student_subject_score 并忘记与同步相关的触发器和问题。

您当前的解决方案使事情变得复杂。您可以在 score 上触发,但检查特定学生是否存在任何行也需要 select 在 table 分数上。这在 row-level 触发器上是不允许的,会导致 mutating table 错误。要解决这个问题,您可能需要复合触发器。此外,您的设计需要复杂的 case when(因为我们必须找到要取消的列),或者动态的 SQL。干什么,老了,简单可靠view解决了所有问题?

见下方更新的触发代码。与我对该问题的上次回答相比,我对触发器应用的更新确实带有一些警告。请参阅 THIS SO Post 以了解这些问题:

触发码

create or replace TRIGGER student_score_trig
  AFTER
   DELETE OR UPDATE of score
  ON score_table
 REFERENCING NEW AS NEW OLD AS OLD
  FOR EACH ROW

DECLARE
  /* The below pragma allows us to SELECT from the same table that Triggered the Trigger.
   | Many circles say that doing any 'work' on the same table as the Trigger
   | is dangerous due to possible uncommited changes that are not able to be
   | seen by the code within the Trigger.
  */
  pragma autonomous_transaction;

  n_student_count  NUMBER  :=  0;

 BEGIN

  IF UPDATING THEN
    /* Not sure how you plan to tie the Subjects between each of the two tables
     | but I'm guessing this would be a non-issue for you.
    */
    UPDATE student_score_table SET subject_a = NULL
    WHERE student_id = :old.student_id
    ;
    COMMIT;
    dbms_output.put_line('This student_id was UPDATED: ' || :old.student_id || ' for subject_id: ' || :old.subject_id);
  END IF;


  IF DELETING THEN

    SELECT COUNT(student_id)
    INTO n_student_count
    FROM score_table
    WHERE student_id = :old.student_id
    ;

    dbms_output.put_line(n_student_count);

    IF n_student_count <= 1 THEN
      DELETE student_score_table
      WHERE student_id = :old.student_id
      ;
      COMMIT;
      dbms_output.put_line('This student_id was DELETED: ' || :old.student_id);
    END IF;

  END IF;

 exception
   when others then
     raise;
END;

Full Setup below... creating tables... inserting data and compiling trigger is all below:

CREATE TABLE score_table 
( student_id  NUMBER,
  subject_id  NUMBER,
  score       NUMBER  
)
;
/

INSERT INTO score_table VALUES (12, 1, 50);
INSERT INTO score_table VALUES (12, 2, 70);
INSERT INTO score_table VALUES (10, 5, 60);

/

CREATE TABLE student_score_table 
( student_id    NUMBER,
  student_name  VARCHAR2(250),
  subject_a     NUMBER,
  subject_b     NUMBER
)
;

INSERT INTO student_score_table VALUES (12, 'Daniel', 50, 90);
INSERT INTO student_score_table VALUES (10, 'James', 70, 45);

/

--set define off; /* Depending on your interface this line might need to be removed */
--/

create or replace TRIGGER student_score_trig
  AFTER
   DELETE OR UPDATE of score
  ON score_table
 REFERENCING NEW AS NEW OLD AS OLD
  FOR EACH ROW

DECLARE
  /* The below pragma allows us to SELECT from the same table that Triggered the Trigger.
   | Many circles say that doing any 'work' on the same table as the Trigger
   | is dangerous due to possible uncommited changes that are not able to be
   | seen by the code within the Trigger.
  */
  pragma autonomous_transaction;

  n_student_count  NUMBER  :=  0;

 BEGIN

  IF UPDATING THEN
    /* Not sure how you plan to tie the Subjects between each of the two tables
     | but I'm guessing this would be a non-issue for you.
    */
    UPDATE student_score_table SET subject_a = NULL
    WHERE student_id = :old.student_id
    ;
    COMMIT;
    dbms_output.put_line('This student_id was UPDATED: ' || :old.student_id || ' for subject_id: ' || :old.subject_id);
  END IF;


  IF DELETING THEN

    SELECT COUNT(student_id)
    INTO n_student_count
    FROM score_table
    WHERE student_id = :old.student_id
    ;

    dbms_output.put_line(n_student_count);

    IF n_student_count <= 1 THEN
      DELETE student_score_table
      WHERE student_id = :old.student_id
      ;
      COMMIT;
      dbms_output.put_line('This student_id was DELETED: ' || :old.student_id);
    END IF;

  END IF;

 exception
   when others then
     raise;
END;

See it in Action