mysql之表与表之间的关系

目录

表与表之间的关系

数据库的三范式

练习题

查询学习课程"python"比课程 "java" 成绩高的学生的学号; 

查询平均成绩大于65分的同学的姓名和平均成绩(保留两位小数) 

 查询所有同学的姓名、选课数、总成绩;

查询所有的课程的名称以及对应的任课老师姓名;

查询没学过“alex”老师课的同学的姓名;

  查询学过'python'并且也学过编号'java'课程的同学的姓名

查询挂科超过两门(包括两门)的学生姓名;

查询选修了全部课程的学生姓名;

1.表关系分类:

  总体可以分为三类: 一对一 、一对多(多对一) 、多对多

2.如何区分表与表之间是什么关系?

#分析步骤:
#多对一 /一对多
#1.站在左表的角度去看右表(情况一)
如果左表中的一条记录,对应右表中多条记录.那么他们的关系则为 一对多 关系.约束关系为:左表普通字段, 对应右表foreign key 字段.

注意:如果左表与右表的情况反之.则关系为 多对一 关系.约束关系为:左表foreign key 字段, 对应右表普通字段.

#一对一
#2.站在左表的角度去看右表(情况二)
如果左表中的一条记录 对应 右表中的一条记录. 则关系为 一对一关系.
约束关系为:左表foreign key字段上 添加唯一(unique)约束, 对应右表 关联字段.
或者:右表foreign key字段上 添加唯一(unique)约束, 对应右表 关联字段.

#多对多
#3.站在左表和右表同时去看(情况三)
如果左表中的一条记录 对应 右表中的多条记录,并且右表中的一条记录同时也对应左表的多条记录. 那么这种关系 则 多对多 关系. 
这种关系需要定义一个这两张表的[关系表]来专门存放二者的关系

3.建立表关系

1.一对多关系

 例如:一个人可以拥有多辆汽车,要求查询某个人拥有的所有车辆。 
 分析:人和车辆分别单独建表,那么如何将两个表关联呢?有个巧妙的方法,在车辆的表中加个外键字段(人的编号)即可。 
 * (思路小结:’建两个表,一’方不动,’多’方添加一个外键字段)*

//建立人员表
CREATE TABLE people(
    id VARCHAR(12) PRIMARY KEY,
    sname VARCHAR(12),
    age INT,
    sex CHAR(1)
);
INSERT INTO people VALUES('H001','小王',27,'1');
INSERT INTO people VALUES('H002','小明',24,'1');
INSERT INTO people VALUES('H003','张慧',28,'0');
INSERT INTO people VALUES('H004','李小燕',35,'0');
INSERT INTO people VALUES('H005','王大拿',29,'1');
INSERT INTO people VALUES('H006','周强',36,'1');
 //建立车辆信息表
CREATE TABLE car(
    id VARCHAR(12) PRIMARY KEY,
    mark VARCHAR(24),
    price NUMERIC(6,2),
    pid VARCHAR(12),
    CONSTRAINT fk_people FOREIGN KEY(pid) REFERENCES people(id)  #给表car下的pid创建与表people下的id关联的外键约束,产生一对多的关系,即一辆车可以属于多个人
);
INSERT INTO car VALUES('C001','BMW',65.99,'H001');
INSERT INTO car VALUES('C002','BenZ',75.99,'H002');
INSERT INTO car VALUES('C003','Skoda',23.99,'H001');
INSERT INTO car VALUES('C004','Peugeot',20.99,'H003');
INSERT INTO car VALUES('C005','Porsche',295.99,'H004');
INSERT INTO car VALUES('C006','Honda',24.99,'H005');
INSERT INTO car VALUES('C007','Toyota',27.99,'H006');
INSERT INTO car VALUES('C008','Kia',18.99,'H002');
INSERT INTO car VALUES('C009','Bentley',309.99,'H005');

代码示例
示例代码

2.一对一关系

 例如:一个中国公民只能有一个身份证信息

 分析: 一对一的表关系实际上是 变异了的 一对多关系. 通过在从表的外键字段上添加唯一约束(unique)来实现一对一表关系.

#身份证信息表
CREATE TABLE card (
  id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
  code varchar(18) DEFAULT NULL,
  UNIQUE un_code (CODE) -- 创建唯一约束的目的,保证身份证号码(code)同样不能出现重复
);

INSERT INTO card VALUES(null,'210123123890890678'),
                       (null,'210123456789012345'),
                       (null,'210098765432112312');

#公民表
CREATE TABLE people (
  id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
  name varchar(50) DEFAULT NULL,
  sex char(1) DEFAULT '0',
  c_id int UNIQUE, -- 添加唯一约束,确保一对一
  CONSTRAINT fk_card_id FOREIGN KEY (c_id) REFERENCES card(code)  #在表公民下的c_id创建与表card下的code相关联的外键约束
);

