# 数据库

# io流文件存储数据的弊端

  1. 存储效率低
  2. 不管存还是取操作比较麻烦
  3. 一般只能保存小量字符串数据

# DB

  • date base 数据库
  • 什么是数据库: 数据库实际上就是一个文件集合,本质就是一个文件系统,数据按照特定的格式存储到文件中,使用sql语言对数据进行增删改查操作.

# 什么是DBMS

DataBaseManagementSystem:数据库管理系统,管理数据库文件的软件

  • 指一种操作和管理数据库的大型软件,用于建立,使用和维护数据库,对数据进行统一的管理和控制,用户通过DBMS访问数据库中的数据 常见:mysql oracle db2 sqlserver sqlite ...

# 数据库的分类

  1. 关系型数据库:经过数学理论验证,可以将现实生活中存在的各种关系,保存到数据库中,这种数据库称为关系型数据库,在此数据库中,以表的形式保存数据之间的关系
  2. 非关系型数据库: 主要为了解决特定的应用场景,如:缓存,高并发访问等,存储数据的方式有多种,redis是常见的非关系型数据库,redis是以键值对的形式保存数据.

# 常见数据库介绍

  1. mysql: oracle公司,08年被sun收购 09 sun被oracle收购 ,开源,市场占有最高 被oracle收购后出了5.5版本由于使用了oracle核心技术,性能有了大幅提高,导致oracle用户流失,公司决定将mysql闭源,导致原mysql程序员离开并创建了mariaDB,maria创始人的女儿名字
  2. oracle: oracle公司,性能最强大的数据库,而且收费最贵,通常不缺钱却技术的公司会选用,市场排名第二
  3. DB2: IBM公司,闭源收费,通常一些银行项目会使用
  4. sqlserver: 微软公司,排名第三,闭源收费,提供整套解决方案(web服务器,操作系统,数据库服务器等)
  5. sqlite:轻量级数据库,应用在嵌入式或移动设备中,大小只有几十k,功能和性能较大型数据库要少很多 ##开源和闭源
  • 开源: 公开源代码,免费, 大牛程序员会维护和升级 弊端:通过卖服务赚钱,来钱比较慢
  • 闭源: 不公开源代码,收费(卖产品) 弊端:大牛程序员会攻击找漏洞

# mysql安装

  • 参见mysql安装文档,注意两点:1. 端口号选择默认的3306
  1. 字符集选择utf-8

# 什么是数据库服务器

服务器:一台高配置电脑 ftp服务器:安装了ftp服务软件提供了ftp服务功能的高配置电脑 邮件服务器:安装了邮件服务的软件 供了邮件服务功能的高配置电脑 数据库服务器:安装了数据库服务的软件 供了数据服务功能的高配置电脑 web服务器:安装了web服务软件的高配置电脑

# 连接数据库

  • 打开终端或命令行 在终端中输入以下命令: mysql -uroot -p然后敲回车,然后再敲回车
  • 退出指令: exit;

# 什么是sql

Stuctured Query Language: 结构化查询语言,使用sql语言和数据库服务器进行交互,通过sql告诉数据库服务器对数据进行什么操作.

# sql规范

  1. 以;(分号)结尾
  2. 关键字之间有空格,通常只有一个,但多个也可以
  3. 可以存在换行
  4. 数据库名称和表名称区分大小写

# 数据库相关的sql

  1. 查看所有数据show databases;
  2. 创建数据库
  • 格式:create database 数据库名称;
  1. 查看数据库详情
  • 格式 show create database 数据库名;
  1. 创建数据库指定字符集
  • utf8或gbk
    create database db2 character set utf8;
  1. 删除数据库
    drop database db2;
  1. 使用数据库
    use db1;

# 和表相关的SQL

  • 什么是表:关系型数据库中保存数据的单元,类似于excel中的表格,创建表时需要指定字段信息
  1. 创建表
  • 格式:create table 表名(字段1名 字段1类型,字段2名 字段2类型);
    create table person(name varchar(10),age int);
  • 练习1:创建一个员工表(emp) 有名字(ename)和工资(sal)
    create table emp(ename varchar(10),sal int);
  • 练习2:创建一个学生表(student)有学号(id)姓名(name)性别(gender)语文(chinese)数学(math)英语(english)
    create table student(id int,name varchar(10),gender varchar(5),chinese int,math int,english int);
  1. 查询所有表
    show tables;
  1. 查看单个表属性
    show create table 表名;

# 表的引擎

  1. Myisam:只支持数据基础的增删改查,不支持高级操作,如:事务,外键盘等

  2. InnoDB:支持高级操作,默认为InnoDB

  3. 创建表并且指定引擎和字符集

    create table t1(id int, name varchar(10)) engine=myisam charset=gbk;
  1. 查看表字段信息
    desc 表名;

# 练习: 创建5个数据库 每个里面创建一张表,第三个数据库字符集gbk,里面的表引擎为myisam,创建完后全部删除

  1. 对创建好的表进行修改
  • 修改表名 rename table 原名 to 新名;
    rename table student to t_stu;
    
    create table hero(name varchar(10));(创建一个新表,因为有的同学删除了以前的表)
  • 修改表属性 alter table hero engine=myisam charset=gbk;

  • 添加表字段

    • 最后位置添加 alter table hero add age int;
    • 最前面添加 alter table hero add money int first;
    • 在某个字段的后面添加 alter table hero add gender varchar(5) after name;
  • 删除表字段 alter table hero drop money;

  • 修改表字段的名和类型 alter table hero change 原字段名 新字段名 类型;

    alter table hero change name heroname varchar(10);
  • 修改表字段的类型和位置
    alter table hero modify age int first;
    alter table hero modify ageint int after XXX;
  1. 删除表 drop table hero;

