MYSQL学习记录

1、启动Mysql 以管理员身份运行CMD 输入:

net start mysql
select version();  //查询当前Mysql的版本号
select database(); //查询当前数据库

2、数据库最基本的单位是表 table

行(row)-记录、 列(col)-字段(每一个字段都有:字段名、数据类型、约束等属性)
一定要记得结尾有分号 ;  SQL不区分大小写
Mysql是不见;不执行;

3、Mysql 的常用命令

\q                     #退出mysql
\C                     //终止一个命名的输入
 cd D:\mysql-8.0.25-winx64\mysql-8.0.25-winx64\bin
 mysql -u root -p      #进入本地数据库 数据库密码1234
 show databases;       #查看有哪些数据库 以;结尾,默认自带数据库
 create database demo; #创建数据库
 use demo;             #使用数据库
 show tables;          #查看数据库中有哪些表

4、关于SQL语句的分类(记忆)

DQL:数据查询语言 凡是带有select关键字的都是查询语言

DML:数据操作语言,对数据表进行增查改,对表的数据
insert delete update
DDL:数据定义语言,操作的是表的结构、不是表的数据(如增加删除表的字段)
create、drop、alter (修改)
TCL:事务控制语言、事务提交
事务提交:commit;  事务回滚:rollback;
DCL:数据控制语言
授权:grant;   撤销权限:revoke;

5、导入数据库表 路径中不要有中文,没有;,

导入sql文件之前,先选择自己要用的库否则会报1046
source  G:\mysql-study\test_sql\bjpowernode.sql
show tables;
select * from 表名; //查看某一个表中的所有数据,同一执行这个SQL语言
desc 表名;          //查看表的结构

6、DQL(简单查询)

select 字段名 from 表名;
注意:其中select 和from 是关键字!字段名和表名是标识符!
在实际开发中,读取所有字段建议把所有的表头名写出来,如果直接用*的话可读性比较差;
select dname from dept;     //查询部门的名字
select ename,job from emp;  //查询多个字段,使用 , 隔开
select * from emp;          //查询所有字段,使用 , 隔开,但是效率比较低,可读性差

Note:
给查询的列起别名,只是将查询结果的名字变了,但是原表的名字没有变。
select 不能对表进行修改操作,他只是负责查询;
在数据库中,字符串统一使用‘ ’,使得代码比较通用!

select deptno,dname as deptname from dept;  //使用as起别的名字
select deptno,dname  deptname from dept; //使用as变为空格也可起别的名字

#如何解决别名中有空格

select deptno,dname 'dept name' from dept;
数据库中的计算相关操作
字段可以参与数学表达式
select ename,sal*12 as sal_year from emp;

7、DQL(条件查询)=>where

1.= ,等号也可以用来查字符串' '    2.!=/<> 3.< 4.> 5.<= 6.>= 
7.between  and(必须遵循左小右大)/  >= and<=
8. is null /is not null ;
9.and; 10.or ; and 的优先级比or高 如果想优先执行or,加( );看下方代码7
11.in 包含,相当于多个or ;not in 表示不在这几个值中;in 并非区间,后面跟的是具体的值;
12.like 为模糊查询,支持%/_匹配  ;
%匹配任意字符,代表任意多个字符,是个特殊的符合 ;
_一个下划线只匹配一个字符,代表任意一个字符,是个特殊的符合 ;
Note:
在数据库中null 不能使用=,因为在数据中null真的是空的
select ename,job from emp where sal=800;
select ename,job from emp where sal between 800 and 3000; #闭区间
select ename from  emp where comm is null; #查找comm列为空的员工名称,=null 不可行
select ename from emp where job='manager' and sal>2500;
select ename from emp where job='manager'or 'SALESMAN' and sal>2500;
	#查找工作大于2500,且部门编号为10/20 的员工名称
 MANAGER sal>2500 and (DEPTNO=10 or DEPTNO=20);  #and or之间的优先级
#查询工作岗位是 SALESMAN、 MANAGER的员工 用in
select ename from emp where job in('SALESMAN','MANAGER');
select  EMPNO,ename,sal from emp where sal not in ('800','3000','5000');
like 模糊匹配
select EMPNO,ename,sal from emp where ename like '%o%';#姓名中有o的。
select EMPNO,ename,sal from emp where ename like '%t';#姓名以t结尾的。
select EMPNO,ename,sal from emp where ename like '_a%';#第二个字母为a。
#找出名字中有下划线的;使用\_代表名字中的下划线;\转义
select EMPNO,ename,sal from emp where ename like '%\_%';

8、排序数据

默认排序: order by ;指定降序:order by ... desc; 指定升序order by ... asc(asc其实可以不写)
select EMPNO,ename,sal from emp order by sal;#默认是升序
select EMPNO,ename,sal from emp order by sal desc;    #指定降序
select EMPNO,ename,sal from emp order by sal asc;     #指定升序
查询员工名字和薪资,要求按照薪资升序排,薪资一样时,再按照名字升序排;
select ename,sal from emp order by sal,ename;  #起主导的是sal,只有sal相等,才用ename

了解 :根据字段的位置也可以排序,但是不建议在开发中这样写。

select ename,sal from emp order by 2#2 代表第二列。按照查询结果的第二列排序
综合练习:找出薪资在1250-3000之间的员工信息,按照薪资降序排列。
关键字顺序不可以改变。select ... from...where...order by...desc;
第一步:from ;第二步 where ;第三步 select ;第四步 order by(排序总是在最后的);
select ename,sal from emp  where sal between 1250 and 3000 order by sal desc;

9、数据处理函数(单行处理函数)

特点:一行输入对应一个输出
常见数据处理函数:
lower-转小写; upper-转大写; 
 substr-取子串(substr(被截取的字符串,起始下标,截取长度)); 起始下标从1开始,没有0
 length -取长度; length(字段名)
  trim-去空格;
