在我的数据库中,我有很多必修课程。有些是选修课。但是,有第三种课程:您必须从中选择X课程的列表。每个学习计划的列表(和数字X)是不同的。你会如何表达这种关系?
答案 0 :(得分:5)
我觉得有趣的是,接受的答案说,“当问题基本上是问题时,没有办法在关系上代表'X'。在我看来,'X of Y'确实可以使用SQL建模(并在很大程度上强制执行),这是一种建议的方式:
示例场景:参加“法语”课程的学生必须从总共三个可能的组成部分(y)中选择两个组成部分(x)。
CREATE TABLE Components
(
component_name VARCHAR(100) NOT NULL,
UNIQUE (component_name)
);
INSERT INTO Components (component_name) VALUES
('Oral'),
('Writing'),
('Vocab'),
('Databases');
显然,'数据库'不属于法语课程,所以我们需要一个表格供课程设计者来建模课程[这些表格有很多相关的候选键,所以为了清楚起见我会在它们的'底部'定义它们CREATE TABLE
声明):
CREATE TABLE XofYCourses
(
course_name VARCHAR(100) NOT NULL,
x_components_choice_tally INTEGER NOT NULL
CHECK (x_components_choice_tally > 0),
y_components_tally INTEGER NOT NULL
CHECK (y_components_tally > 0),
CHECK (x_components_choice_tally < y_components_tally),
UNIQUE (course_name),
UNIQUE (course_name, y_components_tally),
UNIQUE (course_name, x_components_choice_tally)
);
INSERT INTO XofYCourses (course_name, y_components_tally,
x_components_choice_tally) VALUES
('French', 2, 3);
以上允许我们模拟法语课程中的“三分之二”属性。现在我们需要一个表来模拟该课程的三个可能组成部分:
CREATE TABLE XofYCourseComponents
(
course_name VARCHAR(100) NOT NULL,
y_components_tally INTEGER NOT NULL,
FOREIGN KEY (course_name, y_components_tally)
REFERENCES XofYCourses (course_name, y_components_tally),
component_sequence INTEGER NOT NULL
CHECK (component_sequence > 0),
component_name VARCHAR(100) NOT NULL
REFERENCES Components (component_name),
CHECK (component_sequence <= y_components_tally),
UNIQUE (course_name, component_sequence),
UNIQUE (course_name, component_name)
);
INSERT INTO XofYCourseComponents (course_name,
component_sequence, y_components_tally, component_name)
VALUES
('French', 1, 3, 'Oral'),
('French', 2, 3, 'Writing'),
('French', 3, 3, 'Vocab');
现在注册。比利想做法语课程......
CREATE TABLE Students
(
student_name VARCHAR(20) NOT NULL,
UNIQUE (student_name)
);
INSERT INTO Students (student_name) VALUES ('Billy');
...并选择'口头'和'词汇':
CREATE TABLE XofYCourseComponentChoices
(
student_name VARCHAR(20) NOT NULL
REFERENCES Students (student_name),
course_name VARCHAR(100) NOT NULL,
x_components_choice_tally INTEGER NOT NULL,
FOREIGN KEY (course_name, x_components_choice_tally)
REFERENCES XofYCourses (course_name, x_components_choice_tally),
component_name VARCHAR(100) NOT NULL,
FOREIGN KEY (course_name, component_name)
REFERENCES XofYCourseComponents (course_name, component_name),
x_component_sequence INTEGER NOT NULL
CHECK (x_component_sequence > 0),
CHECK (x_component_sequence <= x_components_choice_tally),
UNIQUE (student_name, course_name, component_name),
UNIQUE (student_name, course_name, x_component_sequence)
);
INSERT INTO XofYCourseComponentChoices (student_name, course_name,
component_name, x_component_sequence, x_components_choice_tally)
VALUES
('Billy', 'French', 'Oral', 1, 2),
('Billy', 'French', 'Vocab', 2, 2);
上述结构可以很好地执行最大值,即法语课程不超过三个组件,每个学生不超过两个选项。
然而,它没有做的是确保确切的数量,例如比利不会只选择一个组件。标准SQL具有解决此问题的方法,例如支持子查询的CHECK
约束(例如,计算Billy总共有两行......)和DEFERRABLE
约束(...但延迟计数直到事务处理的时间点提交)。拥有“多重分配”功能会更好。但是,大多数SQL产品都没有这些功能。
这是否缺乏对完整解决方案的支持意味着我们什么都不做,只是相信应用程序会避免编写无效数据?当然不是!
一个好的临时方法是从基表中撤销权限并提供帮助程序存储过程,例如一个用于注册学生,该学生将他们选择的课程组件作为参数:计数在INSERT
之后完成,如果它违反了数据规则(例如法语少于两个),那么事务将被回滚并且错误返回。
答案 1 :(得分:4)
这里需要3个表:StudyPrograms,Courses and Components。组件代表构成每个StudyProgram的课程,是课程和StudyPrograms之间的联结表。
每个组件记录都可以包含一个字段,指示课程是否是StudyProgram的必修部分。您还可以包含一个字段,以指示课程是否是可以选择的列表之一。
无法以关系方式表示“X of Y”,您需要在存储过程中使用一些逻辑来确保遵循此业务规则(或者可能在数据访问代码层中,具体取决于您希望如何组织申请)。
答案 2 :(得分:3)
您有两种选择:您可以将数据建模更接近现实,其中一些是单课程要求,另一些是Y课程要求的X,或者您可以将所有要求建模为Y来自Y,其中单课程要求是“1比1”的要求。
我会推荐这样的东西:
Course
---------------
CourseID
Description
...
Program
---------------
ProgramID
Description
...
CourseGroup
---------------
CourseGroupID
CourseID
ProgramCourseGroup
---------------
ProgramID
CourseGroupID
RequiredCourses
Course
和Program
是两个顶级表格。它们分别定义了所有课程和程序的简单列表,两者之间没有任何关系。
CourseGroup
定义了一组课程。这涉及Course
,但没有其他表格。
ProgramCourseGroup
将课程组与课程联系起来。一个程序表明需要一组特定的课程,然后RequiredCourses
表示必须从该组中选择多少课程才能满足要求。
例如,假设您有一个名为“篮子编织”的程序,需要:
以下四门课程中的两门:
您的数据如下:
Course
------------------------------------
CourseID Description
1 Intro to baskets
2 Basic weaving techniques
3 Easter baskets
4 Handbaskets
5 Picnic baskets
6 SCUBA Diving
Program
--------------------------
ProgramID Description
1 Basket Weaving
CourseGroup
--------------------------
CourseGroupID CourseID
1 1
2 2
3 3
3 4
3 5
3 6
ProgramCourseGroup
-----------------------------------------
ProgramID CourseGroupID RequiredCourses
1 1 1
1 2 1
1 3 2
答案 3 :(得分:2)
原则上,人们希望能够创建类似这样的约束来强制执行规则:
CHECK
(NOT EXISTS
(SELECT 1
FROM CourseEnrolement c, ProgramEnrolement p
WHERE c.StudentId = p.StudentId
AND c.ProgramId = p.ProgramId
GROUP BY p.StudentId, p.ProgramId, p.NumberOfCoursesRequired
HAVING COUNT(*) <> p.NumberOfCoursesRequired
))
不幸的是,SQL几乎不可能实现这一点,或者至少在强制执行约束时更新数据库非常困难。因此,如果您真的想在数据库中表示这样的规则,那么您可能需要一个比SQL更好的模型。实际上,这些规则通常会在应用程序代码中强制执行。
答案 4 :(得分:1)
您可以将这些课程视为“集合”,然后使用单独的COURSES_SET
表格来显示集合的ID。您的课程可以有一个参考课程表的optinal set_id
。这只是一种方式来做到这一点......
示例:
COURSES_SET CourseSetID Name ----------- --------------- 1 Early Renaissance medical techniques 2 Jurassic theological certificate program 3 Mad Science COURSES CourseID Name CourseSetID CourseSequenceNumber -------- -------------------- ----------- --------------------- 1001 The joys of leeches 1 1 2011 How to keep your patient from dying 1 2 1700 Is there a T-Rex? Arguements for and Against 2 1 1301 Intro to Algorithms (NULL) (NULL) 3301 Cackling: An advanced 3 3 course
此模型允许您将课程与课程相关联(尽管您也可以称之为“课程”)课程,并指定序列号以确保学生以正确的顺序学习。如果两个课程可以同时进行(两个课程之间的顺序并不重要),您可以为它们分配相同的序列号。
然后你可以有一个由STUDY_PROGRAM
引用的单独的COURSES_SET
表,这样你就可以知道一组课程属于哪个学习计划。
答案 5 :(得分:1)
|--------| |-----------------| |---------------|
|Program | |CoursesByProgram | |Courses |
|------ | |-----------------| |---------------|
|Id PK |---------|ProgramId PK | |CourseId PK |
|Name | |CourseId PK |-------------|Name |
|--------| |ListId FK NULL | |---------------|
|IsCompulsory bit|
|IsElective bit|
-------------------
|
|
|-----------------|
|List |
|-----------------|
|Id PK |
|Name |
------------------
答案 6 :(得分:1)
我会做以下事情:
首先创建课程表:
courseId courseName IsCompulsory OptionalCoursesId
======== ========== ============ ================
然后有一个可选课程的第二个表:
OptionalcourseId courseName etc etc etc
================ ========== === === ===
本质上,第二个表是包含所有可选选项的链接表。
示例:
courseId courseName IsCompulsory OptionalCoursesId
======== ========== ============ ================
1 science 0 NULL
2 IT 0 2
OptionalcourseId courseName
================ ==========
2 SQL
2 C#
2 Java
5 Extreme Ironing
5 Native Julu dancing
希望这是有道理的
答案 7 :(得分:1)
我会定义一个表Courses
,一个表Components
,一个表Programmes
以及Courses
和Components
之间以及Components
之间的链接表}和Programmes
。 Programmes
和Components
之间的链接表还应该有一个列,指示链接到的程序需要多少来自链接组件的信用。
例如,您可以拥有一个数学组件,在该组件中您可以使用代数,三角函数,微积分I和II等课程 - 这可以通过ComponentCourses
表中的记录来表示。
然后,您可以同时拥有需要数学课程的Physics
程序和Chemistry
程序。如果Physics
要求6学分数学而Chemistry
需要4学分,则表示Credits
表中的ProgrammeComponents
值。
要了解某一系列课程是否符合课程的文凭要求,只需将课程的学分值与每个部分相加,然后查看它是否与ProgrammeComponents
表中所需的值相符。
下面是一些示例表数据,它定义了两个程序,物理和化学,以及它们与三个数学课程和一个力学课程的关系。学生的要求如下:
如您所见,此架构非常灵活,只要确定您可以“挑选”最少量(0 <组件中所需的信用额&lt;组件总积分)的课程“篮子”,完全选修课程(必修学分= 0)以及必修课程(必修学分=成分总学分)。
架构:
Courses Components
******* **********
CourseId | CourseName | Credits ComponentId | ComponentName
1 | Algebra | 1 1 | Math
2 | AP Algebra | 2 2 | Physics
3 | Trigonometry | 1
4 | Mechanics | 1
ComponentCourses Programmes
**************** **********
CourseId | ComponentId ProgramId | ProgramName
1 | 1 1 | Physics
2 | 1 2 | Chemistry
3 | 1
4 | 2
ProgrammeComponents
ProgrammeId | ComponentId | RequiredCredits
1 | 1 | 3
2 | 1 | 2
1 | 2 | 0