Oracle SQL Developer - 在外键上添加约束

Posted

技术标签:

【中文标题】Oracle SQL Developer - 在外键上添加约束【英文标题】:Oracle SQL Developer - Adding Constraints on Foreign Key 【发布时间】:2014-04-21 17:11:14 【问题描述】:

我是 Oracle SQL 的新手,我很难添加约束。我正在尝试在我的表上添加约束以强制执行特定的业务规则,这些规则只允许学生参加 4 门课程,每班最多 25 名学生。

请让我知道您需要我提供哪些其他信息来帮助回答这个问题。我很茫然……

CREATE TABLE GRADES
(STU_ID       int NOT NULL ENABLE,
 CRSE_ID        CHAR(9) NOT NULL ENABLE,
 STU_CRSE_GRADE VARCHAR2(20) 
 check(STU_CRSE_GRADE='A' or 
       STU_CRSE_GRADE='B' or 
       STU_CRSE_GRADE='C' or 
       STU_CRSE_GRADE ='D' or 
       STU_CRSE_GRADE= 'F'),
 CONSTRAINT GRADES_PK PRIMARY KEY (STU_ID, CRSE_ID),
 constraint fk_Grades Foreign key(Stu_ID)
   REFERENCES Students,
 constraint fk_Grades_Crse_ID foreign key(Crse_ID)
   REFERENCES Courses
);

没问题!见下表:

CREATE TABLE Students
(Stu_ID int Constraint pk_Stu_ID Primary Key,
Stu_name VARCHAR(255) NOT NULL, Stu_Add varchar(255),
Stu_Maj CHAR(6)
);

CREATE TABLE Instructors
(Instr_ID char(3) Constraint pk_Instr_ID Primary Key,
Instr_Name VARCHAR(255) NOT NULL, Instr_Office varchar(8)
);

CREATE TABLE Courses
(Crse_ID char(9) Constraint pk_Crse_ID Primary Key,
Crse_Title VARCHAR(255) NOT NULL,
Student’s name: Lai Xia
Instr_ID CHAR(3) not null,
constraint fk_Courses_Instr_ID Foreign key(Instr_ID) REFERENCES Instructors
);

【问题讨论】:

可能您有一些未在此处列出的表格,这些表格显示了学生正在上哪些课程以及班级中有哪些学生。 grades 表似乎与您的问题无关——您需要发布这些表。您在此处列出的规则似乎不是您可以通过约束强制执行的事情,如果这是家庭作业,它们可能是您可以通过触发器强制执行的事情。触发器会在现实世界中产生其他问题。 听起来你需要一个插入触发器 正如@JustinCave 所说,如果没有看到学生和课程表,就很难提供帮助。 【参考方案1】:

外键单独只能表示一对多的关系。如果您想将“多”部分限制为特定数字,您需要:

    在应用程序代码或触发器中执行。 或者重新设计数据库的其余部分以“帮助”FK 实现该目标。

(1) 易于实现但容易出错:您必须小心使用锁定以避免并发环境中的竞争条件:

假设两个并发事务试图将同一个学生连接到不同的课程。 第一笔交易统计学生当前连接的课程,发现有3门。一切顺利。 第二个事务执行相同的操作,也只看到 3 个课程(因为第一个事务尚未提交)。 因此,两个交易都认为它们不会超过允许的数量,并且双方都乐于插入他们的学生课程连接。 最终结果:学生连接了5门课程,违反了只能连接4门的规则。

为避免这种情况,您需要序列化这些操作,可能通过SELECT ... FOR UPDATE 锁定学生。


(2) 可以通过更改键设计,然后限制键可以具有的值来实现。例如,强制一个学生最多可以参加 4 门课程可以这样完成:

CREATE TABLE STUDENT (
    STUDENT_ID INT PRIMARY KEY
);

CREATE TABLE COURSE (
    COURSE_ID INT PRIMARY KEY
);

CREATE TABLE STUDENT_COURSE (
    STUDENT_ID INT REFERENCES STUDENT,
    COURSE_ID INT REFERENCES COURSE,
    COURSE_NO INT NOT NULL CHECK (COURSE_NO IN (1, 2, 3, 4)),
    PRIMARY KEY (STUDENT_ID, COURSE_ID),
    UNIQUE (STUDENT_ID, COURSE_NO)
);

CHECK 和 UNIQUE 约束的组合意味着 DBMS 本身将拒绝将同一学生连接到超过 4 门课程。

这样做会成功:

INSERT INTO STUDENT_COURSE VALUES (11, 111, 1);
INSERT INTO STUDENT_COURSE VALUES (11, 222, 2);
INSERT INTO STUDENT_COURSE VALUES (11, 333, 3);
INSERT INTO STUDENT_COURSE VALUES (11, 444, 4);

但这样做显然不会(CHECK 约束违规):

INSERT INTO STUDENT_COURSE VALUES (11, 555, 5);

顺便说一句,当学生已经连接到某些课程并且您想找到剩余的空闲“插槽”时,您可以这样做:

SELECT NEW_NO
FROM (
    SELECT
        COURSE_NO + 1 NEW_NO,
        LEAD (COURSE_NO) OVER (ORDER BY COURSE_NO) NEXT_NO
    FROM STUDENT_COURSE
    WHERE STUDENT_ID = 11
)
WHERE NEW_NO <> NEXT_NO OR NEXT_NO IS NULL;

[SQL Fiddle]

【讨论】:

【参考方案2】:

看起来您已经了解了 oracle 中的基本约束(例如检查和外键)。对于更复杂的场景,您可以使用触发器。 触发器在事件集上执行(当您尝试在表中插入、更新或删除记录时)。

我假设学生只修了 4 门课程,这意味着每个学生在成绩表中只能有 4 条记录。在触发器中,您可以做到这一点。 因此,您在插入或更新之前(也有之后)创建一个触发器,在触发器内您检查插入行的学生已有多少记录并接受或拒绝特定的插入/更新操作。

您可以为一门课程中的 25 名学生编写类似的触发器。这将是对 grate 表的同一个插入/更新的另一个触发器。

对于确切的语法,首先尝试文档,触发器很有趣:)

另外,稍后您可能会考虑将存储过程作为在数据库中实现业务逻辑的更高级方式。祝你好运!

【讨论】:

【参考方案3】:
CREATE TABLE STUDENT (
    STUDENT_ID INT PRIMARY KEY
);

CREATE TABLE STUDENT_COURSE (
    STUDENT_ID INT REFERENCES STUDENT,
    COURSE_ID INT REFERENCES COURSE,
    COURSE_NO INT NOT NULL CHECK (COURSE_NO IN (1, 2, 3,

CREATE TABLE COURSE (
    COURSE_ID INT PRIMARY KEY
);

),
    PRIMARY KEY (STUDENT_ID, COURSE_ID),
    UNIQUE (STUDENT_ID, COURSE_NO)
);

【讨论】:

以上是关于Oracle SQL Developer - 在外键上添加约束的主要内容,如果未能解决你的问题,请参考以下文章

怎样在pl/sql developer中给oracle添加一个调度作业

oracle 11g 行转列后的列名怎么在外层SQL中使用

pl/sql developer中文乱码,为啥呢?怎么解决?Oracle问题

pl/sql developer中文乱码,为啥呢?怎么解决?Oracle问题

pl/sql developer中文乱码,为啥呢?怎么解决?Oracle问题

pl/sql developer和sql developer有啥区别?