str_to_data 字符串转日期; 
data_format 格式化日期;
TIMESTAMPDIFF(unit,begin,end)
	unit参数是确定(end-begin)的结果的单位,表示为整数。 以下是有效单位:
	MICROSECOND  微秒
	SECOND  秒
	MINUTE  分钟
	HOUR  小时
	DAY  天
	WEEK  周
	MONTH  月份
	QUARTER 
	YEAR  年份

练习:

select avg(timestampdiff(YEAR, hiredate, now())) from emp
format设置千分位; round 四舍五入;
rand()生成随机数;
ifnull 将null转换成一个具体值 用法:ifnull(数据,被当做的值)
concat 拼接 concat(,)
select lower(ename)as ename_lower from emp;  #十四个输入对应十四个输出
select substr(ename,1,1)as ename_lower from emp; #起始下标从1开始的。
select concat('EMPNO','ename')as ename_lower from emp;#EMPNOename
select concat(EMPNO,ename)as ename_lower from emp;#拼接指定两列不需要加''
练习 :找出员工名字第一个字母为A的
select ename from emp where ename like 'A%'; #模糊查询
select ename from emp where substr(ename,1,1)='a';

总结:select后面可以跟某个表的字段名,也可跟字面量(字面值)。

select 字段名 from 表名
select 数据    from  表名
select 'abc' as test from emp; #把 abc重复 len(emp)的次数,赋值到test,类似python
select round(23.45,1) as result from emp; #四舍五入
select round(123.45,-1) as result from emp;#对十位进行四舍五入 结果120
select rand() as result from emp;  #生成随机数
select round(rand()*100,1) as result from emp;# 一百以内的随机数
select ename,(ifnull(COMM,0)+sal)*12 as year_sal from emp;
练习:当员工是manager 是工资上调10% 当员工是salesman 时,工资上调50%
case... when... then... when ...then...  else ...end
select 
ename,
job,
(case job when ' MANAGER' then sal*1.1 when 'SALESMAN' then sal*1.5 else sal end) as newsal
from emp;

10、分组处理函数(多行处理函数)

特点:多行输入对应一个输出
5个:count sum avg max min=分组函数;分组函数必须先进行分组,然后才可以使用
如果未分组 默认一个表为一组
Note:
1 分组函数自动忽略null;
2 分组函数中 count(*) 和count(字段) 有什么区别;
3 分组函数不能直接使用在where 字句中 ???
答:因为分组函数在使用的时候要先分组之后才能使用,where 执行的时候还没有分组,所有where后面不能出现分组函数
4 所有的分组函数是可以组合一起用

select ename,sal from emp where sal> min(sal);   #语法错误
select ename,max(sal) from emp;     #语法错误
count(字段) 统计该字段下不为null的元素的总数
 count(*)  统计表中的总行数
select count(comm) from emp; #count 为4
select count(*) from emp;    #count 为14
select max( HIREDATE) from emp;

11、分组查询(非常重要)

select

from

group by

以下关键字的顺序不可以被颠倒,需要注意

select  
...
from 
...
where
...
group by
....
having
....
order by
...

代码 执行顺序 1 from 2 where 3 group by 4 having 5 select 6order by

练习:找出每个工作岗位的最高工资,然后对工资求和
select job,sum(sal) from emp group by job;

重要结论:
在一条select语句当中,如果有group by 语句的话,select 后面只能跟:参加分组的字段以及分组函数,其他一律不跟。
练习:找出不同部分 不同工作岗位的最高薪资
技巧 两个字段联合成一个字段

select deptno,job,sum(sal) from emp group by deptno,job order by deptno;

使用having 可以对分组后的数据进行过滤
但是having 必须和group by 一起使用
练习:找出每个部门最高薪资,要求显示最高薪资大于3000;

select deptno,max(sal) from emp group by deptno having max(sal)>3000;
select deptno,max(sal) from emp where sal >3000 group by deptno;

优化策略 :where 和having 优先选择where 若 where实在完成不了 再选择having
找出每个部门的平均薪资然后找出平均薪资高于2500

select deptno,avg(sal) from emp group by deptno having avg(sal)>2500;

找出每个岗位的平均薪资,要求显示平均薪资大于1500,除manager

select
 job,avg(sal) as avgsal
 from 
 emp 
 where 
 job !='MANAGER' 
 group by 
 job 
 having avg(sal)>1500 
 order by avg(sal) 
 desc;

group_concat()-函数的使用
将分组结果进行连接

select dept_no, group_concat(emp_no) as employees
from dept_emp 
group by dept_no

#group_concat 默认的连接是,也可以自己输出参数
‘’’
dept_no employees
d001 10001,10002
d002 10006
d003 10003,10004
‘’’


day 2 1去除重复数据distinct

把查询数据结果去除重复记录,原表数据不会发生改变
使用distinct 字段,distinct只能出现在所有字段的最前方见第4行,且可以和函数嵌套使用见第6行

select distinct(job) from emp;
select ename,distinct job from emp;  //会报错的
//distinct出现在所有字段前方,所有字段联合去重
select distinct job,deptno from emp; 
#统计工作岗位的数量
select count(distinct job,deptno) from emp; 

2 连接查询(超级重点!!!)

从一张表中查询是单表 查询,跨表查询,多张表联合起来,叫做连接查询
根据表的连接方式可以分为:
内连接-完全能够匹配上这个条件的数据查询出来
假设A B 两张表进行连接,使用内连接的话,凡是A表和B表能够匹配上的记录查询出来,这就是内连接。A B 两张表没有主副之分,两张表是平等的
等值连接
非等值连接
自连接
外连接
假设 A B 两张表进行连接,使用外连接的话 ,AB两张表中一个表示主表 一个表是副表,如果附表中没有数据与主表匹配,副表自动模拟出null与之匹配
左外连接-左边的表为主表
右外连接-右边的表为主表
左右连接的写法都有
全连接(少用)
当两张表连接,没有任何限制条件的时候,最终查询结果条数是两张表的乘积,这种现象被称为笛卡尔积
select ename,dname from emp,dept;  //emp-14,dept4 最终结果14*4=56行数据

