数据库多表

多表操作

数据完整性

保证用户输入的数据保存到数据库中是正确的;
做法:
在创建表的时候给表添加约束
完整性的分类

  • 1.实体完整性
  • 2.域完整性
  • 3.引用完整性

实体完整性
1.1实体:
表中的一行数据,一行数据表示的就是一个实体

1.2作用:标识每一行数据不能重复

1.3约束类型

1.3.1主键约束(primary key)
1.3.2唯一约束(unique)
1.3.3自动增长(auto_increment)

1.3.1主键约束:primary key

每个表里必须要有个主键,主键特点 不重复,且唯一,不能为null
找不到唯一主键 ,就建一个唯一的字段

(1)添加主键约束

格式:

  1. Creat table 表名(字段名1 类型 primary key,字段名2 类型 约束条件2,…);
  2. Creat table 表名(字段1 类型,字段2 类型,… ,primary key(字段));
    //最后写主键约束
(2)给已经创建好的表添加约束

格式:
Alter table 表名 add primary key(需要添加约束的字段);

(3)多个字段合在一起作为主键——联合主键:

格式:
Creat table 表名(字段1 类型,字段2 类型,… ,primary key(,字段1,字段2等));
这个格式可以在primary key里面放一个字段。表示一个主键
例子:
创建表格
– 方式1,带主键的创建表

CREATE TABLE stu(
id INT PRIMARY KEY,
NAME VARCHAR(20) );

– 因为主键的特点是唯一 所以不能有重复的主键出现
INSERT INTO stu VALUES(1,'zhangsan ');
– 删除 stu表

 DROP TABLE stu;

– 方式2,创建表后添加主键约束

CREATE TABLE stu(
id INT , 
NAME VARCHAR(20) );

– 给已经创建好的表添加主键约束

ALTER TABLE stu ADD PRIMARY KEY(id);
联合主键

某一个字段并不能实现效果使用联合主键
格式:
– 方式3 好处可以添加联合主键
– 创建 student表

CREATE TABLE stu(
id INT,
NAME VARCHAR(20),
PRIMARY KEY(id) ); -- 一个主键

– 删除 stu表

DROP TABLE stu;
CREATE TABLE stu(
sid INT,
classid INT,
NAME VARCHAR(20),
PRIMARY KEY(sid,classid) );-- 两个联合在一起的主键

-- 主键约束 每个单元格的值都是和这个字段的其他单元格比较
1.3.2唯一约束:unique

数据唯一
创建唯一约束;
//约束1可以不存在
Create table 表名(字段1 类型 约束1,字段2 类型unique,…);
主键约束和唯一约束,都是唯一,但是唯一约束可以为null;
语法规定:主键只能有一个,而unique可以有多个
例子:
就是单元个数据唯一 但可以有多个null值
创建表格

CREATE TABLE stu(
 Id INT PRIMARY KEY,
NAME VARCHAR(20) UNIQUE
);
添加内容·
INSERT INTO stu VALUES(1,'aaa');
INSERT INTO stu VALUES(2,'aaa');
INSERT INTO stu VALUES(2,'bbb');
INSERT INTO stu VALUES(3,NULL);
-- 唯一约束,但是这里可以给多个null
SELECT * FROM stu;
-- 主键 是唯一且不能是null  一个表只能有一个主键
-- 唯一约束   是唯一  但是可以有多个null 可以有多个唯一约束
INSERT INTO stu VALUES(NULL,NULL);
1.3.3自动增长:auto_increment

自动增长
一般给主键自动增长。必须是整数类型;
格式:
1)创建表时
//和primary key一起使用
Create table 表名(字段1 类型 primary key auto_increment,约束1,字段2 类型,…);
//auto_increment 单独使用;注意有key的都是主键
Create table 表名(字段1 类型auto_increment key,约束1,字段2 类型,…);

2)添加内容时:
Insert into 表名 values(null,内容2,…);
自动增长当没有给初值的时候从1开始,给一个初值,之后的insert种写为null,就实现了从初值开始的自动增长;删除对应的自动增长值之后,再添加不会进行缩进
赋初值的insert :
Insert into 表名 values(初值,内容2,…);
之后:
Insert into 表名 values(null,内容2,…);
……………………
3)创建表之后 并且已经设置了主键但没有设置自增长,添加自增长
更改列名字和类型
格式:
alter table 表名 change 字段名字 新名字 新类型

AUTO_INCREMENT //
ALTER TABLE student CHANGE  stu_id stu_id INT AUTO_INCREMENT;

  
CREATE TABLE stutest(id INT PRIMARY KEY AUTO_INCREMENT,NAME VARCHAR(20) UNIQUE);
INSERT INTO stutest VALUES(2,'li');//初始赋值
INSERT INTO stutest VALUES(NULL,'l');//自动增长
INSERT INTO stutest VALUES(NULL,'i'); //自动增长
INSERT INTO stutest VALUES(NULL,'li2'); //自动增长
DELETE FROM stutest WHERE id = 5;//删除id=5的记录
 -- 删除之后,不会自动向前进位,也就是下一个记录是从id为6开始
INSERT INTO stutest VALUES(NULL,'li3');//再添加记录,记录的id=6
一般给主键自增长,必须是整数类型
-- 创建主键自增长
CREATE TABLE stu(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20) );
-- 怎么添加自增长
//将自增长的值选择写为null就可以实现自增长
INSERT INTO stu VALUES(NULL,'ddd');
SELECT * FROM stu;
-- 删除 id 为4的人
DELETE FROM stu WHERE id =4;

实体完整性: 每个单元格的 值都是和这个字段的其他单元格比较

2. 域完整性

作用:使填入的内容为给定的格式;
约束:数据类型,非空,默认约束

2.1数据类型

int varchar(),char(),double…这些类型

2.2 非空—Not null:

格式:
Create table 表名(字段1 类型 not null,…);
添加的时候不写成null,就行;
例子:Create table 表名(name varchar(50) not null,…);
就是限定写入内容不能为null
Name非空
– 非空约束

 DROP TABLE stu;
 CREATE TABLE stu(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(20) NOT NULL);
-- 测试
 INSERT INTO stu VALUES(NULL,NULL);-- 报错

2.3 默认值—Default:

格式:
Create table 表名(字段1 类型 default 默认值)
添加为默认值的时候:
Insert into 表名values (default);//写上default 就是默认值了
//默认约束:就是在没有赋值的时候,给一个默认值;
例子:Create table 表名(gender varchar(50) default ‘female’,…);
例子:
创建表格

CREATE TABLE stu(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(20) NOT NULL,
	sex VARCHAR(20) DEFAULT '男'
 );
SELECT * FROM stu;

向表中添加内容
//要设置为默认值就写为default就ok

INSERT INTO stu VALUES(NULL,'ddd',DEFAULT);
-- 错误写法 INSERT INTO stu VALUES(NULL,'ddd',);

3. 引用完整性

次表的外键可以不是主键,但关联的主表键必须是主键
至少两张表;
外键约束:
方法一:创建次表并向次表添加次表字段内容以及外键约束
Create table 次表名(内容…,constraint 外键名 foreign key(次表关联项) references 主表(主表字段))
两个链接的键之间的数据类型必须一致
方法二:创建好表之后,添加外键
//需要注意的是,(references 主表 )主表字段必须是主键
次表依赖于主表;

Alter table 次表 add foreign key(次表字段) reference 主表(主表字段);

删除的方式

删除有主外键关系的表;
先删除次表再删除主表

-- 外键约束
-- 创建一个表 部门表
CREATE TABLE dept(
did INT PRIMARY KEY AUTO_INCREMENT,
dname VARCHAR(20)
); 
INSERT INTO dept VALUES(NULL,'市场部');
INSERT INTO dept VALUES(NULL,'人事部');
INSERT INTO dept VALUES(NULL,'财务部');
-- 员工表 emp
CREATE TABLE emp(
eid  INT PRIMARY KEY AUTO_INCREMENT,
ename VARCHAR(20),
sal DOUBLE,
brithday  DATE,
sex VARCHAR(20),
dno INT
);
INSERT INTO emp VALUES
	(NULL,'张三',8000,'1989-09-09','女',1);
INSERT INTO emp VALUES
	(NULL,'李四',9000,'1988-09-09','女',1);
INSERT INTO emp VALUES
	(NULL,'王五',2000,'2001-09-09','女',2); 
INSERT INTO emp VALUES
	(NULL,'赵六',3000,'2001-09-09','女',3) ;
-- 添加一条数据
INSERT INTO emp VALUES
	(NULL,'赵六',3000,'2001-09-09','女',10) ;
-- 应该员工都属于某个部门
-- 这里的员工的dno(部门编号) 应该取自于dept表里的did
-- 这个时候使用外键约束来实现
/* 我们做了一个给emp表中插入一条部门编号不存的员工,
这种情况不应该发生,这个时候多个表之间应该添加外键约束 
*/
-- 外键   FOREIGN KEY
-- 第一种添加格式  主次表已经存在了
ALTER TABLE emp ADD FOREIGN KEY(dno)
	REFERENCES dept(did);
-- 这个时候 就不能随意添加了
INSERT INTO emp VALUES
	(NULL,'赵六',3000,'2001-09-09','女',10) ;//报错
-- 创建表的时候直接添加
-- 删除有主外键关系的表 顺序? 先删除次表
DROP TABLE emp;//次表
DROP TABLE dept;//主表
--第二种添加格式 存在主表,次表未创建
-- 创建一个表 部门表—主表
CREATE TABLE dept(
	did INT PRIMARY KEY AUTO_INCREMENT,
	dname VARCHAR(20)
); 
INSERT INTO dept VALUES(NULL,'市场部');
INSERT INTO dept VALUES(NULL,'人事部');
INSERT INTO dept VALUES(NULL,'财务部');
-- 员工表 emp
CREATE TABLE emp(
eid  INT PRIMARY KEY AUTO_INCREMENT,
ename VARCHAR(20),
sal DOUBLE,
brithday  DATE,
sex VARCHAR(20),
dno INT,
CONSTRAINT fk FOREIGN KEY(dno) REFERENCES dept(did)
);
-- 格式
CONSTRAINT 外键名 FOREIGN KEY(次表的外键字段)REFERENCES 主表(字段)

3.1表和表之间的关系

一对一 1~1
一个身份证号对应一个人,一个人只有一个id
将两个字段设置为主键,然后设置其中一个为外键就可以了
一对多 1~n
一个部门多个员工,一个员工只属于一个部门
多对多 n~m
学生选课
一个学生可以选多门课,一门课可以有多个学生选
在设置多表之间的关系的时候要区分主次关系

4.连接查询

多表查询;
*Select from 表一,表二,…;
//会产生一堆数据,这堆数据的数量等于两个表的记录数的乘积;这就是笛卡尔积
解决办法:消除笛卡尔积,需要找出关联条件
*Select from 表一,表二,… Where 条件;
明确知道是哪张表的数据;
*Select from 表一,表二,… Where 表名1.字段 判断条件 表名2.字段;
取别名;
*Select from 表一 别名1,表二 别名2,… Where 别名1.字段 判断条件 别名2.字段;

交叉连接

Cross join 关键字 (可以省略)
**Select from 表一,表二,…;
Select from 表一Cross join表二,…;

内链接

关键字inner join
显示内链接
//inner 可以省略
*格式: select from 表1 inner join 表2 on 关联条件
三个或者以上的显示内链接格式
*Select from 表1 inner join 表2 on 条件1 inner 表3 on 条件3 ;
//注意 on的条件中,必须是表1和表2的关系,如果写成表一和表三的关系会出错;条件1如果是表一、二的关系,则格式应为
Select *from 表1 inner join 表3 on 条件1 inner 表3 on 条件3 ;

隐式内链接

格式: *select from 表1,表2 where 关联条件—>这是常用的方式
//都可以其别名
//内链接取的是两个表之间的交集

外连接

//显示所有的信息,
左外连接
Left outer join

//左表为主表,主表的信息会全部显示 即使由null,也会显示,而交集 即内链接,显示的是交集的内容
格式:select 要显示的内容 from 主表名 left outer join 次表名 on 条件;
右外链接
Right outer join

//右表为主表
格式:select 要显示的内容 from 次表名 right outer join 主表名 on 条件

子查询

子查询又叫嵌套查询,一个select 语句种包含一个完整的select 语句
如果一个查询语句种存在两个或者两个以上的select 语句,那么就是子查询语句
子查询:一个查询的条件依赖于另一个查询的结果
括号内部的返回值要被用于再次查询;
例如

SELECT *FROM student WHERE birthday<(SELECT birthday FROM student WHERE student.sname='张三');

所以内部的值要是birthday 而不是*

带关键字的子查询;

/*交叉连接:cross join
交叉连接:查询到的是两个表的笛卡尔积。
语法:
select * from 表1 cross join 表2;
select * from 表1,表2;
内连接:inner join(inner是可以省略的)  其实就是 消除笛卡尔积的查询
显示内连接:在SQL中显示的调用inner join关键字
语法:select * from 表1 inner join 表2 on 关联条件;
隐式内连接:在SQL中没有调用inner join关键字
语法:select * from 表1,表2 where 关联条件;
外连接:outer join(outer可以省略的)
左外连接:
语法:select * from 表1 left outer join 表2 on 关联条件;
右外连接
语法:select * from 表1 right outer join 表2 on 关联条件;
*/
-- 查询学生的班级信息
展示两个表格内容
SELECT * FROM student,classes;
-- 会产生一堆数据 这些数据刚好就是两张表的乘积(9*4)
-- 这一堆数据就是笛卡尔积  
-- 消除笛卡尔积的办法就是找关联条件 cno = cid 就是正确数据
消除笛卡尔积 // 下面的这句表达式隐式内链接
SELECT * FROM student,classes WHERE cno = cid;
-- 为了让我明确知道是哪张表中的字段
看清是那个字段
SELECT * FROM student,classes 
	WHERE student.cno = classes.cid;
-- 如果表名很长我看起来开很麻烦  咋办? 起别名
起别名
SELECT * FROM student s,classes  c
	WHERE s.cno = c.cid;
交叉连接:
 --  交叉连接: 关键字 cross join  关键字可以省略
交叉连接:查询到的是两个表的笛卡尔积。//就是和前面的显示两个表的数据结果一样
语法:
SELECT * FROM1 CROSS JOIN2;
SELECT * FROM1,2;
SELECT * FROM student CROSS JOIN classes; 
内链接
-- 为了消失笛卡尔积我们使用内连接
-- 关键字 inner join
-- 显式内连接  inner是可以省略的
-- 格式: 
select * from1 inner join2  on 关联条件
SELECT * FROM student s  INNER JOIN classes c ON s.cno = c.cid;
SELECT * FROM student s  JOIN classes c ON s.cno = c.cid;
-- 隐式内连接 // 这个用的最多
 SELECT * FROM student s , classes  c WHERE s.cno = c.cid;
-- 查询学生姓名选课名称和成绩 
-- 三个表  关联条件 s.sid = sc.sno c.cid = sc.cno
SELECT s.sname,c.cname,sc.score FROM student s,course c, stu_cour sc
WHERE s.sid = sc.sno AND sc.cno = c.cid;
显式内连接 做三表查询
-- 显式内连接  inner join 在做多表联查的时候,
-- 两个表的结果当作一张表和第三张表关联
SELECT s.sname,c.cname,sc.score FROM student s INNER JOIN  stu_cour sc
		ON  s.sid = sc.sno INNER JOIN course c ON sc.cno = c.cid;
