Удаление записей Использование триггера после удаления всех записей для идентификатора из исходной таблицы - PullRequest
0 голосов
/ 25 июня 2019

У меня есть две таблицы.Поскольку я не могу поделиться именами таблицы, так как это было бы слишком конкретным, я просто попытаюсь объяснить свой запрос, используя общие таблицы.

Две таблицы являются 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 2. Удалите всю запись в Student_Score_Table, когда все записи для конкретного студента будут удалены из Score_table

Я попытался написать триггер «Удалить после» на Score_table и проверить, есть ли какие-либо записи для студента перед удалением Student_Score_Table, используя запрос подсчета, но с этим я получаю результат «1» вместо «0» отсчет.

Ответы [ 3 ]

2 голосов
/ 26 июня 2019

Вместо использования отдельной таблицы и триггеров я бы использовал простой сводный запрос:

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

Сделайте представление, используйте его вместо таблицы student_subject_score и забудьте о триггерах и проблемах, связанных с синхронизацией.

Ваше текущее решение усложняет ситуацию. Вы можете выполнить триггер на score, но для проверки, существуют ли какие-либо строки для конкретного учащегося, требуется select также на счет таблицы. И это недопустимо для триггеров на уровне строк, что приводит к ошибкам mutating table. Для решения этой проблемы вам, вероятно, понадобится сложный триггер. Также ваш дизайн требует либо сложного case when (потому что мы должны найти какой столбец обнулять), либо динамического SQL. Зачем, когда старый, простой и надежный view решает все проблемы?

1 голос
/ 26 июня 2019

См. Ниже обновленный код триггера.Обновления, которые я применил к триггеру по сравнению с моим последним ответом на этот вопрос, содержат некоторые предупреждения.См. ЭТО ТАК сообщение , чтобы понять эти проблемы:

Код триггера

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

0 голосов
/ 25 июня 2019

Хотя я не был на 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;

Полная настройка ниже ... создание таблиц... вставка данных и триггер компиляции приведены ниже:

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

...