INSERT INTO people VALUES(null,'zhangsan','1',1),
                         (null,'lisi','0',2),
                         (null,'wangwu','1',3);

代码示例
示例代码

3.多对多关系

 例如:学生选课,一个学生可以选修多门课程,每门课程可供多个学生选择。 
 分析:这种方式可以按照类似一对多方式建表,但冗余信息太多,好的方式是实体和关系分离并单独建表,实体表为学生表和课程表,关系表为选修表,
其中关系表采用联合主键的方式(由学生表主键和课程表主键组成)建表。

#//建立学生表
CREATE TABLE student(
    id VARCHAR(10) PRIMARY KEY,
    sname VARCHAR(12),
    age INT,
    sex CHAR(1)
);
INSERT INTO student VALUES('S0001','王军',20,1);
INSERT INTO student VALUES('S0002','张宇',21,1);
INSERT INTO student VALUES('S0003','刘飞',22,1);
INSERT INTO student VALUES('S0004','赵燕',18,0);
INSERT INTO student VALUES('S0005','曾婷',19,0);
INSERT INTO student VALUES('S0006','周慧',21,0);
INSERT INTO student VALUES('S0007','小红',23,0);
INSERT INTO student VALUES('S0008','杨晓',18,0);
INSERT INTO student VALUES('S0009','李杰',20,1);
INSERT INTO student VALUES('S0010','张良',22,1);

# //建立课程表
CREATE TABLE course(
    id VARCHAR(10) PRIMARY KEY,
    sname VARCHAR(12),
    credit DOUBLE(2,1),
    teacher VARCHAR(12)
);
INSERT INTO course VALUES('C001','Java',3.5,'李老师');
INSERT INTO course VALUES('C002','高等数学',5.0,'赵老师');
INSERT INTO course VALUES('C003','JavaScript',3.5,'王老师');
INSERT INTO course VALUES('C004','离散数学',3.5,'卜老师');
INSERT INTO course VALUES('C005','数据库',3.5,'廖老师');
INSERT INTO course VALUES('C006','操作系统',3.5,'张老师');

# //建立选修表  创建中间表
CREATE TABLE sc(
    sid VARCHAR(10),
    cid VARCHAR(10),
      PRIMARY KEY(sid,cid),
      CONSTRAINT fk_student FOREIGN KEY(sid) REFERENCES student(id),  #在选修表中出现的数据一定是要在学生和课程表之下的,素以这里要添加两个外键约束
      CONSTRAINT fk_course FOREIGN KEY(cid) REFERENCES course(id)
);

INSERT INTO sc VALUES('S0001','C001');
INSERT INTO sc VALUES('S0001','C002');
INSERT INTO sc VALUES('S0001','C003');
INSERT INTO sc VALUES('S0002','C001');
INSERT INTO sc VALUES('S0002','C004');
INSERT INTO sc VALUES('S0003','C002');
INSERT INTO sc VALUES('S0003','C005');
INSERT INTO sc VALUES('S0004','C003');
INSERT INTO sc VALUES('S0005','C001');
INSERT INTO sc VALUES('S0006','C004');
INSERT INTO sc VALUES('S0007','C002');
INSERT INTO sc VALUES('S0008','C003');
INSERT INTO sc VALUES('S0009','C001');
INSERT INTO sc VALUES('S0009','C005');
示例代码

数据库的三范式

1.第一范式(确保每列保持原子性)

第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。

第一范式的合理遵循需要根据系统的实际需求来定。比如某些数据库系统中需要用到“地址”这个属性,本来直接将“地址”属性设计成一个数据库表的字段就行。但是如果系统经常会访问“地址”属性中的“城市”部分,那么就非要将“地址”这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。这样设计才算满足了数据库的第一范式,如下表所示。

即不可用一个地址列来代替省份列、城市列和详细地址列,这样取数据的时候比较容易。

上表所示的用户信息遵循了第一范式的要求,这样在对用户使用城市进行分类的时候就非常方便,也提高了数据库的性能。

                

2.第二范式(确保表中的每列都和主键相关)

第二范式在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。

比如要设计一个订单信息表,因为订单中可能会有多种商品,所以要将订单编号和商品编号作为数据库表的联合主键,如下表所示。

 订单信息表

这样就产生一个问题:这个表中是以订单编号和商品编号作为联合主键。这样在该表中商品名称、单位、商品价格等信息不与该表的主键相关,而仅仅是与商品编号相关。所以在这里违反了第二范式的设计原则。

而如果把这个订单信息表进行拆分,把商品信息分离到另一个表中,把订单项目表也分离到另一个表中,就非常完美了。如下所示。

这样设计,在很大程度上减小了数据库的冗余。如果要获取订单的商品信息,使用商品编号到商品信息表中查询即可。

3.第三范式(确保每列都和主键列直接相关,而不是间接相关)

第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。

比如在设计一个订单数据表的时候,可以将客户编号作为一个外键和订单表建立相应的关系。而不可以在订单表中添加关于客户其它信息(比如姓名、所属公司等)的字段。如下面这两个表所示的设计就是一个满足第三范式的数据库表。

这样在查询订单信息的时候,就可以使用客户编号来引用客户信息表中的记录,也不必在订单信息表中多次输入客户信息的内容,减小了数据冗余。

注意事项:

1.第二范式与第三范式的本质区别:在于有没有分出两张表。

第二范式是说一张表中包含了多种不同实体的属性,那么必须要分成多张表,第三范式是要求已经分好了多张表的话,一张表中只能有另一张标的ID,而不能有其他任何信息,(其他任何信息,一律用主键在另一张表中查询)。

2.必须先满足第一范式才能满足第二范式,必须同时满足第一第二范式才能满足第三范式。

 练习题

练习题所用的数据脚本:

#课程表
CREATE TABLE `course` (
  `c_id` int(11) NOT NULL,
  `c_name` varchar(50) DEFAULT NULL,
  `t_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`c_id`),
  KEY `t_id` (`t_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `course` VALUES ('1', 'python', '1');
INSERT INTO `course` VALUES ('2', 'java', '2');
INSERT INTO `course` VALUES ('3', 'linux', '3');
INSERT INTO `course` VALUES ('4', 'web', '2');

#成绩表
CREATE TABLE `score` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `s_id` int(11) DEFAULT NULL,
  `c_id` int(11) DEFAULT NULL,
  `num` double DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;

INSERT INTO `score` VALUES ('1', '1', '1', '79');
INSERT INTO `score` VALUES ('2', '1', '2', '78');
INSERT INTO `score` VALUES ('3', '1', '3', '35');
INSERT INTO `score` VALUES ('4', '2', '2', '32');
INSERT INTO `score` VALUES ('5', '3', '1', '66');
INSERT INTO `score` VALUES ('6', '4', '2', '77');
INSERT INTO `score` VALUES ('7', '4', '1', '68');
INSERT INTO `score` VALUES ('8', '5', '1', '66');
INSERT INTO `score` VALUES ('9', '2', '1', '69');
INSERT INTO `score` VALUES ('10', '4', '4', '75');
INSERT INTO `score` VALUES ('11', '5', '4', '66.7');

#学生表
CREATE TABLE `student` (
  `s_id` varchar(20) NOT NULL,
  `s_name` varchar(50) DEFAULT NULL,
  `s_age` int(10) DEFAULT NULL,
  `s_sex` char(1) DEFAULT NULL,
  PRIMARY KEY (`s_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `student` VALUES ('1', '鲁班', '12', '');
INSERT INTO `student` VALUES ('2', '貂蝉', '20', '');
INSERT INTO `student` VALUES ('3', '刘备', '35', '');
INSERT INTO `student` VALUES ('4', '关羽', '34', '');
INSERT INTO `student` VALUES ('5', '张飞', '33', '');

#老师表
CREATE TABLE `teacher` (
  `t_id` int(10) NOT NULL,
  `t_name` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`t_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `teacher` VALUES ('1', '大王');
INSERT INTO `teacher` VALUES ('2', 'alex');
INSERT INTO `teacher` VALUES ('3', 'egon');
INSERT INTO `teacher` VALUES ('4', 'peiqi');

数据脚本
练习题数据脚本

1、新建一个数据库db3

插入练习题数据脚本,后生成四张表:

四张表之间的数据间的关系:学生表中的s_id和成绩表中的s_id是关联的、老师表中的t_id和课程表中的t_id是关联的、课程表中的课程编号c_id和成绩表中的c_id是关联的。

2、查询

(1)查询学习课程"python"比课程 "java" 成绩高的学生的学号;   

        因为课程表中只有课程名字和课程编号没有成绩,在成绩表中有课程成绩和课程编号没有课程名字,所以先联合课程表和成绩表将python和java的成绩先找出来

   1)select * from course,score where course.c_id=score.c_id

    

  2)select num from course,score where course.c_id=score.c_id and course.c_name='python';

       select num from course,score where course.c_id=score.c_id and course.c_name='java';

   

  3)select score.s_id,score.num from course,score where course.c_id=score.c_id and course.c_name='python';

   select score.s_id,score.num from course,score where course.c_id=score.c_id and course.c_name='java';

   

  3)select * from (select score.s_id,score.num from course,score where course.c_id=score.c_id and course.c_name='python') as python,

        (select score.s_id,score.num from course,score where course.c_id=score.c_id and course.c_name='java') as java
        where python.s_id=java.s_id and python.num > java.num

  

  4)select student.s_id,student.s_name from (select score.s_id,score.num from course,score where course.c_id=score.c_id and course.c_name='python') as python,

                                                                           (select score.s_id,score.num from course,score where course.c_id=score.c_id and course.c_name='java') as java,
                                                                          student
                    where python.s_id=java.s_id and python.num > java.num and python.s_id=student.s_id

  

 (2)查询平均成绩大于65分的同学的姓名和平均成绩(保留两位小数);

     分析:查询“同学姓名”要用到学生表,“成绩”要用到成绩表,学生表和成绩表之间的关联就是学生编号

  1) select * from student,score where student.s_id = score.s_id

   

  2)  select student.s_id,student.s_name,score.num from student,score where student.s_id = score.s_id

  

     3)    select student.s_id,student.s_name,avg(score.num) from student,score where student.s_id = score.s_id group by score.s_id having avg(num)>65

  

        4)    select student.s_id,student.s_name,round(avg(score.num),2) from student,score where student.s_id = score.s_id group by score.s_id having avg(num)>65

   (3)  查询所有同学的姓名、选课数、总成绩;

   分析:要用到学生表、课程表、成绩表

  1)  select student.s_id,student.s_name,count(score.s_id) from student,score where student.s_id=score.s_id group by score.s_id

   2)删除后:

    

  3)  select student.s_id,student.s_name,count(score.s_id) as sum_course,sum(score.num) as sum_score from student,score where student.s_id=score.s_id group by score.s_id

  

  (4)查询所有的课程的名称以及对应的任课老师姓名;

  1) select * from course,teacher where course.t_id=teacher.t_id

  

  2) select course.c_name,teacher.t_name from course,teacher where course.t_id=teacher.t_id

  

  (5) 查询没学过“alex”老师课的同学的姓名;

  1)  select course.c_id from teacher,course where teacher.t_id=course.t_id and teacher.t_name='alex'

  

  2)   select score.s_id from score where score.c_id in (select course.c_id from teacher,course where teacher.t_id=course.t_id and teacher.t_name='alex')

  

   3)  select * from student where student.s_id not in (select score.s_id from score where score.c_id in (select course.c_id from teacher,course where           teacher.t_id=course.t_id and teacher.t_name='alex') )

  

  (6)  查询学过'python'并且也学过编号'java'课程的同学的姓名;

     由于course表中python和java对应的有用的只有课程代码(c_id),而在student表中没有课程代码(c_id),有学生编号s_id;但是在score表中有c_id和学生编号s_id,所以引入score表

  1)select score.s_id from score,course where course.c_id=score.c_id and course.c_name in('python','java')

   

  2)select score.s_id,count(score.s_id) from score,course where course.c_id=score.c_id and course.c_name in('python','java')group by score.s_id

  

   3)select score.s_id,count(score.s_id) from score,course where course.c_id=score.c_id and course.c_name in('python','java') group by score.s_id having   count(score.s_id)>=2

  

  4) select score.s_id,count(score.s_id),student.s_name from score,course,student where course.c_id=score.c_id and student.s_id=score.s_id and          course.c_name in('python','java') group by score.s_id having                      count(score.s_id)>=2

  

  (7)查询挂科超过两门(包括两门)的学生姓名;

  1)select * from score where score.num<70 

  

  2)select score.s_id from score where score.num<70 group by score.s_id having count(score.s_id)>=2

  

  3)select score.s_id,student.s_name from score,student where student.s_id=score.s_id and score.num<70 group by score.s_id having count(score.s_id)>=2

  

  (8)查询选修了全部课程的学生姓名;

     1)select count(course.c_id) from course 

    

    2)  select * from score group by score.s_id having count(*)=(select count(course.c_id) from course )  #这一句有错误,但是思路是对的们就是先查询出课程的总数,然后给score表根据课程分组,根据s_id将学生数累加起来,和总的课程相比较就可以了

  (9)查询至少有一门课程与“貂蝉”同学所学课程相同的同学姓名;

    1)  select score.c_id from student,score where student.s_id=score.s_id and student.s_name='貂蝉'

    

    2)select score.s_id from score where score.c_id in (select score.c_id from student,score where student.s_id=score.s_id and student.s_name='貂蝉')

    

    3)select score.s_id,student.s_name from score,student where student.s_id=score.s_id and score.c_id in (select score.c_id from student,score where                          student.s_id=score.s_id and student.s_name='貂蝉')

    

      4)去掉貂蝉的名字:select score.s_id,student.s_name from score,student where student.s_id=score.s_id and score.c_id in (select score.c_id from student,score where student.s_id=score.s_id and student.s_name='貂蝉') and student.s_name!='貂蝉'

    

转自:https://www.cnblogs.com/wangfengming/articles/7929118.html

原文地址:https://www.cnblogs.com/YiYA-blog/p/10261387.html