避免笛卡尔积现象:连接加条件;但是匹配过程中的匹配次数没有减少。表的连接次数越多,表的效率越低。
练习:查询员工所在部门名称

select
   //ename,dname  防止ename,dname都会去emp dept两个表中查找,采取下行

方法
e.ename,d.dname
// from emp,dept
from emp as e ,dept as d //起别名
where
e.deptno=d.deptno ;
select e.ename,d.dname from emp as e ,dept as d where e.deptno=d.deptno;

内连接-等值连接
案列 查询员工所在部门名称,显示员工名和部门名?

select e.ename,d.dname from emp as e ,dept as d where e.deptno=d.deptno;//92语法 
select e.ename,d.dname from emp as e join dept as d on e.deptno=d.deptno;//99语法

Sql 92 语法的缺点:结构不清晰,表的连接条件和后期进一步筛选的条件都放到了where的后面
Sql 99 表连接的条件是独立的,连接后需要进一步筛选,直接添加where

select
...
from 
    a
join (join 默认是 inner join)
    b 
on 
    a和b的连接条件
    axxx=bxxx 条件是等量关系,等值连接
where
    ...

内连接-非等值连接
练习 找出每个员工的薪资等级,要求显示员工名、薪资、薪资等级?

select
 e.ename,e.sal,s.grade 
 from 
 emp as e 
 join 
 salgrade as s 
 on
 e.sal between s.losal and s.hisal; //条件非等量关系,称为 非等值连接

内连接-自连接
练习:查询员工的上级领导,要求显示员工名和对于的领导名

select empno ,ename,mgr from emp;

-------±-------±-----+
empno | ename | mgr |
-------±-------±-----+
7369 | SMITH | 7902 |
7499 | ALLEN | 7698 |
7521 | WARD | 7698 |
7566 | JONES | 7839 |

-------±-------±-----+
技巧 一个表看成两个表;

select
 e.empno ,e.ename,b.ename
 from 
 emp as e 
 join 
 emp as b 
 on
 e.mgr =b.empno;

外连接 -使用较多!!!(公司的笔试题一般最后一个也是外连接)
练习:找出每个员工的上级领导(l两种写法)

select  a.ename as '员工',b.ename as '领导'
 from emp as a left join emp as b on a.mgr=b.EMPNO;
select  b.ename as '员工',a.ename as '领导'
 from emp as a right join emp as b on a.EMPNO=b.mgr;

练习:找出哪个部门没有员工?

select d.DEPTNO '部门',e.ename '员工'
 from dept as d left join emp as e
 on d.DEPTNO=e.DEPTNO
 where e.DEPTNO is null;

3 三张表连接

select 
from 
join  on
join  on;

1 练习 找出每个员工的部门名称以及工资等级。

select e.ename,d.dname,s.GRADE
 from emp as e left join dept as d  
 on e.DEPTNO=d.DEPTNO
 left join salgrade as s
 on e.sal between s.LOSAL and s.HISAL;

1 练习 找出每个员工的部门名称、工资等级以及上级领导。

select e.ename,e1.ename ‘上级领导’,d.dname,s.GRADE from emp as e left
join emp as e1 on e.mgr =e1.EMPNO join dept as d on
e.DEPTNO=d.DEPTNO join salgrade as s on e.sal between s.LOSAL and
s.HISAL;

4 子查询

什么是子查询 ? 在何处使用?
答:子查询中嵌套select 语句,被嵌套的select语句为子查询

select
    (select) from
    (select) where
    (select)

情况1 在where语句中使用子查询。
练习1 找出高于平均薪资的员工信息?

select *
 from emp
 where sal>(select avg(sal) from emp);

情况2 在from语句中使用子查询。
练习2 找出每个部门平均薪水的薪资等级。
第一步
找出每个部门的平均薪资

select DEPTNO,avg(sal) sal from emp group by DEPTNO;

第二步 将上述查询结果当做临时表t 让t表和salagrade s 连接 条件是 t.sal between s.LOSAL and s.HISAL;

select t.DEPTNO,t.sal,s.grade 
 from (select DEPTNO,avg(sal) sal from emp group by DEPTNO) t 
 join  salgrade s on t.sal between s.LOSAL and s.HISAL;

练习3 找出每个部门平均的薪水等级

select e.DEPTNO, avg(s.grade)
 from   emp e  join salgrade s on e.sal between s.LOSAL and s.HISAL
 group by e.DEPTNO order by e.DEPTNO ;

情况3 select 后面跟的嵌套语句
练习:找出每个员工所在部门名称 要求显示员工名和部门名

select e.ename,d.dname
 from emp e join dept d on e.DEPTNO=d. DEPTNO;
 select e.ename,(select d.dname from dept d where e.DEPTNO=d. DEPTNO ) dname
  from emp e ;

5 union (可以将查询结果相加)

找出工作岗位是 SALESMAN MANAGER 的员工?liang
方法1 in 方法2 or 方法3 union

select *
 from emp 
 where job in ('SALESMAN' ,'MANAGER');
 select * from emp where job ='SALESMAN'
  union select * from emp where job ='MANAGER';

两张不相干的表可以用union 拼接,前后两个结果的列必须一样;

6 limit (非常非常重点!!!! 分页查询全靠它)

limit 是mysql中特有的 其他数据库没有;(oracle 中 有一个相同的机制,叫做rownum)
limit 取结果集中的部分数据,这是他的作用;
语法机制 limit startindex,length startindex-表示起始位置 length-表示长度
limit 是sql语言执行的最后一个环节
sql的通用标准分页
例如: 每页显示3条;
第一页:0,3 第二页3,3 第三页:6,3
每页显示 ps条记录: 第PN页:(PN-1)*PS, NPS
练习 取出工资前五名的员工(取前五个数据,从0开始)

select * from emp order by sal desc limit 0,5;
select * from emp order by sal desc limit 5;

练习 取出工资4-9名的员工
select * from emp order by sal desc limit 3,6;

7 创建表

建表语句的格式
create table 表名(
字段名1 数据类型,
字段名2 数据类型,
字段名3 数据类型,

);

关于MYsql当中的数据类型? 以下只说常见的
int
bigint float
char-定长字符串 varchar-可变长
当在开发中 如果某个数据是定长的 使用char 例如 生日 性别 char的运行效率比varchar高
字符串 data
Blob-二进制大对象 (存储图片,视频等流媒体信息)
Clob-字符大对象(存储较大文本,比如可以存储4个G的字符串)
表名在数据库中一般建议以:t_或者tbl_开始
default 非必要设置
create table t_student(
number bigint default 1,
name varchar(255),
sex char(1) default 1,
classno varchar(255),
birth char(10) default ‘1997-01-01’
);
//在表中添加一列
alter table actor add
create_date datetime NOT NULL default ‘2020-10-01 00:00:00’;

8 insert 语句插入数据
insert只要执行成功,数据库中就一定会多一条数据
语法格式:
insert into 表名(字段1,字段2,字段3,…)values(值1,值2,值3,…);
注意 字段名和值的个数需要一致,且类型相同
insert into t_student (no,name,sex,classno,birth)
values(‘1’,‘cece’,‘0’,‘gaosan23ban’,‘1997-01-22’);
insert into t_student (name,sex,classno,birth,no)
values(‘zizi’,‘0’,‘gaosan23ban’,‘1997-01-22’,‘2’);
insert into t_student (name)
values(‘zizi’);

插入数据方法2
插入字段可以省略,但是插入的值数量和值得顺序不可以改变,缺少
()可以存在 也可以省略;
insert into t_student () values(‘1’,‘cece’,‘0’,‘gaosan23ban’,‘1997-01-22’);
insert into t_student values(‘2’,‘roll’,‘0’,‘gaosan23ban’,‘1997-12-22’);

插入多行数据
insert into t_student
values(‘3’,‘jack’,‘1’,‘gaosan24ban’,‘1996-12-22’),
(‘1’,‘jack’,‘1’,‘gaosan20ban’,‘1996-10-22’);
select * from t_student;

9 删除表格
drop table 表名;
drop table if exists t_student ;//mysql中支持

10 表的复制 以及批量插入
表的复制-查询结果当做表创建出来
create table emp1 as select empno,ename from emp;
create table emp2 as select * from emp;

批量插入数据-列数要一致;
create table dept1 as select * from dept;
insert into dept1 select * from dept;
select * from dept1;

11 修改数据-update
Note :update 之间是, 不是and
语法格式:
update 表名 set 字段名1 =值1,字段名2 =值2… where 条件;
注意:没有条件整张表全部更新
练习 在dept1中将部门10的loc修改为shanghai,将部门名称修改为:HR
update dept1 set loc=‘shanghai’,dname=‘hr’ where DEPTNO=10;
//更新所有数据
update dept1 set loc=‘x’,dname=‘y’;

12 删除数据-delete
语法格式 delete from 表名 where 条件; 注意 没有条件全部删除
练习 删除10部门的数据;
delete from dept1 where DEPTNO=10;
//删除表中的数据-这种方法对于较大的表删除仍需要一定时间
delete from dept1;

怎么快速删除大表的数据(重点)-风险级别较高
//将表截断,只留下表头,永久丢失数据
truncate table emp1;

13 对于表结构的修改 在工作中遇到的比较少,一般使用工具就可以处理了。
14 创建表加约束
什么是约束?常见的有哪些?
在创建表的时候,可以给表添加约束,保证数据有效性、合法性、完整性;(如:用户密码非空,用户名的唯一性)
常见约束:
非空约束(not null):约束字段不能既不能为Null-只有列级约束,只能加在字段后面
唯一约束(unique):不能重复,但是可以为null
主键约束(primary key):约束字段不能既不能为Null 也不能重复-列级约束
外键约束(foreign key): 外键值是可以为null的
检查约束(check):注意 mysql不支持该约束,在oracle中支持该约束;
列级约束:单个字段的约束
表级约束:多个字段联合约束时,需要表级
非空约束(not null)
drop table if exists t_user;
create table t_user(
id int,
usename varchar(255) ,
password varchar(255) not null
);
//Field ‘password’ doesn’t have a default value
insert into t_user( id, usename) values(‘1’,‘spring’);
insert into t_user( id, usename,password) values(‘1’,‘spring’,‘123698745’);
select * from t_user;

唯一约束(unique)
练习 给某一列添加唯一性字段
drop table if exists t_user;
create table t_user(
id int,
usename varchar(255) unique,
password varchar(255) not null
);
insert into t_user( id, usename,password) values(‘1’,‘spring’,‘123698745’);
//ERROR 1062 (23000): Duplicate entry ‘spring’ for key ‘t_user.usename’
insert into t_user( id, usename,password) values(‘2’,‘spring’,‘123’);
insert into t_user( id,password) values(‘3’,‘78798’);
select * from t_user;

练习 给多列添加唯一性字段
要求用户编号 和用户名联合起来具有唯一性?
drop table if exists t_user;
create table t_user(
id int,
usename varchar(255),
usecode varchar(255),
unique(usename,usecode)//表级约束
);
//上下两个代码直接的区别
drop table if exists t_user;
create table t_user(
id int,
usename varchar(255) unique,//列级约束
usecode varchar(255) unique
);

主键约束?-非空且不可重复
主键的作用 ??
表的设计三范式 第一范式就要求任何一张表都得有主键;
主键值:这行记录的唯一标识 相当于身份证号;一张表只能有一个主键
主键的分类:根据主键字段的字段数量来划分:
单一主键(常见的)
复合主键(多个字段联合起来添加一个主键约束,不建议使用,违背三范式)
根据主键的性质:
自然主键
业务主键(主键值和业务挂钩,如银行卡 卡号,但是不建议使用)-最好不要用与业务挂钩的值做主键

drop table if exists t_user;
create table t_user(
id int primary key,
usename varchar(255),
usecode varchar(255)
);
insert into t_user values(‘1’,‘zizi’,‘010’),(‘2’,‘cece’,‘011’);

主键的表级约束
drop table if exists t_user;
create table t_user(
id int ,
usename varchar(255),
usecode varchar(255),
primary key(id)//主键的表级约束
primary key(id,usecode)//复合主键,但是在工作做一般不用
);
insert into t_user values(‘1’,‘zizi’,‘010’),(‘2’,‘cece’,‘011’);

mysql提供主键值自增 auto_increment;-自然主键可以自己增加
drop table if exists t_user;
create table t_user(
id int primary key auto_increment,
usename varchar(255),
usecode varchar(255)
);
insert into t_user(usename,usecode) values(‘zizi’,‘010’),(‘cece’,‘011’);
select * from t_user;

外键约束
关于外键约束相关术语
外键约束:foreign key;
外键字段:添加有外键约束的字段
外键值:外键字段的值
业务背景:请设计数据库表 用来维护学生和班级信息?
foreign key 子表字段 references 父表(父表所对应的字段)

alter table 子表
add constraint foreign key (字段对应字段)
references 父表 (父表对应字段);// 注意括号不可以漏掉

NOTE:外键 引用另一张表的字段时 被引用的字段不一定是主键 但是必须具有唯一性
外键可以引用null
t_class :班级表
cno(pk) cname
101 class1
102 class2
t_student 学生表 t_class 父表(父表被引用的字段)
sno sname cno(该字段添加外键约束)
1 lisi 101
2 zs 101
3 cici 102
将以上建表语句写出来-Mysql语言实现:
t_student中的cno字段引用t_class表中的cno字段,此时t_student 表叫做子表,t_class表父表
删除表和数据时:先删子表 再删父表 、
添加数据或者表时:先添加父表再添加子表
foreign key 字段名
drop table t_student
drop table t_class

create table t_class(
cno int primary key ,
cname varchar(255)
);
create table t_student(
sno int primary key ,
sname varchar(255),
classno int,
create table t_student(
sno int ,
sname varchar(255),create table t_student(
sno int ,
sname varchar(255),
classno int,
foreign key (classno) references t_class(cno)

);
classno int,
foreign key (classno) references t_class(cno)

);

);

15 存储引擎-面试的时候会被问到
存储引擎描述的是表的存储方法,只有在mysql中存在
show create table emp; //查询表的创建语句以及存储方法
//mysql 默认的存储引擎是InnoDB 默认采用的字符集是utf8mb4_0900_ai_ci
ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

常见的存储引擎有那些? 每一个存储引擎对应一个存储方式每个存储引擎有自己的优缺点;
show engines \g //查看当前版本可以使用的存储引擎

MyISAM-存储引擎
这个存储不只是事物,是最常用的存储引擎但是不是默认的
Hash based, stored in memory, useful for temporary tables
Transactions-NO
XA-NO
Savepoints-NO
使用三个文件表示每个表:
格式文件-存储表结构的定义(mytable.frm)
数据文件-存储表行的内容(mytable.MYD)
索引文件-存储表上索引(mytable.MYI)
灵活的AUTO_INCREMENT字段处理
优点:可被转换为压缩,只读表来节省空间,可以转换为只读表,节省效率
InnoDB
Supports transactions, row-level locking, and foreign keys
Transactions-YES
XA-YES
Savepoints-YES
这种存储引擎最安全,支持事务,但是不能被压缩,不能节省空间
表的结构存储在xxx.frm文件中,数据存储在tablespace这样的表空间中(逻辑概念),无法被压缩,无法转换成只读,但是在开发中常常使用这样的搜索引擎,因为它安全。同时在mysql奔溃后可以自动恢复,支持级联删除,级联更新
MEMORY
Hash based, stored in memory, useful for temporary tables
Transactions-NO
XA-NO
Savepoints-NO
使用 MEMORY存储引擎的表,其数据存在内存中,长度固定,这两个特点使得 MEMORY存储引擎特别的快。
优点 查询速度最快
缺点:不支持事务,由于数据存储在内存中,数据容易丢失
以下特征:在数据库目录内,每个表均匀.frm格式的文件表示。 表的数据及索引被存储在内存中。
表级锁机制 不能包含CLOB BLOB字段

以上三种存储引擎之间的对比:
 MyISAM 适合用于大量数据读而少量数据更新的混合操作, MyISAM表的另一种适合情形是使用压缩的只读表。
如果查询中包含较多的更新操作,应使用InnoDB,其行级锁机制和多版本的支持为数据读取和更新的混合操作提供了良好的并发机制。
MEMORY存储非永久性数据,或者能从基表中生成的数据。

1 事务(Transaaction)-DML语句 (insert delete update)
TCL (commit-提交 rollback-回滚)savepoint
insert
delete
update
savepoint a1;//设置保存点
delete

rollback a1//回滚到a1处

一个事务是一个完整的业务逻辑单元,不可再分;
事务的存在是为了保证数据的完整性 安全性;
比如 银行账号转账,从A账号向B账号转账10000,需要执行两个update语句
update t_act set balance=balance-10000 where actno =‘act-001’;
update t_act set balance=balance+10000 where actno =‘act-002’;

以上 两条DML语句 必须两个同时成功 或者同时失败 不能一成功 一失败
要想保证两条DML语句 必须两个同时成功 或者同时失败,那么需要使用数据库的“事务机制”。
如果数据需要一个DML的语句,就不需要事务
事务的四大特性(ACID):
A:原子性 事务是最小的工作单元,不可再分;
C一致性 事务必须保证多条DML语句同时成功 或者同时失败
I 隔离性 事务之间具有隔离
隔离级别 (四个-理论上)
1 读未提交数据(read uncommitted)
我们当前事务可读取到未提交的数据,存在脏读现象,表示读到脏数据
2读已提交(read committed)
我们当前事务可读取到提交的数据,存在不可重读(不能读到之前提交的数 据)
解决了脏读现象。
3 可重复读 (repeatable read)-mysql默级别
这种隔离高级版解决了不可重复读问题。解决 读取数据是幻象的
4 序列化/串行化读 解决了所有问题 但是效率低下
D持久性 最终数据必须持久话到硬盘文件中,事务才算成功的结束。
演示事务
mysql 事务默认情况下是自动提交,执行任意一个DML语句则提交一次.
如何关闭自动提交?
start transaction;

演示
drop table if exists t_user;
create table t_user(
id int primary key auto_increment,
username varchar(255) );
insert into t_user(username) values(‘zeze’);
//回滚前后的数据一样 因为mysql只要执行DML语句,自动提交数据
select * from t_user;//1 | zeze
rollback;
select * from t_user;//1 | zeze
//使用 start transaction 关闭自动提交机制
start transaction ;
insert into t_user(username) values(‘jack’),(‘wangwu’);
select * from t_user;
1 | zeze
2 | cici
3 | jack
4 | wangwu
rollback;
1 | zeze
start transaction //开始提交数据

演示四个等级
使用两个事务演示隔离级别
1 读未提交(read uncommitted)
set global transaction isolation level read uncommitted; //设置事务隔离级别为第一级别
select @@global.tx_isolation; //查看事务的全局隔离级别

16 索引

什么是索引?作用?
索引是添加给某一字段或者某些字段的;
相当于是目录,可以通过目录找到对于资源
方法一 全表扫描 方法二 根据索引检索(效率高)
缩小扫描范围,但是不可随意添加索引,也需要不断维护,如果数据库数据经常修改,索引也会发生变化,重新排序。

select ename,sal from emp where ename ='smith'

当ename 字段上没有添加索引的时候,以上sql语句会进行全表扫描,扫描字段中的所有值
当ename 字段上没有索引的时候,以上sql语句会根据索引扫描,快速定位。
如何创建?如何删除?
create index 索引名 on 表名(字段名);
drop index 索引名 on 表名(字段名); //删除数据
//创建唯一索引

create unique index uniq_idx_firstname on actor(first_name);

什么时候给字段添加索引?
数据量相对比较庞大。
该字段很少dml操作。(字段进行修改,索引也会修改)
该字段经常出现在where 子句中。(经常根据那个字段查询)
主键和unique约束的字段会自动添加索引;==根据主键查询效率较高;

select ename ,sal from emp where sal=5000;

//查看SQL语句的执行计划;

explain select ename ,sal from emp where sal=5000; //typeall rows=1

//给薪资sal字段添加索引;

create index emp_sal_index on emp(sal); //type 为ref rows=1

//使用强制索引force index (索引名)

select  * from salaries  force index (idx_emp_no) where emp_no=10005

索引的实现原理
通过B Tree 缩小扫面范围,底层索引进行了排序,分区,索引会携带数据在表中的‘物理地址’,最终通过检索到数据之后 获取到关联的物理地址,通过物理地址定位表中的数据,效率是最高的。

索引的分类:
单一索引:单个字段添加索引。
复合索引:多个字段联合添加索引。
主键索引:主键字段添加索引。
唯一索引:有unique约束的字段会自动添加索引。

索引什么时候失效?
select ename from emp where ename like ‘%A%’
//模糊查询时,第一个通配符使用的是%,索引会失效

17 视图(view)

什么是视图?不同角度看数据
视图得作用:
视图可以隐藏表得实现细节。用于保密级别较高得的数据库。
创建视图-只有使用DQL语句才能以视图对象得方式创建出来。
create view myview as select empno,ename from emp;
select * from myview; //查看视图

删除视图
drop view myview;

对视图得增删改查 会影响到源表数据(是通过视图影响得源表数据,不是直接操作影响得源表)
create table emp_bak as select * from emp;
create view myview1 as select empno,ename,sal from emp_bak;
select * from myview1;
update myview1 set ename=‘hehe’ ,sal=1 where empno=‘7369’;
delete from myview1 where empno=‘7369’;

18 数据库数据的导入和导出
导出
\q //退出数据库

mysqldump demo>d:\bjpowernode1.sql-u root -p1234
mysqldump -u root -p1234 demo > test_db.sql
mysqldump -u root -p1234 demo > D:\mysql-study\bjpowernode1.txt
mysql -uroot -p1234 --default-character-set=utf8 demo< D:\mysql-study\bjpowernode1.sql;

导入
见文档开头;
19 数据库设计三范式(重点内容 面试常问)-减少数据冗余
什么是设计范式
第一范式:任何一个表都应该有主键 并且每一个字段原子性不可再分
第二范式:所有非主键字段完全依赖主键,不能产生部分依赖;
多对多?三张表,关系表两个外键;
第三范式:所有非主键字段依赖主键,不能传递依赖
一对多?两张表,多的表加外键
NOTE:在实际开发中,以客户的需求为主,有的时候拿冗余换执行速度。

一对一 如何设计?
两个方法:
1 主键共享
2 外键+唯一约束
用户登陆表 和用户详细信息表


DAY 3 实操练习中的知识点补充

1、CHARINDEX

CHARINDEX ( expression1 , expression2 [ , start_location ] )
    //expression1 必需 ---要查找的子字符串
	//expression2 必需 ---父字符串
	//start_location 可选 ---指定从父字符串开始查找的位置,默认位置从1开始

从expression2字符串中指定的位置处开始查找是否包含expression1字符串
如果能够从expression2字符串中查找到expression1字符串则返回expression1在
expression2出现的位置;反之,返回0

累计数据:

select emp_no, salary,sum(salary) over (order by emp_no) as running_total
from salaries
where to_date = '9999-01-01'
sum(被累加的列) over (order by 按照某一列排序)

需求:输出first_name排名(按first_name升序排序)为奇数的first_name,输出时不需排序

select e1.first_name
from employees e1
where (SELECT COUNT(*)
FROM employees e2
WHERE e1.first_name >= e2.first_name)%2=1
> select ename, row_number() over (order by ename) as row_idx 
> //按照某一列增加一列排序  from emp;

//输出为排序在奇数列的数据,这是按照名称的升序排的

 select e1.ename
 from (select ename,
row_number() over (order by ename) as row_idx  
 from emp ) e1
 where e1.row_idx %2=1

//输出为排序在奇数列的数据,这是按照原表顺序排的

select e2.ename
 from emp e2 ,(select e1.ename
 from (select ename,
row_number() over (order by ename) as row_idx  
 from emp ) e1
 where e1.row_idx %2=1) e
 where e2.ename =e.ename;

求出某个字符串中,出现的次数
10,A,B 把,替换掉,求差值

select length('10,A,B')-length(replace('10,A,B',',','')) as ',出现次数';//2

连接 今天(') 好

select concat('今天','\'','好')  as 'result' //注意转义字符 \'

修改表的名字

rename table test to test1;

查找排除最大、最小salary之后的当前(to_date = ‘9999-01-01’ )员工的平均工资avg_salary。

select (sum(salary)-max(salary)-min(salary))/(count(*)-2)
from salaries 
where to_date = '9999-01-01' 

分页查询employees表,每5行一页,返回第2页的数据

select *from employees limit 5,5

#limit m,n --m的含义表示从数据的第m条开始查询(mysql中第一条数据m=0)
# m=(currentPage-1)*linesize,n=linesize
# n的含义是从第m条数据开始往后查询n条数据

使用含有关键字exists查找未分配具体部门的员工的所有信息。

select e.*
from employees  e 
where not exists (select * from dept_emp  d where e.emp_no=d.emp_no)
select e.date,
#Round(数值,保留的小数位数)
round(sum(case e.type when 'no_completed' then 1 else 0 end)/count(*), 3) as number
from email e  join  user u on e.send_id=u.id and u.is_blacklist=0
join user u1 on e.receive_id =u1.id and u1.is_blacklist=0
group by e.date
order by e.date

牛客每个人最近的登录日期

select user_id,max(date) d
from login
group by user_id
order by user_id
select u.name u_name,c.name c_n,max(l.date) date from user u, client c,login l where l.user_id = u.id and 
l.client_id = c.id group by u.name 

mysql里查找某一天的后一天的用法是:DATE_ADD(yyyy-mm-dd,INTERVAL 1 DAY),四舍五入的函数为round)

select round(count(l.user_id)*1.0/count(r.user_id),3)
from (select user_id ,min(date) as date from login group by user_id ) r
left join login l 
on r.user_id=l.user_id and l.date=DATE_ADD(r.date,INTERVAL 1 DAY)

sql语句:查询每个日期登录新用户个数,并且查询结果按照日期升序排序(无新用户登陆的日期记录次数为0)
#先得到所有日期表select distinct date from login;
#然后左连接新用户首次登陆的日期表
#归类统计日期出现的次数.

select r.date ,count(a.user_id) new
from (select distinct date from login ) r
left join 
(select user_id,min(date) first_date from login group by user_id) a
on a.first_date=r.date
group by r.date
order by r.
select round(count(ifnull(l.date,0))*1.0/count(r.date),3)
from(select user_id ,min(date) date from login group by user_id) r
left join login l on l.user_id=r.user_id and l.date=date_ADD(r.date,INTERVAL 1 DAY)
group by r.date
select r1.date,ifnull(result.p,0)
from  (select distinct date from login) r1 
left join (select r.date r_date,round(count(l.date)*1.0/count(r.date),3) p
from(select user_id ,min(date) date from login group by user_id) r
left join login l on l.user_id=r.user_id and l.date=date_ADD(r.date,INTERVAL 1 DAY)
group by r.date) result 
on r1.date=result.r_date

牛客每天有很多人登录,请你统计一下牛客每个用户查询刷题信息,包括: 用户的名字,以及截止到某天,累计总共通过了多少题。 不存在没有登录却刷题的情况,但是存在登录了没刷题的情况,不会存在刷题表里面,有提交代码没有通过的情况,但是会记录在刷题表里,只不过通过数目是0

select u.name u_n,pn.date ,sum(pn.number)over(partition by u.name order by pn.date) ps_num
from passing_number pn  join user u on u.id=pn.user_id 
order by pn.date, u.name
sum(v2) over(partition by v1 order by v2)的用法
#对某一列数据进行累加
select v1,v2,sum(v2) over(order by v2) as sum from wmg_test;
#对某一列数据按照v1列进行分组累加,并按照V2进行排序
select v1,v2,sum(v2) over(partition by v1 order by v2) as sum from wmg_test;
#相同key的进行回填处理,没有order by
#假如V1中aa的值累计和是158,那么每个v3所对应的aa值就是158
select v1,v2,sum(v2) over(partition by v1) as v3

求考试分数大于平均分数的数据
#先进行筛选了每个工作所对应的分数均值

RANK()返回的是不持续的编号,
例如10, 11, 11, 12返回的编号将是1,2,2,4;
DENSE_RANK()返回的是持续的编号,
例如10, 11, 11, 12返回的编号是1,2,2,3;
ROW_NUMBER()返回的是持续不重复的编号
例如10, 11, 11, 12返回的编号将是1,2,3,4
count(*) over(partition by user_id) #计数
#找出每个language_id 位于前两个的人并且按照名字、id 升序,分数降序

```python
select g.id,l.name,g.score
from grade g,language l
where g.language_id=l.id and
(select count(distinct g1.score) from grade g1 where g.language_id=g1.language_id and g.score<g1.score) in (0,1)
order by l.name ASC,g.score DESC,g.id ASC

#sqlite 运行无误

```python
select a.id,l.name,a.score
from (select id,language_id,score,dense_rank()over(partition by language_id order by score desc) as rank
from grade group by id ) a
join language l on  a.language_id=l.id
where a.rank <=2
order by l.name ,a.score desc,a.id asc
select *
from order_info oi
where oi.user_id=(select user_id
from order_info 
where date>"2025-10-15" and product_name in ('Python','C++','Java') and status='completed'
group by user_id
having count(user_id)>=2
order by user_id)
order by id
select ifnull(c.name,'GroupBuy') source,count( c.name) cnt
from 
(select *,count(*) over (partition by user_id) cnt
from order_info 
where product_name in ('C++','Java','Python') and date>='2025-10-15' and status='completed') r

left join client c on r.client_id=c.id
where r.cnt>=2
group by source
order by source
SELECT
IFNULL(c.name, 'GroupBuy') AS source, COUNT(client_id) AS cnt
FROM (SELECT * , COUNT(*) OVER (PARTITION BY user_id) AS num
      FROM order_info WHERE date >= '2025-10-15'
      AND product_name IN ('C++', 'Java', 'Python')
      AND status LIKE 'com%') t
LEFT JOIN client c ON t.client_id = c.id
WHERE t.num >= 2
GROUP BY source
ORDER BY source;
select distinct r.job,r.date,r.num,r1.job ,r1.date,r1.num1
from(select id,job,substr(date,1,7) date,sum(num) num
from resume_info where date like '2025%' 
   group by date,job) r
left join
(select job,substr(date,1,7) date,sum(num) num1
from resume_info where date like '2026%'
  group by date,job) r1
on r.job=r1.job and substr(r.date,5,3)=substr(r1.date,5,3)
order by r.date desc,r.job desc
select r.grade
from(select grade ,sum(number) over (order by grade) cs,
     sum(number) over (order by grade desc) dcs,
      (select sum(number) from class_grade) number
from class_grade  ) r
where r.number*1.0/2<=r.cs and r.number*1.0/2<=r.dcs
order by r.grade
# (select sum(number) from class_grade) number-保证每行都有一个和输出
select c.name ,count(c.name)
from 
(select f.film_id,fc.category_id
from film f join film_category fc on f.film_id =fc.film_id 
where f.description like '%robot%'
and fc.category_id=(select category_id from film_category 
                    group by category_id having count(film_id)>=5)) r
join category c on c.category_id=r.category_id
group by c.name
select *,dense_rank() over (order by l1.日期)  biaohao 
from (select *,if (人数>100,1,0) as num from run_day) l1;
select *,count(*) over (partition by 人数 order by l1.日期 )  biaohao 
from (select *,if (人数>100,1,0) as num from run_day) l1;

select *,row_number() over (order by 日期) rownum  from run_day where 人数>100;

select *,row_number() over (order by 日期) rownum  from run_day where 人数>100 order by 日期;
 sql语句:查询每个日期登录新用户个数,并且查询结果按照日期升序排序(无新用户登陆的日期记录次数为0)

递归查询 2022/04/11 17:02

with t(n) as (select 1 as n )  #创建一个临时表
select * from t   #调用表
#如何实现递归呢
with t(n) as 
(select 1 as n #递归初始化
union all 
select n+1 from t where n<10 )#递归

地图路线查询

with recursive transfer(station_name,stop_station,stops, paths) as (
SELECT station_name,next_station,1 stops ,
	CAST(CONCAT(line_name,station_name,"->",line_name,next_station) as char (1000))as paths
from bj_subway
where station_name="王府井"
union ALL
SELECT t.station_name,s.next_station,stops+1,concat(paths,"->",s.line_name,s.next_station)
from transfer t 
join bj_subway s 
on (t.stop_station=s.station_name AND instr(paths,s.next_station)=0)
)
SELECT *
from transfer
where stop_station="积水潭" 
LIMIT 2

力扣题

select employee_id, 
(case name and employee_id 
when name not like 'M%' and employee_id%2=1 then 0 else salary end) bonus from Employees
order by employee_id

这里的not like 不是模糊匹配M开头的名字 而是对like ‘M%’ and employee_id%2=1 取反
使用update 性别取反

update Salary set sex=(case sex when 'm' then 'f' else 'm' end)  
delete from Person
where id not in 
(select id from (select min(id) id from Person group by email ) t )
#妙
delete u
from Person u , Person v
where v.id < u.id and u.email = v.email 

需要给表命名 不能下面这个写法的原因是 原表也存在id字段,这个where的id mysql无法分辨是子查询的还是person表的,需要借助别名进行准确的定义

delete from Person 
where id not in 
(
    select min(id) as id
    from Person
    group by email
)

你可能感兴趣的:(mysql)