删除记录一旦删除了源表中ID的所有记录,就可以使用触发器

时间:2019-06-25 18:12:42

标签: sql oracle plsql procedure database-trigger

我有两个桌子。由于我不能共享表名,因为它太具体了,所以我将尝试使用通用表解释我的查询。

这两个表是Score_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     |

学生和科目都有各自的查找表,从中可以获取学生姓名和科目。

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

中的记录

我在思考如何编写删除触发器时遇到问题,如何在此处编写触发器,该触发器将: 1.删​​除分数Student_Score_Table,即当我更新Score_table中的值时将其设置为null 2.从Student_Score_Table

中删除特定学生的所有记录后,删除Score_table中的整个记录​​。

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

3 个答案:

答案 0 :(得分:2)

我将使用简单的数据透视查询来代替使用单独的表和触发器:

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

创建视图,使用它代替表student_subject_score,而忽略触发器和与同步相关的问题。

您当前的解决方案使事情变得复杂。您可以在score上触发,但要检查特定学生是否存在任何行,也需要在表分数上select。而且行级触发器不允许这样做,从而导致mutating table错误。为了解决这个问题,您可能需要复合触发器。同样,您的设计需要复杂的case when(因为我们必须找到要无效的列)或动态SQL。旧的,简单而可靠的view能解决所有问题吗?

答案 1 :(得分:1)

请参阅下面更新的触发代码。与上一个对此问题的最后答案相比,我应用于触发器的更新确实带有一些警告。请参阅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;
  

下面的完整设置 ...创建表...插入数据和编译触发器都在下面:

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;
  

查看实际操作

enter image description here

答案 2 :(得分:0)

虽然不能百分百确定自己需要什么,但我可以使用此触发器完成我认为要实现的目标:

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;
  

下面的完整设置...创建表...插入数据和编译触发器都在下面:

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;
  

查看实际操作

enter image description here