Процедура удаления дубликатов в таблице - PullRequest
0 голосов
/ 28 июня 2018

Краткий обзор модели:

У меня есть таблицы student и course. Поскольку отношение «многие ко многим» существует, также имеется соединительная таблица student_course (id_student, id_course) с ограничением unique на оба столбца (составное).

Проблема, которую я хочу решить:

Из-за ошибки в столбце code таблицы course нет ограничения unique. Он должен как code столбец уникально идентифицировать course. В результате в таблице course есть две строки с одинаковым значением в столбце code. Я хочу удалить этот дубликат, проверить, что других дубликатов нет, и добавить ограничение unique для столбца code. Без потери отношений с student таблицей.

Мой подход к решению проблемы:

Я создал процедуру, которая должна делать то, что я хочу.

    CREATE OR REPLACE PROCEDURE REMOVE_COURSES
  (
    v_course_code        IN VARCHAR2,
    v_course_price       IN VARCHAR2
  )
AS
  new_course_id  NUMBER;
  BEGIN
    INSERT INTO course (CODE, PRICE) VALUES (v_course_code, v_course_price)
    RETURNING ID INTO new_course_id;
    FOR c_course_to_overwrite IN (SELECT *
                                  FROM course
                                  WHERE code = v_course_code AND id != new_course_id) LOOP

      UPDATE student_course SET id_course = new_course_id WHERE id_course = c_course_to_overwrite.id;
      DELETE FROM course WHERE id = c_course_to_overwrite.id;
    END LOOP;
  END REMOVE_COURSES;
/

Основная проблема, которую я хочу решить:

Процедура продолжает выдавать ошибку об unique нарушении ограничения для таблицы student_course. Но я действительно не уверен, насколько это возможно, поскольку я использую new_course_id, поэтому нет никакой вероятности, что в соединительной таблице есть две строки с одинаковыми id_student, id_course. Что мне нужно исправить?

Разное:

Я хочу решить эту проблему, используя процедуру только для целей обучения

РЕДАКТИРОВАНИЕ:

    CREATE TABLE student (
      id          NUMBER        GENERATED BY DEFAULT ON NULL AS IDENTITY,
      name        VARCHAR2(150) NOT NULL,
      PRIMARY KEY (id)
    );

    ALTER TABLE student MODIFY ID
      GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH LIMIT VALUE);

    CREATE TABLE course (
    id          NUMBER        GENERATED BY DEFAULT ON NULL AS IDENTITY,
    code        VARCHAR2(255) NOT NULL,
    PRIMARY KEY (id)
    );

    ALTER TABLE course MODIFY ID
      GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH LIMIT VALUE);

    CREATE TABLE student_course (
      id_student NUMBER NOT NULL,
      id_course NUMBER NOT NULL,
      PRIMARY KEY (id_student, id_course),
      CONSTRAINT student_fk FOREIGN KEY (id_student) REFERENCES student (id),
      CONSTRAINT course_fk FOREIGN KEY (id_course) REFERENCES course (id)
    );

    insert into student (name) values ('John');
    INSERT INTO course (ID, CODE) VALUES (1, 'C_13');
    INSERT INTO course (ID, CODE) VALUES (2, 'C_13');
    commit;
    INSERT INTO STUDENT_COURSE (ID_STUDENT, ID_COURSE) VALUES (1, 1);
    INSERT INTO STUDENT_COURSE (ID_STUDENT, ID_COURSE) VALUES (1, 2);
    commit;

CALL REMOVE_COURSES('C_13');

[23000][1] ORA-00001: unique constraint (SYS_C0014983) violated ORA-06512: near "REMOVE_COURSES", line 8  

1 Ответ

0 голосов
/ 28 июня 2018

Вместо того, чтобы удалять один из дублирующих кодов, вы создаете третий курс с тем же кодом и пытаетесь переместить всех студентов на любом из старых курсов на новый. Ошибка предполагает, что у вас есть студенты, которые уже зачислены на оба старых курса.

Ваш запрос цикла курсора:

SELECT *
FROM course
WHERE code = v_course_code AND id != new_course_id

Он найдет все записи о соединении для и старых версий кода, а затем обновление установит все из этих записей о соединении в тот же новый идентификатор.

Если есть какие-либо учащиеся, перечисленные с обоими старыми идентификаторами для кода - что будет разрешено вашим составным уникальным ключом - тогда они оба будут обновлены до того же нового идентификатора.

Скажем, курсы, на которые вы смотрите, [обновлены для вашего примера кода]:

ID  CODE
--  ----
 1  C_13
 2  C_13

и у вас есть записи о соединении для студентов обоих курсов, например:

ID_STUDENT  ID_COURSE
----------  ---------
         1          1
         1          2

Вы создаете новый курс:

ID  CODE
--  ----
 3  C_13

Ваш цикл курсора ищет code = 'ABC' and ID != 3, который находит идентификаторы 1 и 2. Итак, на первой итерации цикла up обновите строки с идентификатором 1, так что теперь у вас есть:

ID_STUDENT  ID_COURSE
----------  ---------
         1          3
         1          2

Затем во второй итерации вы пытаетесь обновить строки с идентификатором 2, что попытается произвести:

ID_STUDENT  ID_COURSE
----------  ---------
         1          3
         1          3

, который нарушит уникальное ограничение - отсюда и ошибка.

Возможно, вы вообще не хотите создавать новый курс, но в любом случае вам нужно удалить дубликаты записей из student_course, то есть строк, которые станут дубликатами при обновлении. В основном вам нужно найти студентов с записями для обоих существующих идентификаторов курса и удалить любой из них. Если вам все равно, что это будет делать:

delete from student_course sc1
where id_course in (
  select id
  from course
  where code = 'C_13'
)
and exists (
  select null
  from student_course sc2
  join course c on c.id = sc.id_course
  where sc2.id_student = sc1.id_student
  and sc2.id_course > sc1.id_course
  and c.code = 'C_13'
);

но есть и другие (возможно, лучшие) способы.

Затем вы можете обновить все оставшиеся записи соединения для обоих старых идентификаторов до вашего нового идентификатора; или объединить один из старых идентификаторов и удалить другой.

(Ваш вопрос подразумевает, что вы хотите решить общую задачу самостоятельно, поэтому я воздержусь от попыток предложить полное решение - надеюсь, это поможет вам понять и решить вашу главную проблему ...)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...