删除表中重复项的过程

时间:2018-06-28 16:36:17

标签: oracle stored-procedures

简要模型概述:

我有一个student和一个course表。由于关系是多对多的,因此还有一个联结表student_courseid_studentid_course),两列(复合)的约束为unique

我要解决的问题:

由于错误,unique表的code列上没有course约束。它应该在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_studentid_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 :(得分:2)

您将使用相同的代码创建第三课程,而不是删除重复的代码之一,并尝试将所有旧课程中的所有学生转移到新课程中。该错误表明您已经有两个旧课程的学生。

您的游标循环查询是:

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

这将找到两个旧版本代码的所有联结记录,然后更新将这些联结记录的 all 设置为相同的新ID。

如果针对该代码的两个旧ID列出了任何学生(您的复合唯一键允许),那么他们都会被更新为相同的新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,该循环将找到ID 1和2。因此,在循环的第一次迭代中,更新ID为1的行,这样您就可以了:

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

然后在第二次迭代中,尝试更新ID为2的行,该行将尝试生成:

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

这将打破唯一约束-因此产生错误。

您可能根本不想创建新课程,但是无论哪种方式,都需要从student_course中删除重复的记录-也就是说,更新后的行将成为重复的记录。基本上,您需要查找具有两个现有课程ID条目的学生,然后删除其中一个。如果您不在乎该怎么做:

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'
);

但是还有其他(可能更好)的方法。

然后,您可以选择将两个旧ID的所有剩余联结记录更新为新ID;或合并其中一个旧ID,然后删除另一个。

(您的问题表示您想自己解决总体任务,因此,我将避免尝试提供完整的解决方案-希望这可以帮助您理解和解决主要问题...)