# 数据相关的sql

# 插入数据

  • 创建表
    create table student(id int, name varchar(10),chinese int, math int, english int);
  • 插入数据,全表插入: 每个字段都赋值,顺序要和表字段一致 insert into 表名 values();
    insert into student values(1,'zhangsan',88,38,98);
  • 指定字段插入
    insert into student (id,name) values(2,'张飞');

# 批量插入

  • 全表批量插入
    insert into student values(6,'唐僧',56,57,58),(7,'八戒',88,89,99);
  • 指定字段插入
    insert into student (id,name) values(8,'吕布'),(9,'貂蝉'),(10,'孙尚香');

# 查询

  • 查询全部数据的全部字段信息 select * from student;
  • 查询指定字段 select id,name from student;
  • 条件查询 select * from student where id<5;

# 修改数据

    update student set math=100;
    update student set english=30 where id=7;

# 删除数据

  • 按条件删除 delete from student where id=7;
  • 全部删除 delete from student;

# 主键约束

  • 给主键添加约束,起到非空并且唯一的作用,主键指表示数据唯一性的字段,一张表中只有一个主键
  • 如何使用:
    create table t1 (id int primary key,name varchar(10));
  • 测试: 一下代码第二行报错因为id重复了
    insert into t1 values(1,'李白');
    insert into t1 values(1,'杜甫');
  • 测试: 第一行mariaDB报错,在linux上mysql会插入一个0,第二行会报错,id值不能为null;
    insert into t1 (name) values('黄忠');
    <!--ERROR 1364 (HY000): Field 'id' doesn't have a default value-->
    insert into t1 values(null,'张三');
    <!--ERROR 1048 (23000): Column 'id' cannot be null-->
  • 主键约束+自增
    create table t2(id int primary key auto_increment, name varchar(10));
  • 测试:
    insert into t2 values (null,'貂蝉');
    insert into t2 values (null,'小乔');
    insert into t2 (name) values('吕布');
    insert into t2 values (5,'魏延');
  • 自增总结
  1. 表内自增的数值 只增不减
  2. 清空表的数据 数值不归零 继续增长
  3. 数值以出现过的最大值的基础+1

# 注释 comment

  • 注释 可以在创建表添加字段的时候对字段进行介绍,便于以后查看表的时候知道每个字段的作用
  • 如何使用:
    create table t3(id int primary key auto_increment comment '这是个主键', name varchar(10), comm int comment '这是奖金');
    
    show create table t3;

# ` '区别

  • `的作用是用来修饰表名和字段名,可以省略
  • '的作用是用来修饰字符串的

# 数据冗余

  • 什么是冗余:如果设计表不够合理,随着数据量的增多,出现大量的重复数据,称为数据的冗余。

# 练习

  1. 创建商品表 price单价,num库存,category_id分类id
    create table item(id int primary key auto_increment, name varchar(10),price int, num int, category_id int);
  1. 创建分类表
    create table category(id int primary key auto_increment, name varchar(10));
  1. 插入苹果手机 价格5888 库存200, dell电脑价格4000, 库存150,联想电脑价格3500, 库存100。
    insert into item (name,price, num,category_id) values('苹果手机',5888,200,1);
  1. 插入手机和电脑分类
    insert into category values(null,'手机'),(null,'电脑');
  1. 给分类表添加上级id的字段
    alter table category add parent_id int;
  1. 插入一条办公耗材分类下的打印机分类下的惠普打印机价格2300 库存20
    insert into category values(null,'办公耗材',null);
    insert into category values(null,'打印机',3);
    insert into item values(null,'惠普打印机',2300,20,4);

# 事务

  • 事务是数据库中执行sql语句中的最小工作单元
  • 如何开启事务:关闭客户端自动提交,改成手动提交,把多次修改数据库的sql放在一次提交中,则多次操作数据库的sql就相当于是放到了同一个事务中
  • 数据库的默认提交方式是自动提交
  • 没有事务的话,语句是挨条执行,如果第一次提交中断(断电)则数据库中的数据会异常
    1. 超人+300 提交
    2. 蝙蝠侠-300 提交
  • 有事务的过程
    1. 关闭自动提交
    2. 超人+300
    3. 蝙蝠侠-300
    4. 手动提交

提上代码就算中间异常中断,结果要么同时成功,要么同时失败

  • 为什么使用事务?
    如果不使用事务,客户端会自动提交,在多次操作数据库做一件事的时候(如:转账)会出现部分成功,部分失败,则数据会出现异常。使用事务,把多次操作数据库的sql合并到一次提交中,这样就能保证同时成功或失败。
  • 使用事务的执行过程?
    1. 关闭自动提交
    2. 执行多次sql(在内存中执行)
    3. 手动提交

# 查看客户端自动提交状态

show variables like '%autocommit%';

# 关闭自动提交 仅对当前窗口生效

  • 关闭set autocommit=0;
  • 打开set autocommit=1;

# 验证转账流程

  1. 创建表 create table person(id int primary key auto_increment, name varchar(10),money int);
  2. 插入数据 insert into person values(null,'超人',200),(null,'蝙蝠侠',10000);
  3. 关闭自动提交
  4. 转账
    update person set money=500 where id=1;
    update person set money=9700 where id=2;
  1. 打开新的窗口 验证是否转账成功!数据没变,以为两次操作都是在内存中操作,并未提交
  2. 手动提交 commit;

# 数据回滚

  • 执行rollback会将数据回滚到上次提交的点 rollback;

# 设置回滚点

  1. 保存回滚点:savepoint s1(标识);
  2. 回滚到指定的回滚点: rollback to s1;

# SQL分类

# DDL

  • data Definition Language 数据定义语言,包括:create,alter,drop,truncate,不支持事务。
  • truncate 删除表 并且创建一个新表 truncate table t1;

# DML

  • Data Manipulation Language 数据操作语言,包括:insert, update, delete, select(DQL),支持事务

# DQL

  • Data Query Language数据查询语言,包括:select,跟事务没关系

# TCL

  • Transaction COntrol Language: 事务控制语言,包括:commit,rollback,savepoint

# DCL

  • Data Control Language:数据控制语言,分配用户权限相关的sql

# truncate, delete, drop的区别

  1. delete:删除表中的数据,自增数值不清零,支持事务
  2. drop:删除表,不支持事务
  3. truncate:删除表并且创建一个新表,自增数值清零,不支持事务

# 数据库的数据类型

  • 物种数据类型分别为:
  1. 整数
  2. 浮点数
  3. 字符串
  4. 日期
  5. 其他

# 整数

  • 常用:int(m) bigint(m), m代表显示长度,如果数据长度不足m时会在数值前面补0,但是必须和zerofill关键字结合使用,使用方式如下:
    例如: create table t_int(num int(10) zerofill);
    insert into t_int values(25);
    select * from t_int;

image

# 浮点数

  • 常用:
  1. double(m,d), m代表总长度,d代表小数长度,超过小数长度四舍五入,不足补零

image 2. decimal(m,d), m代表总长度,d代表小数长度,超高精度小数,需要设计超高精度运算的时候使用。

# 字符串

  • char(m): m代表字符长度,固定长度,执行效率高(不用改变长度),最大长度255,
  • varchar(m): 可变长度,会根据内容长度改变自身长度,更节省资源,最大长度65535,如果长度超过255建议使用text。
  • text: 可变长度,最大值65535。

# 日期

  • date: 只能保存 年月日
  • time: 只能保存 时分秒
  • datetime: 年月日时分秒,默认值为null, 最大值 9999年12月31号
  • timestamp: 年月日时分秒,默认值为当前时间,最大值,2038年1月19号
  • 测试:
    create table t_date(t1 date,t2 time, t3 datetime, t4 timestamp);
    insert into t_date values('2018-05-16',null,null,null);
    insert into t_date values(null,'12:32:18','2018_05-11 18:30:08',null);

image

在数据库中先导入day03中的两个文件,直接复制粘贴

# is null

  1. 查询没有上级领导的员工编号,姓名,工资
    select empno,ename,sal from emp where mgr is null;
    
  1. 查询emp表中没有奖金(comm)的员工姓名,工资和奖金
    select empno,ename,sal from emp where comm is null; 
    

# is not null 不为null

  1. 查询emp表中有奖金的员工
    select ename,sal,comm from emp where comm is not null;
    

# 别名,还有多表联查的作用

  1. 查询emp中所有姓名 将ename 显示成'姓名'
    select ename as '姓名' from emp;
    select ename '姓名' from emp;
    select ename 姓名 from emp;
    

# 去重

  1. 查询emp表中出现的所有职位
    select distinct job from emp;
    

# 比较运算符 >, <, >=, <=, =, !=和<>

  1. 查询工资小于等于1600的所有员工姓名和工资
    select ename,sal from emp where sal<=1600;
  2. 查询部门编号是20的所有员工姓名,职位和部门编号
    select ename,job,deptno from emp where deptno=20;
  3. 查询职位是manager的所有员工姓名和职位
    select ename,job from emp where job='manager';
  4. 查询部门不是10号部门的所有员工姓名和部门编号使用两种方式实现
    select ename,deptno from emp where deptno!=10; select ename,deptno from emp where deptno<>10;
  5. 查询t_item表中单价等于23的商品
    select id,title from t_item where price=23;
  6. 查询t_item表中单价不等于8443的商品的信息 select id,title from t_item where price!=8443;

# and和or

  • and和Java中的&&效果一样
  • or和java中的||效果一样
    1. 查询不是10号部门并且工资小于3000的员工信息
    select * from emp where deptno!=10 and sal<3000;
    
    1. 查询部门编号为30或者上级领导为7698的员工姓名,职位,上级领导和部门编号
    select ename,job,mgr,deptno from emp where deptno=30 or mgr=7698;
    

# in

  1. 查询emp表中工资为5000,1500,3000的员工信息
        select * from emp where sal in(5000,1500,3000);
    

# between x and y 在x和y之间(包括x和y)

  1. 查询emp表中,工资在2000至4000之间的员工信息
    select * from emp where sal between 2000 and 4000;
    

# like

  • _: 代表单个未知字符
  • %: 代表0个或多个未知字符
  • 举例:
    1. 包含字符a: %a%
    2. 以a开头:a%
    3. 以a结尾:%a
    4. 第二个字符是a:_a%
    5. 倒数第三个字符是a:%a__
    6. 第二个字符是a,最后一个字符是b:_a%b
  • like 案例
    1. 查询标题中包含记事本的商品信息
    select id,title,price from t_item where title like '%记事本%';
    
    1. 查询单价低于100的记事本标题和单价
    select title,price from t_item where title like '%记事本%' and price<100;
    
    1. 查询单价在50到200之间的得力商品
    select * from t_item where title like '%得力%' and price between 50 and 200;
    
    1. 查询有图片(image字段不为null)的得力商品
    select id,title,price,image from t_item where image is not null and title like '%得力%';
    
    1. 查询分类(category_id)为238,917的商品信息
    select id,title,price,category_id from t_item where category_id in(238,917);
    
    1. 查询含有赠品的商品信息(sell_point中包含赠字)
    select id,title,price,sell_point from t_item where sell_point like '%赠%';
    
    1. 查询标题中不包含得力的商品标题
    select * from t_item where sell_point not like '%得力%';
    
    1. 查询价格介于50到200之外的商品信息
    select * from t_item where price not between 50 and 200;
    

# 排序 order by

  • order by 写在where查询要求后面,没有where写在最后
  • by的后面写排序的字段名称
  • 默认排序是升序,也可指定升序:asc;降序desc
    1. 查询所有员工的姓名和工资,按照工资降序排序
    select ename,sal from emp order by desc;
    
    1. 查询所有dell商品,按照单价降序排列
    select * from t_item where title like '%dell%' order by price desc; 
    
    1. 查询所有员工信息按照部门编号升序排序,工资按照将序排序
    select * from t_item order by deptno,sal desc;
    

# 分页查询 limit

  • limit 跳过数量,每页的数量
  • limit关键字通常写在sql的最后面
    1. 查询所有商品 按照单价升序排序,显示第二页,每页7条数据
    select id,litle,price from t_item order by price desc limit 7,7;
    
    1. 查询工资金额前三名的三位员工信息
    select * from emp order by sal desc limit 0,3;
    

# 数值计算 + - * / % 7%2等效mod(7,2)

  1. 查询员工姓名,工资,及年终奖信息(年终奖=工资*5);
    select ename,sal,sal*5 年终奖 from emp;
    
  2. 查询t_item表中商品单价,库存和总金额(单价*库存)
    select ename,price,num,price*num 总金额 from t_item;
    

# 日期相关函数

select 'helloworld';

  • 获取当前日期+时间 now(); select now();
  • 获取当前日期 curdate(); select curdate()
  • 获取当前的时间 curtime(); select curtime()
  • 从年月日时分秒中,提取年月日和提取时分秒
    • 提取年月日 select date(now());
    • 提取时分秒 select time(now());
  • 从年月日时分秒中提取年,月,日,时,分,秒
        select extract(year from now());
        select extract(month from now());
        select extract(day from now());
        select extract(hour from now());
        select extract(minute from now());
        select extract(second from now());
    
  • 日期格式化函数
    • 格式:date_format(date,format);
    • format: %Y四位年,%y两位年,%m两位月,%c一位月份,%d日,%H 24小时,%h 12小时,%i 分,%s 秒;
    1. 把now()转换成2018年06月19日 16时27分30秒的格式
        select date_format(now(),'%Y年%m月%d日%H时%i分%s秒');
    
  • 把非标准格式的时间转换成标准格式 str_to_date(非标准格式的时间,非标准时间格式);
    1. 把14.08.2008 08:00:00 转换成标准格式
        select str_to_date('14.08.2008 08:00:00','%d.%m.%Y %H:%i:%s');
    

# ifnull()函数

  • age=ifnull(x,y) 如果x的值为null则age=y,如果x不为null,则age的值为x
    1. 将emp表中的奖金为null的全部修改为0
        update t_item set comm=ifnull(null,0);
    

# 聚合函数

  • 对多行数据进行统计:
  • 求和:sum(字段名) select sum(sal) from emp; emp中工资总和
  • 平均值:avg(字段名) select avg(sal) from emp where deptno=10;
  • 最大值:max(字段名) select max(sal) from emp where deptno=10;
  • 最小值:min(字段名) select min(sal) from emp where deptno=10;
  • 统计数量:count(*) select count(*) from emp where sal<1000;

# 字符串相关函数

  • 字符串拼接 concat(s1,s2) s1s2
    1. 查询emp表中,员工姓名和工资,工资后面有单位元
        select ename,concat(sal,'元') from emp;
    
  • 获取字符串的长度 char_length(str)
    1. 查询员工的姓名和名字的长度
        select ename,char_length(ename) 名字长度 from emp;
    
  • 获取字符串在另外一个字符串中出现的位置(位置从1开始)
    • 格式1:instr(str,substr) select instr('abcdefg','d');
    • 格式2: select locate('d','abcdefg');
  • 插入字符串(位置从1开始)
    • 格式:insert(str,start,length,newstr);
    select insert('abcdefg',3,2,'m');//abmefg
    
  • 转大写转小写
    • 格式: upper(str) lower(str)
    select upper('Nba'),lower('NBA');//NBA,nba
    
  • 从左边截取和从右边截取
    • 格式:left(str,count) right(str,count)
    select left('abcdefg',2);//ab
    select right('abcdefg',2);//fg
    
  • 去除字符串两端的空格
    select trim('   ab   ');//ab
    
  • 获取字符串()
    select substring('abcdefg',2);//从2开始截
    select substring('abcdefghij',4,3);//def(这里的3代表长度)
    
  • 重复 repeat(str,count);(内容,次数)
    select repeat('ab',2);//abab
    
  • 替换replace(str,old,new)
    select replace('abcde','c','m');//abmde
    
  • 反转 reverse(str)
    select reverse('abc');//cba
    

# 数学相关的函数

  • floor(num) 向下取整 select floor(3.84);//3
  • round(num) 四舍五入 select round(23.8);//24
  • round(num,m) 四舍五入,m代表小数位数 select round(23.869,2);//23.87
  • truncate(num,m) 和上面一样,(非四舍五入) select truncate(23.869,2);//23.86
  • rand() 随机数,获取0-1的随机数 select rand();
    1. 获取0-5的随机数 select floor(rand()*6);
    2. 获取3-8的随机数 select floor(rand()*6)+3;
    3. 获取8-10的随机数 select floor(rand()*3)+8;

# 分组查询

  • 分组查询通常和聚合函数结合使用
  • 一般情况下 每个部门(职位,分类)就以部门(职位,分类)作为分组的条件
  • 可以有多个分组条件
  • group by 存在的位置 where...group by...order by...limit...
    1. 查询每个部门的最高工资
    select deptno,max(sal) from emp group by deptno;
    
    1. 查询每个职位的平均工资
    select job,avg(sal) from emp group by job;
    
    1. 查询每个部门下每个主管的手下人数
    select deptno,mgr,count(mgr) from emp group by deptno,mgr;
    
  1. select deptno,count(),sum(sal) from emp group by deptno order by count(),sum(sal) desc;
  2. select deptno,sum(sal),avg(sal),min(sal),max(sal) from emp where sal between 1000 and 3000 group by deptno order by avg(sal);
  3. select job,count(*),sum(sal) 总和,avg(sal) 平均,min(sal) from emp where mgr is not null group by job order by 总和 desc, 平均;

#

  1. 查询每个部门的平均工资,要求平均工资大于2000;
  • 以下的错误语法: where后面不能写聚合函数
select deptno,avg(sal) a from emp where a>2000 group by deptno;
  • 使用having 解决聚合函数的条件过滤问题,having写在group by 后面
  • where后面写普通字段的过滤条件,having后面写聚合函数的过滤条件
    select deptno,avg(sal) a from emp group by deptno having a>2000;
    
    1. 查询每个分类商品的库存总量,高于1000的库存总量
    select category_id,sum(num) from t_item group by category_id having a>19999;
    
    1. 查询每个分类商品所对应的平均单价,要求平均单价低于100
    select  category_id,avg(price) from t_item group by category_id having avg(price)<100;
    
    1. 查询分类id为238和917两个分类的平均单价
    select category_id,avg(price) 单价 from t_item where category_id in(238,917) group by category_id;
    
    1. 查询emp表中每个部门的平均工资高于2000的部门编号,部门人数,平均工资,最后根据平均工资降序排序
    select deptno,count(*),avg(sal) a from emp group by deptno having avg(sal)>2000 order by a desc;
    
    1. 查询emp表中工资在1000-3000之间的员工,每个部门的编号,工资总和,平均工资,过滤掉平均工资低于2000的部门,按照平均工资升序排序
    select deptno,count(*),sum(sal),avg(sal) from emp where sal between 1000 and 3000 group by deptno having avg(sal)>=2000 order by avg(sal); 
    
    1. 查询emp表中名字不是以s开头,每个职位的名字,人数,工资总和,最高工资,过滤掉平均工资是3000的职位,根据人数升序排序,如果一致根据工资总和降序排序
    select job,count(*),sum(sal),avg(sal) from emp where ename not like 's%' group by job having avg(sal)!=3000 order by count(*),sum(sal) desc;
    
    1. 查询emp表,每年入职的人数(提高题)
    select extract(year from hiredate),count(*) from emp group by extract(year from hiredate);
    

# 子查询(嵌套查询)

# 子查询总结

  • 嵌套在sql语句中的查询语句称为查询语句
  • 子查询能嵌套n层
  • 子查询可写位置
    1. 可以写在where/having的后面作为查询条件的值
    2. 可以写在from后面当作一张新表 新表必须有别名
    3. 可以写在创建表的时候
    create table t_emp_10 as (select * from emp where deptno=10);
    
  1. 查询emp表中工资最高的员工信息
     select * from emp where sal=(select max(sal) from emp);
    
  2. 查询emp表中工资超过平均吸纳的所有员工的信息
    select * from emp where sal>(select avg(sal) from emp);
    
  3. 查询工资高于20号部门平均工资的员工信息
    select * from emp where sal>(select avg(sal) from emp where deptno=20);
    
  4. 查询和jones形同工作的其他员工信息
    select * from emp where job=(select job from emp where ename='jones') and ename!=jones;
    
  5. 查询工资最低的员工的相同部门的员工信息
    select * from emp where deptno=(select deptno from emp where sal=(select min(sal) from emp));
    
  6. 查询最后入职的员工信息
    select * from emp where hiredate=(select max(hiredate) from emp);
    
  7. 查询姓名为king部门编号和部门名称(需要使用dept表)
    select deptno,dname from dept where deptno=(select deptno from emp where ename='king');
    
  8. 查询有商品的分类id和分类名称(有商品 就是在商品中出现的分类,需要使用t_item_category表)
    - 先从商品表中得到所有的分类id
    select distinct category_id from t_item;
    - 从分类表中查询id等于上面结果的分类信息
    select id,name from t_item_category where id in(select distinct category_id from t_item);
    
  9. 查询有员工的部门信息
    select * from dept where deptno in(select distinct deptno from emp);
    
  10. 扩展题(难度最高):查询平均工资最高的部门信息
    • 得到最高的平均工资
    select deptno,avg(sal) a from emp group by deptno order by a desc limit 0,1;
    
    • 通过最高的平均工资得到部门编号
    select deptno from emp group by deptno having avg(sal)=(select avg(sal) a from emp group by deptno order by a desc limit 0,1);
    
    • 通过部门编号得到部门信息
    select * from dept where deptno in(select deptno from emp group by deptno having avg(sal)=(select avg(sal) a from emp group by deptno order by a desc limit 0,1));
    

# 关联查询

  • 同时查询多张表的数据称为关联查询
  1. 查询每一个员工的姓名和对应的部门名称
    select e.ename,d.dname from emp e,dept d where e.ename=d.ename;
    
  2. 查询在纽约工作的所有员工的信息
    select e.* from emp e,dept d where e.deptno=d.deptno and d.loc='new york';
    

# 笛卡尔积

  • 关联查询如果不写关联关系,则查询结果为两张表的乘积,这个乘积称为笛卡尔积
  • 笛卡尔积是一种错误的查询结果,工作中切记不要出现

# 等值链接和内部连接

  1. 等值连接:
    select * from A,B where A.x=B.x and A.age=18;
    
  2. 内连接 用的更多
    select * from A join B on A.x=B.x where A.age=18;
    
  • 查询每个员工的姓名和对应的部门名称
    select e.ename,d.dname from emp e join dept d on e.deptno=d.deptno;
    

# 外连接

  • 左外连接:以join左边表为主表,左边表显示所有数据,右边交集数据
    select e.ename,d.dname from emp e left join dept d on e.deptno=d.deptno;
    
  • 右外连接:以join右边表为主表,右边表显示所有数据,左边交集数据
    select e.ename,d.dname from emp e right join dept d on e.deptno=d.deptno;
    

# 关联查询总结

  1. 查两个表的交集数据,使用内连接(推荐)或等值连接
  2. 查一个表所有数据另外一个表交集数据使用外连接

# 表设计 关联关系

  • 外键:用来建立关系的字段称为外键
  • 主键:用来表示数据唯一性的字段称为主键

# 一对一

  • 什么是一对一:两张表a表和b表,a表中一条数据对应b表中的一条数据,这种关系称为一对一的关系
  • 应用场景:用户表和用户信息扩展表,商品表和商品信息表扩展表
  • 如何建立关系:在从表中添加一个外键字段值指向主表的主键字段
  1. 创建两张表
    create table user(id int primary key auto_increment,name varchar(10),password varchar(10));
    create table userinfo(userid int primary key,nick varchar(10),tel varchar(15),address varchar(20));
    
  2. 往表中添加数据
    insert into user values(null,'wukong','123456'),(null,'bajie','123456'),(null,'libai','123456');
    insert into userinfo values(1,'齐天大圣','13833446622','花果山'),(2,'二师兄','13833446622','高老庄'),(3,'李白','13833446622','语文书里');
    
  • 查询李白的用户名和密码是什么
    select u.username,u.password
    from user u join userinfo ui
    on u.id=ui.userid
    
  • 查询每一个用户的所有信息
    select * from user u join userinfo ui on u.id=ui.userid;
    
  • 查询用户名bajie的昵称是什么
    select ui.nick from user u join userinfo ui on u.id=ui.userid where u.name='bajie';
    

# 一对多

  • 什么是一对多:ab两张表,a表中一条数据对应b表中多条数据,b表中一条数据对应a表中一条数据,称为一对多
  • 应用场景:部门和员工,分类和商品等
  • 如何建立关系: 一对多的两张表,在多的表里面添加外键盘记录另外一个表的主键值
# 练习
  1. 创建t_emp(id,name,deptid) 和 t_dept(id,name,loc)
    create table t_emp(id int primary key auto_increment, name varchar(10), deptid int);
    create table t_dept(id int primary key auto_increment, name varchar(10), loc varchar(10));
    
  2. 表中插入数据 部门表:
    insert into t_emp values(null,'悟空',1),(null,'刘备',2),(null,'路飞',3),(null,'八戒',1);
    insert into t_dept values(null,'神仙部门','花果山'),(null,'三国部',
    '蜀国'),(null,'海贼部','日本');
    
  • 查询每个员工的姓名和部门
    select e.name,d.name from t_emp e join t_dept d on e.deptid=d.id;
    
  • 查询工作在花果山的员工
    select e.name from t_emp e join t_dept d on e.deptid=d.id
    where d.loc='花果山';
    

# 多对多

  • 什么是多对多:AB两张表,A表中的一条对应B表多条数据,同时B表中的一条数据对应A表的多条数据称为多对多关系。
  • 应用场景:老师和学生,用户表和角色表
  • 如何建立关系:需要创建单独的关系表,关系表中两个外键字段保存两个表的主键值。
# 练习:
  1. 创建三个表:student(id,name) teacher(id,name) t_s(sid,tid)
    create table student(id int primary key auto_increment, name varchar(10)); 
    create table teacher(id int primary key auto_increment,name varchar(10));
    create table t_s(tid int,sid int);
    
  2. 插入以下数据,苍老师和对应的学生:小明,小红,小花,刘老师对应的学生 小绿,小蓝,小明;
    insert into teacher values(null,'苍老师'),(null,'刘老师');
    insert into student values(null,'小明'),(null,'小红'),(null,'小花'),(null,'小绿'),(null,'小蓝');
    insert into t_s values(1,1),(2,1),(3,1),(4,2),(5,2),(1,2);
    
  3. 查询每个老师对应的所有学生
    select t.name,s.name from student s join t_s ts on s.id=ts.sid join teacher t on t.id=ts.tid;
    
  4. 查询小明的老师都有谁
    select t.name from student s join t_s ts on s.id=ts.sid join teacher t on t.id=ts.tid where s.name='小明';
    
  5. 查询苍老师的所有学生
    select s.name from student s join t_s ts on s.id=ts.sid join teacher t on t.id=ts.tid where t.name='苍老师';
    

# 自关联

  • 什么是自关联:在表中有一个外键字段指向自己表的主键值,称为自关联
  • 应用场景:需要保存层级关系,但是不确定有多少层的时候使用自关联
  1. 查询员工姓名和对应的上级主管姓名
    select e.ename,m.ename from emp e join emp m on e.empno=m.mgr;
    

# 连接方式和关联关系

  1. 连接方式:包括内连接和外连接(左外,右外),是关联查询的查询方式。
  2. 关联关系:包括一对一,一对多,多对多,是指设计表时两个表之间存在的逻辑关系。

# 案例:权限管理的设计

  • 用户表 角色表 权限表
  • 用户-角色关系表 角色—权限关系表
  1. 创建db4并使用,创建五张表

    create table user(id int primary key auto_increment,name varchar(10));
    create table role(id int primary key auto_increment,name varchar(10));
    create table module(id int primary key auto_increment,name varchar(10));
    
  2. 创建关系表

    create table u_r(uid int,rid int);
    create table r_m(rid int,mid int);
    
  3. 插入数据

    insert into user values(null,'刘德华'),(null,'张学友'),(null,'凤姐');
    insert into role values(null,'男游客'),(null,'男会员'),(null,'女游客'),(null,'女管理员');
    insert into module values(null,'男浏览'),(null,'男删帖'),(null,'女浏览'),(null,'女发帖'),(null,'女删帖');
    
  4. 插入关系数据

    insert into u_r values(1,1),(2,2),(3,4),(3,3);
    insert into r_m values(1,1),(2,1),(2,2),(3,3),(4,3),(4,4),(4,5);
    
  5. 查询每个用户对应的权限

    select u.name,m.name
    from user u join u_r ur on u.id=ur.uid 
    join r_m rm on ur.rid=rm.rid
    join module m on rm.mid=m.id;
    
  6. 查询张学友的所有权限

    select u.name,m.name
    from user u join u_r ur on u.id=ur.uid 
    join r_m rm on ur.rid=rm.rid
    join module m on rm.mid=m.id
    where u.name='张学友';
    
  7. 查询男发帖的用户都有哪些

    select u.name
    from user u join u_r ur on u.id=ur.uid 
    join r_m rm on ur.rid=rm.rid
    join module m on rm.mid=m.id
    where m.name='女发帖';
    
    1. 创建两张表
  • 创建trade交易流水表(id,time,money,type,pid)
    create database db6;
    use db6;
    create table trade(id int primary key auto_increment, time date,money int,type varchar(5),pid int);
    
  • 创建pwerson人物表(id,name,gender,rel)
    create table person(id int primary key auto_increment, name varchar(10),gender varchar(5),rel varchar(5));
    
  1. 插入数据
    insert into person values(null,'刘德华','男','亲戚'),(null,'杨幂','女','亲戚'),(null,'马云','男','同事'),(null,'特朗普','男','朋友'),(null,'貂蝉','女','朋友');
    insert into trade values
    (null,'2018-03-20',-20,'微信',1),
    (null,'2018-04-20',500,'现金',2),
    (null,'2018-05-20',-50,'现金',2),
    (null,'2018-03-21',50000,'支付宝',3),
    (null,'2018-03-22',-5,'支付宝',3),
    (null,'2018-03-26',2000,'微信',4),
    (null,'2018-04-22',-20000,'微信',5);
    
  2. 统计2018年2月15号到现在的所有红包收益
    select sum(money) from trade where time>str_to_date('2018年04月21号','%Y年%c月%d号');
    
  3. 查询2018年2月15号到现在金额大于100所有女性亲戚的名字和红包总额
    select p.name,t.money from trade t join person p on t.pid=p.id where p.gender='女' and  t.money not between -100 and 100 and t.time>str_to_date('2018年02月15号','%Y年%c月%d号') and p.rel='亲戚';
    
  4. 查询三个平台分别收入的红包总额
    select type,sum(money) from trade where money>0 group by type;
    

# 视图

  • 什么是视图:数据库中表和视图都是其内部的对象,视图可以理解成一个虚拟的表,视图本质就是取代了一段sql语句
  • 为什么使用视图:因为有些数据的查询需要使用大量的sql语句,每次书写比较麻烦,使用视图可以起到sql重用的作用,可以隐藏表中的敏感信息。
  • 如何使用:
    • 格式:create view 视图名 as 子查询;
  1. 练习1:创建部门是20并且工资小于3000的视图
    create view v_emp_20 as (select * from emp where deptno=20 and sal<3000);
    
  2. 练习2:创建每个部门的工资总和,平均工资,最大工资,最小工资的视图
    create view v_emp_group as ( select deptno,sum(sal),avg(sal),max(sal),min(sal) from emp group by deptno);
    

# 视图的分类

  1. 简单视图:创建视图的子查询中不包含:去重,函数,分组,关联查询的视图称为简单视图,可以对数据进行增删改查操作。
  2. 复杂视图:创建视图的子查询中包含:去重,函数,分组,关联查询的视图称为简单视图,只能进行查询操作

# 简单视图的增删改操作 操作方式和table一样

  1. 插入数据,如果插入的数据在视图中不显示但是原表中显示,称为数据污染
    insert into v_emp_10 (empno,ename) values(10010,'Tom');
    
    • 正确写法
    insert into v_emp_10 (empno,ename,deptno) values(10086,'jerry',10);
    
    • 可以通过with check option 关键字禁止出现数据污染
    create view v_emp_30 as (select * from emp where deptno=30)
    with check option;
    
    • 测试:
    insert into v_emp_30(empno,ename,deptno) values(10011,'张三',30);
    
  2. 修改数据,只能修改视图中存在的数据
    update v_emp_30 set ename='zhangsan' where empno=10011;(成功)
    update v_emp_30 set ename='汤姆' where empno=10010;(失败);
    
  3. 删除数据 只能删除视图中存在的数据
    delete from v_emp_30 where empno=10011;(成功)
    delete from v_emp_30 where empno=10010;(失败)
    

# 修改视图

  • 格式:create or replace view 视图名 as 子查询;
    create or replace view v_emp_10 as(select * from emp);
    

# 删除视图

drop view v_emp_10;
drop view if exists v_emp_10;没有也不会报错

# 视图字段别名

  • 创建视图的时候子查询对字段起了别名,则后期对视图进行操作只能使用别名。
create view v_emp_10 as (select empno,ename name from emp);
  • 测试:
    update v_emp_10 set name='汤姆' where empno=10010;(成功)
    update v_emp_10 set ename='汤姆aa' where empno=10010;(失败)
    

# 约束

  • 什么是约束:约束是给表字段添加的限制条件

# 非空约束 not null

  • 添加的字段值为null
    create table t1(id int,age int not null);
    insert into t1 values(1,20);(正确)
    insert into t1 values(2,null);(报错)
    

# 唯一约束 unique

  • 字段值不能重复
    create table t2(id int,age int unique);
    insert into t2 values(1,20);(成功)
    insert into t2 values(2,20);(失败)
    

# 主键约束 primary key

  • 字段值不能重复也不能为null
  • 创建表的时候添加主键
    create table t_pri(id int primary key auto_increment);
    
  • 创建表后添加主键
    create table t_pri2(id int);
    alter table t_pri2 add primary key(id);
    
  • 删除主键,一个表只能有一个主键
    alter table t_pri2 drop primary key;
    

# 自增

  1. 当字段的值为null的时候数值自动+1
  2. 删除数据数值不减
  3. 如果插入数据指定比较大的值,下次从最大值的基础上+1,如果删除最大值,也是从曾经出现的最大值基础上+1
  4. 使用delete删除全表数据,自增值不变
  5. 使用truncate

# 默认约束 default

  • 当字段的值为不赋值 的时候默认约束的内容生效
    create table t_def(id int,age int default 10);
    insert into t_def values(1,2);
    insert into t_def(id) values(2);
    

# 检查约束 check

  • mysql语法支持,不报错,但是没有效果
    create table t_check(id int,age int check(age>10));
    

# 外键约束

  • 作用:
  1. 外键值可以为null,可以重复,但是不能是不存在的值
  2. 外键指向的表,不能先删除
  3. 外键指向的数据不能先删除,如果需要删除先删除外键约束,或先删除外键的数据(断开关联关系)
  • 使用外键必须要两张表相同的InnoDB引擎,myisam不支持外键约束
  • 除非特定情况 一般工作中不使用外键约束,使用Java代码通过逻辑对插入和删除的数据进行限制,因为加了约束后不方便测试。
# 如何使用外键约束
  1. 创建部门表
    create table dept(id int primary key auto_increment, name varchar(10));
    
  2. 创建员工表
    constraint 外键约束名 foreign key(外键字段) references 表名(主键)
    
    create table emp(id int primary key auto_increment, name varchar(10), deptid int, constraint fk_dept foreign key(deptid) references dept(id));
    
    • 插入数据
    insert into dept values(null,'神仙部'),(null,'妖怪');
    
    • 测试:
    insert into emp values(null,'悟空',1);(成功)
    insert into emp values(null,'赛亚人',3);(失败)
    drop table dept;()失败
    delete from dept where id=1;(失败)
    

# 索引

# 导入数据

  • 在db6数据库下 执行source 命令,将文件拖入,自动补全路径;

  • 测试:

  1. show tables; 看是否有item2的表
  2. select count(*) from item2; 看是否有172万
  3. select * form item2 where title='100'; 看查询时间是多少
  • 什么是索引:索引是数据库中用来提高查询效率的技术,类似于目录
  • 为设么使用索引:如果不使用索引数据会零散的保存在磁盘块中,查询数据需要挨个的遍历每一个磁盘块,知道找到数据为止,使用索引会在磁盘中以树状结构对数据进行保存,查询数据时会大大降低磁盘块访问量,从而提高查询效率。

索引原理图

# 索引是越多越好吗?

索引会占用磁盘空间,只对常用的查询字段创建索引

# 有索引就一定好吗?

不一定,如果数据量比较小,使用索引反而会降低查询效率

# 索引的分类

  1. 聚集索引:一张表只有一个聚集索引,数据库会自动为添加了主键的表创建聚集索引,一般情况下聚集索引就是通过主键值创建的索引,聚集索引的树状结构中保存了数据
  2. 非聚集索引:一张表可以有多个非聚集索引,通过非主键字段创建的索引称为非聚集索引,非聚集索引中没有数据,保存的是磁盘块的地址

# 如何创建索引

  • 格式: create index 索引名 on 表名(字段名(长度)); (一般不写长度)
  • 创建title索引
    create index index_item2_title on item2(title);
    
  • 测试:
    select * from item2 where title='100';
    

# 查看索引

show index from item2;

# 删除索引

drop index index_item2_title on item2;

# 复合索引

  • 通过多个字段创建的索引称为复合索引
  • 应用场景:频繁使用多个字段作为查询条件时,可以为这几个字段创建一个复合索引
  • 创建格式:
    create index index_item2_title_price on item2(title,price);
    

# 索引总结

  1. 索引是用来提高查询效率的技术,类似目录的作用
  2. 因为会占用空间不是越多越好
  3. 数据量小时,会降低查询效率
  4. 聚集索引自动通过主键创建,保存数据,只有一个, 非聚集索引,有多个,没有数据只有磁盘块地址
  5. 尽量不要在频繁修改的表上创建索引

# 事务

  • 数据库汇总执行sql语句中的最小工作单月,不能拆分,执行同一业务的sql语句可以保证全部成功或全部失败

# 事务的ACID特性 面试常考

  • Atomicitu:原子性, 最小不可拆分,全部成功或全部失败
  • Consistency:一致性, 从一个一致状态到另一个一致状态
  • Isolation:隔离性, 多个事务之间互不影响
  • Durability:持久性, 事务完成后数据提交到数据库中持久保存

# 事务相关指令

  1. 查看自动提交状态 show variables like '%autocommit%';
  2. 设置自动提交状态 set autocommit=0/1;
  3. 提交 commit
  4. 回滚 rollback
  5. 保存回滚点 savepoint s1;
  6. 回滚到某个回滚点 rollback to s1;
Last Updated: 10/5/2021, 1:39:27 PM