我有两个桌子。由于我不能共享表名,因为它太具体了,所以我将尝试使用通用表解释我的查询。
这两个表是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'计数。
答案 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'))
创建视图,使用它代替表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;
查看实际操作
答案 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;
查看实际操作