这里的所有操作,都以mysql为主
我这里不介绍简单的SQL语句了,但是可以提供简单sql语句的训练题目和答案,如果都练过的话,我相信,简单的 sql 语句将不在话下
数据库安装,配置什么的就不讲了,网上都有
这里,就介绍一下三大范式
同一数据表,不能出现相同的列名
每行数据唯一可分(加上一个主键)
一张表中不包含已经存在的其他表中已包含的非主关键字信息
比如有两张表一张是学生表,一张是学生信息表,如果学生表中包含了学生姓名,那学生信息表,就不要包含学生姓名了
倒霉催 Q
我这里给几个简单SQL语句的练习,看了就会了:
# 年级表
drop table if exists `grade`;
create table `grade` (
`gradeid` bigint not null comment '年级 id',
`name` varchar(50) comment '年级名称',
primary key (`gradeid`)
) engine=innodb default charset =utf8mb4 comment '年级表';
# 成绩
drop table if exists `score`;
create table `score` (
`scoreid` bigint not null comment '成绩id',
`stuno` bigint not null comment '学员编号',
`subjectid` bigint not null comment '科目 id',
`score` int(10) not null comment '分数',
`examtime` datetime not null comment '考试时间',
primary key (`scoreid`)
) engine=innodb default charset =utf8mb4 comment '成绩表';
# 学生表
drop table if exists stu;
create table `student` (
`stuid` bigint not null comment '学生 id',
`stuname` varchar(50) comment '学生名称',
`password` varchar(50) comment '登录密码',
`sex` varchar(10) comment '性别',
`gid` bigint not null comment '年级id',
`telphone` varchar(50) comment '电话',
`address` varchar(255) comment '地址',
`birthday` date comment '出生日期',
`email` varchar(50) comment '邮箱',
primary key (`stuid`)
) engine=innodb default charset =utf8mb4 comment '学生表';
# 科目表
drop table if exists `subject`;
create table `subject` (
`subjectid` bigint not null comment '科目 id',
`subjectname` varchar(50) comment '科目名称',
`studycount` int comment '课时',
`gradeid` bigint not null comment '年级 id',
primary key (`subjectid`)
) engine=innodb default charset =utf8mb4 comment '科目表';
-- 1. grade 表增加一个阶段,“就业期”
insert into grade value (1,'就业期');
-- 2.将第三阶段的学生的 gradeid 改为就业期的 id
update stu set gid=(select gradeid from grade where name='就业期') where gid=3;
-- 3.查询所有得了 100 分的学号
select stuno from score where score=100;
-- 4.查询所有 1989 年出生的学生(1989-1-1~1990-1-1)
select * from stu where birthday between '1989-1-1' and '1990-1-1';
-- 5.查询学生姓名为“金蝶”的全部信息
select * from stu where stuname='金蝶';
-- 6.查询 subjectid 为 8 的科目考试未及格(60 分)的学号和成绩
select stuno,score from score where scoreid=8 and score<60;
-- 7.查询第 3 阶段课时大于 50 的课程全部信息
select * from subject where gradeid=3 and studycount>50;
-- 8.查询 S1101001 学生的考试信息
select * from score where stuno='S1101001';
-- 9.查询所有第二阶段的女生信息
select * from stu where gid=2 and sex='女';
-- 10.“基于.NET 平台的软件系统分层开发”需要多少课时
select subjectname,studycount from subject where subjectname='基于.NET 平台的软件系统分层开发';
-- 11.查询“设计 MySchool 数据库”和“面向对象程序设计”的课时(使用 in)
select studycount from subject where subjectname in ('设计 MySchool 数据库','面向对象程序设计');
-- 12 查询所有地址在山东的学生信息
select * from stu where address like '%山东%';
-- 13 查询所有姓凌的单名同学
select * from stu where stuname like '凌%';
-- 14.查询 gradeid 为 1 的学生信息,按出生日期升序排序
select * from stu where gid=1 order by birthday asc;
-- 15.查询 subjectid 为 3 的考试的成绩信息,用降序排序
select * from score where subjectid=3 order by score desc;
-- 16.查询 gradeid 为 2 的课程中课时最多的课程信息
select * from subject where subjectid=2 order by studycount desc limit 0,1;
-- 17.查询北京的学生有多少个
select count(*) from stu where address like '%北京%';
-- 18.查询有多少个科目学时小于 50
select * from subject where studycount<50;
-- 19.查询 gradeid 为 2 的阶段总课时是多少
select sum(studycount) from subject where gradeid=2;
-- 20.查询 subjectid 为 8 的课程学生平均分
select avg(score) from score where subjectid=8;
-- 21.查询 gradeid 为 3 的课程中最多的学时和最少的学时
select max(studycount),min(studycount) from subject where gradeid=3;
-- 22.查询每个科目有多少人次考试
select subjectid,count(*) from score group by subjectid;
-- 23.每个阶段课程的平均课时
select gradeid,avg(studycount) from subject group by gradeid;
-- 24.查询每个阶段的男生和女生个数(group by 两列)
select gid,sex,count(*) from stu group by gid,sex;
BEGIN;
insert into grade values(2,'哈哈');
commit;
rollback;
BEGIN;
insert into grade values(3,'嘿嘿');
rollback;
commit;
-- 雇员表(employee):雇员编号(empid,主键),姓名(name),性别
-- (sex),职称(title),出生日期(birthday),所属部门(depid)
--
-- 部门(department):部门编号(depid,主键),部门名称(depname)
--
-- 工资表(salary):雇员编号(empid),基本工资(basesalary),职务工
-- 资(titlesalary),扣除(deduction)
# 雇员表
drop table if exists `employee`;
create table `employee` (
`id` bigint not null comment '雇员编号id',
`name` varchar(50) comment '姓名',
`sex` varchar(10) comment '性别',
`title` varchar(255) comment '职称',
`birthday` date comment '出生日期',
`dept_id` bigint comment '所属部门',
primary key (`id`)
) engine=innodb default charset =utf8mb4 comment '雇员表';
# 部门表
drop table if exists `department`;
create table `department` (
`id` bigint not null comment 'id',
`name` varchar(50) comment '部门名称',
primary key (`id`)
) engine=innodb default charset =utf8mb4 comment '部门表';
# 工资表
-- 工资表(salary):雇员编号(empid),基本工资(basesalary),职务工
-- 资(titlesalary),扣除(deduction)
drop table if exists `salary`;
create table `salary` (
`id` bigint not null comment 'id',
`emp_id` bigint comment '雇员 id',
`basesalary` double comment '基本工资',
`titlesalary` double comment '职务工资',
`deduction` double comment '扣除',
primary key (`id`)
) engine=innodb default charset =utf8mb4 comment '工资表';
-- 各表关系为
-- 雇员-部门:多对一
-- 工资-雇员:多对一
# 1. 修改表结构,在部门表中添加部门简介字段
alter table department add column dept_desc varchar(255);
# 2. 将李四的职称改为“工程师”,并将她的基本工资改成 2000,职务工资为 700
update employee,salary
set employee.title='工程师',salary.basesalary=2000
where employee.id = salary.emp_id and employee.name='李四';
# 3. 删除人事部门的部门记录
delete from department where name='人事部门';
# 4. 查询出每个雇员的雇员编号,实发工资,应发工资
select emp_id,basesalary+titlesalary-deduction as '实发工资',basesalary+titlesalary as '应发工资'
from salary;
# 5. 查询姓张且年龄小于 40 的员工记录
select * from employee
where name like '张%'
and DATE_ADD(now(),INTERVAL 40 year )<birthday;
# 6. 查询雇员的雇员编号,姓名,职称,部门名称,实发工资
select employee.id,employee.name,employee.title,department.name,(salary.basesalary+salary.titlesalary-salary.deduction) as '实发工资'
from employee,department,salary
where employee.id=salary.emp_id and employee.dept_id=department.id;
# 7. 查询销售部门的雇员姓名,工资
select employee.name,salary.titlesalary
from department,employee,salary
where employee.id=salary.emp_id and employee.dept_id=department.id and employee.name='销售部门';
# 8. 统计各职称的人数
select title,count(*)
from employee
group by title;
# 9. 统计各部门的部门名称,实发工资总和,平均工资
select department.name,sum(salary.basesalary+salary.titlesalary-salary.deduction) as '实发工资总和',avg(salary.basesalary+salary.titlesalary-salary.deduction) as '实发工资平均'
from department,employee,salary
where employee.id=salary.emp_id and employee.dept_id=department.id
group by department.name;
# 10. 查询比销售部门所有员工基本工资都高的雇员姓名
select employee.name
from employee,salary
where employee.id=salary.emp_id
and salary.titlesalary>(select max(salary.titlesalary)
from salary,department,employee
where employee.id=salary.emp_id and employee.dept_id=department.id
and department.name='销售部门');
DDL用来建库建表,修改列
这里,我们给出常用的数据类型
这里就讲一部分,因为数据库操作和背书差不多
delete: 是删除表数据,后面可以回滚
truncate: 是 drop 整张表,然后创建一个和原来一样的表;数据不能找回,但是比 delete 稍快
与用户、授权相关
查询语句
当需要分组查询时需要使用GROUP BY子句,例如查询每个部门的工资和,这说明要使用部分来分组。
注意:如果查询语句中有分组操作,则select后面能添加的只能是聚合函数和被分组的列名
为什么需要使用 group by?
假设我们有这么一个需求:查询每个学生的总分
这里我们先介绍一下,每个学生有多个成绩,但是,一个成绩,只对应一个学生,所以是一对多的关系;因为成绩是多,所以,成绩表中,有一列,代表的是学生的id
我们写一下需求的 SQL 语句:
select stu.name,sum(score)
from stu,score
where stu.id = score.stu_id
group by stu.name;
这样就能成功查询到每个学生的成绩总和:
这里,如果不使用 group by,那么,数据库没法判断我们要查询的成绩总和是和谁对应,会报错:
select stu.name,sum(score)
from stu,score
where stu.id = score.stu_id;
# group by stu.name;
其实我们想想,逻辑也对不上号,如果不分组,那么查询到的成绩总和,就是所有成绩的总和,也和我们的需求不符,只是数据库发现这个问题,给我们报错了。
是在 group by 后,在继续筛选
查询工资总和大于9000的部门编号以及工资和:
SELECT deptno, SUM(sal)
FROM emp
GROUP BY deptno
HAVING SUM(sal) > 9000;
这里,整理一下数据库中,所有数据类型
用来保证存放到数据库中的数据是有效的,即数据的有效性和准确性 确保数据的完整性 = 在创建表时给表中添加约束
完整性的分类:
建议这些约束应该在创建表的时候设置
多个约束条件之间使用空格间隔
示例:
create table student(
studentno int primary key auto_increment, loginPwd varchar(20) not null default '123456', studentname varchar(50) not null,
sex char(2) not null,
gradeid int not null,
phone varchar(255) not null,
address varchar(255) default '学生宿舍', borndate datetime,
email varchar(50)
);
不要小瞧这个知识点,只有掌握了这些关系,才能设计更好的表关系
客户和订单,分类和商品,部门和员工
多方塞入一方的 id
学生和课程,学生上多个课程,一个课程,也包含多个学生
需要三张表,才能表示此关系
学生表和课程表,必须借助第三个表 学生_课程表,才能表示多对多关系
一夫一妻
将引用的对方非 id,都设为 unique
多表查询分为下面这些:
连接查询,就是查出两张表的笛卡尔积
我们在继续往下面讲之前,先建几个表,并插入测试数据
CREATE TABLE dept1(
deptno int primary key,
dname varchar(14),
loc varchar(13)
);
insert into dept1 values(10,'服务部','北京');
insert into dept1 values(20,'研发部','北京');
insert into dept1 values(30,'销售部','北京');
insert into dept1 values(40,'主管部','北京');
CREATE TABLE emp1(
empno int,
ename varchar(50),
job varchar(50),
mgr int,
hiredate date,
sal double,
comm double,
depton int
);
insert into emp1 values(1001,'张三','文员',1006,'2019-1-1',1000,2010,10);
insert into emp1 values(1002,'李四','程序员',1006,'2019-2-1',1100,2000,20);
insert into emp1 values(1003,'王五','程序员',1006,'2019-3-1',1020,2011,20);
insert into emp1 values(1004,'赵六','销售',1006,'2019-4-1',1010,2002,30);
insert into emp1 values(1005,'张猛','销售',1006,'2019-5-1',1001,2003,30);
insert into emp1 values(1006,'谢娜','主管',1006,'2019-6-1',1011,2004,40);
如果没有表1.列名=表2.列名
,那查出的笛卡尔积,是没有意义的,我们要在笛卡尔积中,找出又用信息。因为这个表的设计,是按照 一 对多来的,所以,我们只要指定找出的笛卡尔积中,多的一方引用的一的一方的 id,等于一的一方的 id,就能找出有关联关系的数据
比如说,我们想查找每个员工,各处在什么部门:
select emp1.ename,dept1.dname from emp1,dept1 where emp1.depton=dept1.deptno;
我们的一对多/多对一 或者一对一的查询,都需要用到两张表的内联查询
我们的多对对,就需要用到三张表的内联查询
select 列名
from 表1
inner join 表2
on 表1.列名=表2.列名 //外键列的关系 where.....
等价于:
如果没有表1.列名=表2.列名
,那查出的笛卡尔积,是没有意义的,我们要在笛卡尔积中,找出又用信息。因为这个表的设计,是按照 一 对多来的,所以,我们只要指定找出的笛卡尔积中,多的一方引用的一的一方的 id,等于一的一方的 id,就能找出有关联关系的数据
select 列名
from 表1,表2
where 表1.列名=表2.列名 and ...(其他条件)
拿我们插入的数据举例,比如说,我们想查找每个员工,各处在什么部门:
连接查询:
select emp1.ename,dept1.dname from emp1,dept1 where emp1.depton=dept1.deptno;
内联查询:
与上面的写法等价
select emp1.ename,dept1.dname
from emp1
inner join dept1
on emp1.depton=dept1.deptno;
三表联查:
对于多对多关系,需要用到三标联查,我们的操作还是差不多
假设我们有 stu teacher stu_teacher 表 (学生有多个老师,老师也有多个学生,所以是多对多,所以要三张表)
我们的业务需求,是查询每位学生的所有老师
使用一般连接查询:
select stu.name,teacher.name
from stu,teacher,stu_teacher
where stu.id=stu_teacher.stu_id and teacher.id=stu_teacher.teacher_id
使用内联查询:
select stu.name,teacher.name
from stu
inner join stu_teacher on stu.id = stu_teacher.stu_id
inner join teacher on stu_teacher.teacher_id = teacher.id
– 左外联:select 列名 from 主表 left join 次表 on 主表.列名=次表.列名
– 1.主表数据全部显示,次表数据匹配显示,能匹配到的显示数据,匹配不成功的显示null
– 2.主表和次表不能随意调换位置
除了将 inner join 换成 left join,其他写法和内联查询一样,但是其可以显示没有连接关系的数据,并把空数据显示出来
就是把左外联换个方向
自然连接(NATURAL INNER JOIN):自然连接是一种特殊的等值连接,他要求两个关系表中进行连 接的必须是相同的属性列(名字相同),无须添加连接条件,并且在结果中消除重复的属性列。
就是查询套查询
子查询出现的位置:
select id from t where num is null
最好不要给数据库留NULL,尽可能的使用 NOT NULL填充数据库.
备注、描述、评论之类的可以设置为 NULL,其他的,最好不要使用NULL。
select id from t where num=10 or Name = 'admin'
可以这样查询:
select id from t where num = 10
union all
select id from t where Name = 'admin'
select id from t where num in(1,2,3)
select id from t where num between 1 and 3
原子性(Atomicity) :
要么全部成功,要么全部失败
事务的原子性是指事务必须是一个原子的操作序列单元。事务中包含的各项操作在一次执行过程中,只 允许出现两种状态之一。
**(1)**全部执行成功
**(2)**全部执行失败 事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错, 会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就 像化学中学过的原子,是物质构成的基本单位。
一致性(Consistency):
宇宙的总量是不变的
事务的一致性是指事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处以一致性状态。
比如:如果从A账户转账到B账户,不可能因为A账户扣了钱,而B账户没有加钱。
隔离性(Isolation):
并发的事务,不能相互干扰
事务的隔离性是指在并发环境中,并发的事务是互相隔离的。也就是说,不同的事务并发操作相同的数 据时,每个事务都有各自完整的数据空间。 一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务是不能互相干扰的。 隔离性分4个级别,下面会介绍。
持久性(Duration):
事务一旦提交,就会永久保存这个数据
事务的持久性是指事务一旦提交后,数据库中的数据必须被永久的保存下来。即使服务器系统崩溃或服 务器宕机等故障。只要数据库重新启动,那么一定能够将其恢复到事务成功结束后的状态
注意,只有 InnoDB 引擎支持,其他类型不支持事务
start transaction
或 begin
commit
rollback
==注意:==回滚操作,必须在事务提交之前,才能生效;如果 commit 了,就不能回滚了!
下面,我们就来演示一下事务的操作
这里,我们操作 grade 表:
我们用 BEGIN 开启事务操作,然后执行插入:
BEGIN;
insert into grade values(2,'哈哈');
可以看到,这个时候,数据库中的内容是没有更新的:
我们再执行 commit 操作:
# BEGIN;
# insert into grade values(2,'哈哈');
commit; # 执行 commit
这时候,我们发现,数据库持久成更新了:
我们在事务提交后,想再执行回滚,已经没用了:
# BEGIN;
# insert into grade values(2,'哈哈');
# commit;
rollback; # 提交后,再回滚,已经无济于事了
但是,如果在提交前回滚,就 ok:
读取到了没有提交的数据, 事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的 数据是脏数
同一条命令返回不同的结果集(更新).事务 A 多次读取同一数据,事务 B 在事务A 多次读取的 过程中,对数据做了更新并提交,导致事务A多次读取同一数据时,结果不一致
重复查询的过程中,数据 就发生了量的变化(insert, delete)
幻读和不可重复读很像,但是,幻读是发生了量的变化(insert,delete),不可重复读是发生了更新操作
事务隔离级别,可以用来解决事务的并发问题
4种事务隔离级别从上往下,级别越高,并发性越差,安全性就越来越高。
一般数据默认级别是 读以提交或可重复读
DQL:查询语句
DML:写操作(添加,删除,修改)
DDL:定义语句(建库,建表,修改表,索引操作,存储过程,视图)
DCL: 控制语言(给用户授权,或删除授权)
DDL(Data Define Language):都是隐式提交。 隐式提交:执行行行这 种语句相当于执行commit;
1nf:同一张表,不能有相同的列
2nf:每张表的行唯一(加主键)
3nf:引用其他表,只能引用其他表的主键(不能使用其他表的其他属性)
DDL,DML,DCL,DQL (DMCQ 倒霉催 Q)
- 实体完整性(行完整性)
- 主键约束:primary key:每个表有一个主键
- 唯一约束:unique [key]:数据不能重复
- 自动增长:auto_increment :主键自增长
- 域完整性(列完整性)
- 非空约束:not null:数据不能为空
- 默认约束:default :为属性设置默认值
- 引用完整性(关联表完整性)
- 外键约束: foreign key:
一对多/多对一:多包一
多对多:三张表
一对一:unique
内联:
查询多张有关系的表
一对多/多对一关系想表,要两张表联查
多对多关系的表,要三张表联查
外联:
查询两张表,一方全部显示,另一方和这一方面有关联,显示,没关联,显示 null
左外联,左边是主表,右边是副表
右外联,左边是副表,右边是主表
自然连接:
自然连接(NATURAL INNER JOIN):自然连接是一种特殊的等值连接,他要求两个关系表中进行连 接的必须是相同的属性列(名字相同),无须添加连接条件,并且在结果中消除重复的属性列。