简体   繁体   English

Oracle SQL Developer - 添加对外键的约束

[英]Oracle SQL Developer - Adding Constraints on Foreign Key

I'm new to Oracle SQL and I am having a hard time adding a constraint.我是 Oracle SQL 的新手,我很难添加约束。 I am trying to add constraints on my table to enforce specific business rules that only allows students only to take 4 courses and a max of 25 students per class.我正在尝试在我的表格上添加约束以强制执行特定的业务规则,该规则仅允许学生参加 4 门课程,每班最多 25 名学生。

Please let me know what additional information you need from me to help answer this question.请让我知道您需要我提供哪些其他信息来帮助回答此问题。 I am at a loss...我很茫然...

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

No problem!没问题! See tables below:见下表:

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

The foreign key alone can only represents a one-to-many relationship. 仅靠外键仅代表一个一对多的关系。 If you want to limit the "many" portion to a specific number, you'll need to either: 如果要将“许多”部分限制为特定数字,则需要执行以下任一操作:

  1. Do it in the application code or triggers. 在应用程序代码或触发器中执行此操作。
  2. Or redesign the rest of the database to "help" the FK in achieving that goal. 或者重新设计数据库的其余部分,以“帮助” FK实现该目标。

The (1) is easy to implement but easy to get wrong: you'll have to carefully employ locking to avoid race conditions in the concurrent environment: (1)易于实现,但很容易出错:您必须谨慎地使用锁定,以避免在并发环境中出现竞争情况:

  • Let's say two concurrent transactions are attempting to connect the same student to a different course. 假设有两个并发事务正在尝试将同一名学生连接到另一门课程。
  • The first transaction counts the courses currently connected to the student and finds that there are 3 of them. 第一笔交易计算了当前与该学生相关的课程,并发现其中有3门课程。 All good and well. 一切都很好。
  • The second transaction does the same and also sees only 3 courses (because the first transaction hasn't committed yet). 第二笔交易也一样,并且只能看到3门课程(因为第一笔交易尚未提交)。
  • So both transactions think they won't exceed the allowed number and both happily proceed with inserting their student-course connection. 因此,两个事务都认为它们不会超过允许的数目,并且愉快地插入了他们的学生课程连接。
  • End result: the student is connected to 5 courses, violating the rule that it can be connected to only 4. 最终结果:该学生已连接到5门课程,违反了只能连接到4门课程的规则。

To avoid that, you'll need to serialize these operations, probably by locking the student through SELECT ... FOR UPDATE . 为了避免这种情况,您可能需要序列化这些操作,可能是通过SELECT ... FOR UPDATE锁定学生。


The (2) could be implemented by changing the key design, and then limiting the values a key can have. (2)可以通过更改键设计,然后限制键可以具有的值来实现。 For example, enforcing that a student can take at most 4 courses can be done like this: 例如,可以强制学生最多参加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)
);

The combination of CHECK and UNIQUE constraints means the DBMS itself will refuse to connect the same student to more than 4 courses. CHECK和UNIQUE约束的组合意味着DBMS本身将拒绝将同一学生连接到4个以上的课程。

Doing this will succeed: 这样做将成功:

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

But doing this obviously won't (CHECK constraint violation): 但是这样做显然不会(检查约束违反):

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

BTW, when student is already connected to some courses and you want to find the remaining free "slots", you can do it like this: 顺便说一句,当学生已经连接到某些课程并且您想找到剩余的免费“插槽”时,可以这样进行:

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] [SQL提琴]

Looks like you already learned about basic constraints in oracle (like check and foreign key). 看起来您已经了解了oracle中的基本约束(例如check和外键)。 For the more complicated scenarious you may use triggers. 对于更复杂的场景,您可以使用触发器。 Triggers are executed on set of events (when you try to insert, update or delete a record into table). 在事件集上执行触发器(当您尝试在表中插入,更新或删除记录时)。

I assume that student takes only 4 courses mean that every student could have only 4 records in grades table. 我认为学生仅修4门课程,意味着每个学生在成绩表中只能有4条记录。 In trigger you can do exactly that. 在触发器中,您可以完全做到这一点。 So you create a trigger before (there is also after ) insert or update, inside a trigger you check how many records student from inserting line already have and accept or decline particular insert/update operation. 因此,您在插入或更新之前(也在之后)创建了一个触发器,在触发器内部,您检查了来自插入行的学生已有多少记录,并接受或拒绝特定的插入/更新操作。

Than you can write similar trigger for 25 students in a course. 相比之下,您可以为一门课程的25名学生编写类似的触发器。 It will be another trigger on the same insert/update into grates table. 这将是同一次插入/更新到gr条表中的另一个触发器。

For exact syntax try documnentation first, triggers are fun :) 对于确切的语法,请首先尝试documnentation,触发器很有趣:)

Also, later you may consider writing stored procedures as a more advanced way of implementing business logic in database. 另外,以后您可能会考虑将存储过程编写为在数据库中实现业务逻辑的更高级方法。 good luck! 祝好运!

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM