数据库约束:数据库会自动对数据的合法性进行校验检查的一系列机制,来保证数据库中能够避免插入或修改一些非法的数据。
创建表时,指定某列不为空:
CREATE TABLE student2(
id int NOT NULL,
sn int ,
name varchar(20)
);
指定某个列的值是唯一的,会对插入和修改操作进行限制
DROP TABLE IF EXISTS student2;
CREATE TABLE student2(
id int NOT NULL,
sn int UNIQUE,
name varchar(20)
);
指定插入数据时,某个列没有赋值时有一个自定义的默认值“未知”,如果没有定义默认值,会使用MySQL自己定义的默认值 NULL。
DROP TABLE IF EXISTS student2;
CREATE TABLE student2(
id int NOT NULL,
sn int UNIQUE,
name varchar(20) DEFAULT '未知'
);
INSERT INTO student2 VALUES(1,10000,NULL);
INSERT INTO student2(id,sn) VALUES(2,10001);
INSERT INTO student2 VALUES(3,10002,'唐三藏');
指定某一列(或多个列的结合)为主键。对有主键的表来说,每次插入或修改数据,会先进行查询操作。
DROP TABLE IF EXISTS student2;
-- 指定id为主键
CREATE TABLE student2(
id int NOT NULL PRIMARY KEY,
sn int UNIQUE,
name varchar(20) DEFAULT '未知'
);
对于整数类型的主键,常搭配自增长 auto_increment来使用,插入数据对应字段不给值时,会自动分配,使用最大值+1;如果手动指定了id ,最大值也会更新
-- 主键是 NOT NULL 和 UNIQUE 的结合,可以不用 NOT NULL
id int PRIMARY KEY AUTO_INCREMENT,
-- NULL 是让数据库自行分配
INSERT INTO student2 VALUES
(NULL,10000,'唐三藏'),
(NULL,10001,'孙悟空'),
(NULL,10002,'猪悟能');
INSERT INTO student2 VALUES
(5,10004,'宋江'),
(NULL,10005,'武松');
拓展:
但是在这里 id 的自动分配有一定局限性。如果是单个mysql服务器,是没问题的;如果是分布式系统,有多个mysql服务器构成的集群,这时依靠自增主键就不行了。
分布式系统应用在面临的数据量很大(大数据),客户端的请求量比较大(高并发),一台服务器就搞不定了,需要多台服务器(分布式)。
一台服务器主机的硬盘空间有限,当有一个表或几个表,数据量特别大,就要使用分库分表来存储,如果这时需要插入一个商品,那商品id如何分配?如何保证存放商品的数据库中的id与其他的数据库id不重复呢?
分布式系统中有生成唯一id的算法,实现算法方式有很多,但又一个通用公式:分布式唯一id = 时间戳+机房编号/主机编号+随机因子(这里的+ 是字符串拼接,而不是算术相加)。
如果添加商品速度比较慢,直接使用时间戳就可以;但一个时间戳内要添加多个商品,是要落到不同的主机上,保证同一时间添加到不同主机上的上的商品编号是不同的,随机因子有概率生成相同因子,但概率很小,在一定范围内,误差很小可以忽略不计。
外键用于关联其他表的主键或唯一键。
语法:
-- REFERENCES 英语有参考意思,在这里是说 列名1 参考其他表的 列名2,也就是说 列名1的值必须在列名2中出现过
-- 外键的值依赖于主键的值
FOREIGN KEY (列名1) REFERENCES 表名(列名2)
示例:创建班级表classes,id为主键
CREATE TABLE classes (
id int PRIMARY KEY AUTO_INCREMENT,
name varchar(20),
caparity int
);
创建学生表student,一个学生对应一个班级,一个班级对应多个学生。使用id为主键,classes_id为外键,关联班级表id
CREATE TABLE student3 (
stuId int PRIMARY KEY AUTO_INCREMENT,
stuNum int UNIQUE,
stuName varchar(20) DEFAULT '未知',
classesId int,
FOREIGN KEY (classesId) REFERENCES classes(id)
);
-- 插入班级信息
INSERT INTO classes values(21070501,'软工一班',40),(21070502,'软工二班',40),(21070503,'软工四班',40);
-- 插入学生信息
INSERT INTO student3 values(410180,2107211001,'张三',21070501);
-- 在子表(参照表)执行插入和更新操作时会自动在相应的表进行查询是否已经存在,若不存在,操作就会失败
INSERT INTO student3 values(410181,2107211002,'李四',21070504);
-- 在父表(被参照表),执行更新操作时,会自动查询被参照列的值是否被引用,若被引用,操作就会失败
UPDATE classes SET id = 21070504 where id = 21070501;
UPDATE classes SET name = '软一' where name = '软工一班';
-- 在父表(被参照表),执行删除操作时,删除操作是删除满足条件的一行或多行记录的数据,只要涉及被参照列的值 被引用的操作都会失败(和上述表述基本相同)
delete from classes where name = '软工一班';
delete from classes where name = '软工四班';
如果要清空(delete)父表(被参照表),必须先将所有主键被引用的所有子表(参照表)中外键所在行的记录清空,删除表也是同样道理。
DELETE FROM student3;
DELETE FROM classes;
MySQL使用时不报错,但忽略该约束。
DROP TABLE IF EXISTS student3;
CREATE TABLE student3 (
stuId int PRIMARY KEY AUTO_INCREMENT,
stuNum int UNIQUE,
stuName varchar(20) DEFAULT '未知',
stuSex varchar(1),
CHECK (stuSex = '男' or stuSex ='女'),
classesId int,
FOREIGN KEY (classesId) REFERENCES classes(id)
);
CREATE TABLE course (
id INT PRIMARY KEY auto_increment,
name VARCHAR(20)
);
CREATE TABLE score (
id int PRIMARY KEY auto_increment,
score DECIMAL(3, 1),
student_id int,
course_id int,
FOREIGN KEY (student_id) REFERENCES student(id),
FOREIGN KEY (course_id) REFERENCES course(id)
);
语法:
-- 插入查询结果 要求列的个数和类型一一对应
INSERT INTO 表名[(列名[, 列名...])] SELECT ...
示例:创建一张用户表,设计有name姓名、email邮箱、sex性别、mobile手机号字段。需要把已有的
学生数据复制进来,可以复制的字段为name、stuSex
-- 创建用户表
CREATE TABLE user (
id INT primary key auto_increment,
name VARCHAR(20) comment '姓名',
age int comment '年龄',
email VARCHAR(20) comment '邮箱',
sex varchar(1) comment '性别',
mobile varchar(20) comment '手机号'
);
-- 将学生表中指定数据复制到用户表
insert into user(name, sex) select name, stuSex from student3
常见的统计总数、计算平局值等操作,可以使用聚合函数来实现,常见的聚合函数有:
示例:
-- 统计student表里有多少个学生
select count(*) from student;
--统计有多少个id,值为 NULL 的不会被 COUNT 统计
select count(id) from student;
--创建新的学生表,课程表,还有成绩表
create table course(
id int primary key auto_increment comment '课程号',
name varchar(20) comment '课程名'
);
create table student(
id int unique auto_increment comment '身份证号',
sn int primary key comment '学号',
name varchar(20) comment '姓名'
);
create table stu_grade(
sn int comment '学号',
course_Id int comment '课程号',
grade decimal(3,1) comment '成绩',
primary key (sn,course_Id),
foreign key(sn) references student(sn),
foreign key(course_Id) references course(id)
);
-- 插入数据
insert into student values(202311301,1001,'李四'),
(202311302,1002,'张三'),
(202311303,1003,'王五');
insert into course values(1,'计算机网络'),
(2,'数据结构与算法'),
(3,'计算机组成原理'),
(4,'数据库系统概论');
insert into stu_grade values(1001,1,87.5),(1001,2,77),(1001,3,90),(1001,4,65.5);
insert into stu_grade values(1002,1,80),(1002,2,90.5),(1002,3,67),(1002,4,83.5);
insert into stu_grade values(1003,1,78.5),(1003,2,90.5),(1003,3,84),(1003,4,93);
--统计课程号为1的总成绩
select sum(grade) from stu_grade where course_Id = 1;
--统计课程号为1的不及格(<60)的总分,没有结果返回NULL
select sum(grade) from stu_grade where course_Id = 1 and grade < 60;
-- 统计课程号为1的平均成绩
select avg(grade) from stu_grade where course_Id = 1;
-- 查找课程号为1的最高分
select max(grade) from stu_grade where course_Id = 1;
-- 查找课程号为1的最低分
select max(grade) from stu_grade where course_Id = 1;
group by 针对指定列进行分组,将值相同的分到同一组中,再对分的组使用聚合函数。
语法:
select column1 [, sum(column2), ..] from table group by column1,column3;
示例:
-- 查询每门课程的总成绩
select course_Id,sum(grade) from stu_grade group by course_Id;
-- 不使用聚合函数 ,查询的结果是一组的某个代表数据
select course_Id,grade from stu_grade group by course_Id;
group by 往往要搭配聚合函数使用,否则查询结果就没有意义
group by 子句进行分组以后,需要对分组结果再进行条件过滤时,不能使用where语句,而是用 having
示例:
-- 挑选出所有课程中总分最低于250的
select course_Id,sum(grade) from stu_grade group by course_Id having sum(grade) < 250;
实际开发中往往数据来自不同的表,所以需要多表联合查询。多表查询是对多张表的数据取笛卡尔积:
同时,联合查询可以对表取别名
以上述4.1创建的course、student、stu_grade 三张表及插入的数据为例
语法:
select 字段 from 表1 别名1 [inner] join 表2 别名2 on 连接条件 and 其他条件;
select 字段 from 表1 别名1,表2 别名2 where 连接条件 and 其他条件;
示例:
-- 查询学生李四的每门课程的成绩
select student.sn,student.name,course.name,grade from student,course,stu_grade
where student.sn = stu_grade.sn and
stu_grade.course_Id = course.id and
student.name ='李四';
外连接分为左外连接和右外连接。如果联合查询,左侧的表完全显示我们就说是左外连接;右侧的表完全显示我们就说是右外连接。
语法:
-- 左外连接,表1完全显示
select 字段 from 表名1 left join 表名2 on 连接条件;
-- 右外连接,表2完全显示
select 字段 from 表名1 right join 表名2 on 连接条件;
-- 插入一条没有考试的学生的信息
insert into student values(202311304,1004,'王麻子');
-- 查找所有学生的成绩--要求没有考试的学生也要显示出来
select student.sn,name,course_Id,grade from student left join stu_grade on student.sn = stu_grade.sn;
自连接是指在同一张表连接自身进行查询
示例:
-- 查询 计算机网络成绩比数据结构与算法好的学生信息和成绩
-- 先查询 计算机网络 比 课程 数据结构与算法 的课程号
select id,name from sourse where name ='计算机网络' or name ='数据结构与算法';
-- 查询计算机网络成绩比数据结构与算法成绩高的信息
select s1.sn,s1.grade '计算机网络',s2.grade '数据结构与算法'
from stu_grade s1,stu_grade s2
where s1.course_Id = 1 and
s2.course_Id = 2 and
s1.sn = s2.sn and
s1.grade > s2.grade;
以上查询只显示了成绩信息,并且是分布执行的,要显示学生及成绩信息且用一条语句实现:
select student.*,s1.grade '计算机网络',s2.grade '数据结构与算法'
from student,stu_grade as s1,stu_grade as s2 ,course as c1,course as c2
where student.sn = s1.sn and
s1.sn = s2.sn and
c1.name = '计算机网络' and
c2.name = '数据结构与算法' and
s1.course_Id = c1.id and
s2.course_Id = c2.id and
s1.grade > s2.grade;
子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询
select student.sn,student.name from student,stu_grade
where student.sn = stu_grade.sn and
stu_grade.course_Id = (select id from course where course.name ='数据结构与算法');
-- 用 in 关键字
select * from stu_grade where course_id in (
select id from course where name='计算机网络' or name='计算机组成原理');
– 用 not in 关键字
select * from score where course_id not in (
select id from course where name!=‘语文’ and name!=‘英文’);
```
-- 使用 exists
select * from stu_grade where exists (
select course_Id from course where (name='计算机网络' or name='计算机组成原理') and id = course_Id);
-- 使用 not exists
select * from stu_grade where not exists (
select course_Id from course where (name !='计算机网络' and name !='计算机组成原理') and id = course_Id);
在实际应用中,为了合并多个select的执行结果,可以使用集合操作符 union,union all。使用UNION 和 UNION ALL时,前后查询的结果集中,字段需要一致
select * from course where id < 3 union select * from course where name = '数据库系统概论';
--使用 or 实现
select * from course where id < 3 or name = '数据库系统概论';
select * from course where id < 3 union all select * from course where name = '数据结构与算法';
– select 的列的数量和类型与插入的要保持一致
insert into 表名 [ 列1, [列2, ,…] ] select …
查询
select … from 表1,表2 where 条件
select … from 表1 [inner] join 表2 on 条件
left 左外连接 ,表1 全部显示,如果表2没有,对应列置空
select … from 表1 left / right join 表2 on 条件
right 右外连接 ,表2 全部显示,如果表1没有,对应列置空
select … from 表1 left / right join 表2 on 条件
select … from 表1 ,表1 where 条件
select … from 表1 join 表1 on 条件
– 单行子查询
select … from 表 where 列 = (select … from …)
– 多行子查询
select … from 表 where 列 in (select … from …)
– from 中的子查询 — 临时表
select … from 表 (select … from …)as tmp where 条件
– union:去除重复数据的合并查询 ,以第1个select 查询的列作为合并后的首要的列
select … from … where 条件 union select … from … where 条件
– union all:不去除重复数据的合并查询
select … from … where 条件 union all select … from … where 条件
SQL查询中各个关键字的执行先后顺序: from > on> join > where > group by > with > having >select > distinct > order by > limit