-- 内连接其实取得是两个表中的交集
-- 想要显示所有学生的信息,	
使用外连接 (outer join-- 1.左外连接 left outer join  左边的表为主表 主表的所有信息都显示
-- 语法 :select * from 左表1 left outer join 右表2 on 关联条件
SELECT * FROM student s LEFT OUTER JOIN classes c ON s.cno = c.cid; 
-- 显示全部的班级信息
SELECT * FROM classes c LEFT OUTER JOIN student s  ON s.cno = c.cid; 
-- 2.右外连接 right outer join
-- 语法 :select * from 表1 right outer join 表2 on 关联条件
-- 右外连接 right outer join  右边的表为主表 主表的所有信息都显示
-- 显示所有的学生信息
SELECT * FROM classes c RIGHT OUTER JOIN    student s ON s.cno = c.cid; 
-- 子查询(非常重要)
-- 一个select 语句中包含另一个完整的select语句。
-- 子查询:一个查询的条件依赖另一个查询语句的结果
-- 查询生日比张三大的学生
-- 分解1:查询张三的生日是哪天 --1990-09-01
SELECT birthday FROM student WHERE sname = '张三';
-- 分解2:查询生日比1990-09-01 大的学生信息
SELECT * FROM student WHERE birthday>'1990-09-01';
-- 最终版本
SELECT * FROM student WHERE birthday >
	(SELECT birthday FROM student WHERE sname = '张三');
-- 查询生日比张三大的学生的班级信息 -- 两个表
SELECT * FROM student s ,classes c WHERE birthday >
		(SELECT birthday FROM student WHERE sname = '张三') 
AND s.`cno` = c.`cid`;
-- 查询于张三同一个班的学生的班级信息
-- 分解1 查询张三的班级编号   1
SELECT  cno FROM student  WHERE sname = '张三'; 
-- 分解2 查询班级编号是1的学生班级信息
SELECT * FROM student s ,classes c WHERE s.cno = 1 AND s.`cno` =c.`cid`;
-- 最终版本
SELECT * FROM student s ,classes c WHERE s.cno = 
	( SELECT  cno FROM student  WHERE sname = '张三')
	AND s.`cno` =c.`cid`;
-- 查询成绩高于2号课程最高的成绩的学生信息 
-- 分解1:查询2号课程的所有人的最大值
SELECT MAX(score) FROM stu_cour WHERE cno = 2; -- 91
 -- 分解2:查询学生信息 选课成绩高于91的人
SELECT * FROM stu_cour sc,student s WHERE sc.score>91 AND sc.`sno`=s.`sid`;
-- 最终版 
SELECT * FROM stu_cour sc,student s 
WHERE sc.score>(SELECT MAX(score) FROM stu_cour WHERE cno = 2) 
AND sc.`sno`=s.`sid`;
-- 带关键字的子查询
-- in (10,20)
-- all  所有
--  any  任何一个
-- 查询成绩高于2号课程最高的成绩的学生信息-- 第二种写法
 -- 大于所有 用all
--  -- 1分解:查询所有2号课程人的成绩
SELECT score FROM stu_cour WHERE cno = 2;
-- 找其他人里成绩大于 2 号所有成绩
SELECT * FROM student s,stu_cour sc WHERE s.`sid` = sc.`sno`
AND sc.`score`> ALL( SELECT score FROM stu_cour WHERE cno = 2);
-- 大于所有 >all 大于最大值  <all 小于最小值
-- 查询成绩高于2号课程任意一个学生的成绩的学生信息 
-- 大于任意一个 >any 其实就是大于最小值
SELECT * FROM student s,stu_cour sc WHERE s.`sid` = sc.`sno`
AND sc.`score`> ANY( SELECT score FROM stu_cour WHERE cno = 2);
-- 查询学生生日在91年之后的班级信息
-- 分解1:查询生日在91年之后的学生信息
SELECT cno FROM student WHERE birthday >'1991-01-01'; -- 1,2,3
-- 分解2:查询学生生日在91年之后的班级信息(1,2,3)
SELECT *FROM classes   WHERE cid IN 
		(SELECT cno FROM student WHERE birthday >'1991-01-01');

-- 查询班级编号 大于所有学生的班级编号的信息  4 
SELECT * FROM classes WHERE cid> ALL(SELECT cno FROM student);
-- 对表查询练习题
-- 查询班级名称和班级总人数    分组条件班级名称
SELECT c.cname,COUNT(*)FROM classes c,student s
	WHERE c.cid=s.`cno` GROUP BY c.cname;

-- 查询学生的姓名和学生所选的总课程的平均成绩     分组条件sname
SELECT s.sname,AVG(sc.score) FROM student s, stu_cour sc
	WHERE sc.`sno` = s.`sid` GROUP BY s.sname;
-- where 和having 	
-- 查询学生的姓名和学生所选课程的总数,显示选课数超过2门的学生姓名
SELECT s.sname,COUNT(*) FROM student s, stu_cour sc
	WHERE s.sid=sc.`sno` GROUP BY s.`sname`
	HAVING COUNT(*)>2;

数据备份和恢复

数据备份就是导出数据,不能导出库只能导出表;
不需要链接上数据库就可以直接备份
使用mysqldump 命令在windows控制下执行,无需登录mysql;
导出:
格式://没有空格 注意不要分号
Mysqldump -u用户名 -p密码 数据库名>生成的文件名字(路径及文件名自己命名).sql
导入:前提需要存在一个库

格式:
Mysql -u用户名 -p密码 数据库<目标文件(路径及文件名)

事务

事务:值得是逻辑上的一组操作,组成这组操作的每个逻辑单元
要么全成功,要么全失败
典型案例:转账;
转账一个人1000-100,另一个1000+100,时出现了断电异常,转账这个活动不会成功
什么时候会需要事务
查询其实不需要触发事务
Update delete insert 都需要
Mysql 是默认开启事务的,还可以手动开启
手动开启事务
格式:
Start transaction;
内容………………

Commit ;//表示执行从start 开始的内容进行执行 并关闭手动事务,事务变为默认开启;
Rollback;//表示撤回前面从start开始的所有操作,并关闭手动事务,事务变为默认开启;

发布了18 篇原创文章 · 获赞 11 · 访问量 1197

猜你喜欢

转载自blog.csdn.net/konmor/article/details/102520574