大家已经学完基础的JavaSE篇章,内容很多~
已经入门了哦,现在继续努力吧!
大家需保持耐心,慢慢的学~
争取你们学习的速度!
跟上我更新的速度哦~
【主要内容】
2.数据库和操作工具的安装
3.创建数据库、表、视图、索引
4.查询、新增、删除、修改数据
5.条件查询
6.数据库的高级查询
7.MySQL的常用函数
8.MySQL版本新特性
9.MySQL索引优化
10.MySQL事务
【学习目标】
1,定义:描述事物的符号
2,表达形式:多种表现形式:文本,图形,音频,视频
1,粮库车库
2,存放数据的仓库存放在计算机中,按照一定格式存放可为用户共享
1,如何科学的组织和存储数据,如何高效的获取和维护数据,靠数据库管理系统完成
2,OracleMySQLSQLServer(微软)DB2FoxPro,Access
1,在数据库管理系统基础上,使用数据库管理系统的语法,开发的直接面对最终用户的应用程序
2,学生管理系统、人事管理系统、图书管理系统
1,数据库管理系统的操作者
1,数据库应用程序的使用者
1,数据库+数据库管理系统+数据库应用程序+最终用户
1,网状数据库
2,层次数据库
3,关系数据库
采用关系[二维表]结构存储与管理数据
采用结构化查询语言(sql)作为客户端数据库服务器间沟通的桥梁
目前主流的数据库技术
4,对象数据库
1,Notonlysql数据库泛指非关系数据库。如MongoDB,Redis
2,关系数据库在超大规模和高并发的web2.0纯属动态网站已经显示力不从心,暴露了很多难以克服的问题。Nosql数据库的产生就是为了解决大无框数据集合多重数据种类带来的挑战,尤其是大数据应用难题
数据库厂商
Oracle甲骨文公司
MySQLMySQL
SQLServerMicrosoft
DB2IBM
SqliteRichardHipp
msi文件:mysql数据库的安装文件
navicat:操作数据库的工具
Navicat:破解navicat的工具
vcredis_x64.exe:安装mysql环境检查的通过的一个工具
2.2.2读条中
2.2.3安装主页面
2.2.4这里选择只安装服务
上图中环境有问题,说差一个MicrosoftVisualC++2013
vcredis_x64.exe----》双击安装它
2.2.8下一步
2.2.9执行安装并下一步
2.2.10下一步
2.2.11设置root用户的密码
2.2.12设置安装的服务名
2.2.13下一步完成配置
2.3.3下一步
2.3.4下一步
2.3.5下一步到结束【安装中】
2.3.6安装完成
2.4.2再解压进入
2.4.3双击运行上面的文件如下
2.4.4点击patch后
2.4.5生成注册码并复制这个注册码
2.4.6打开navcat并点击注册
2.4.7输入注册码并点击激活
2.4.9按下图操作
2.4.11连接mysql
2.5.2复制这个目录地址右键此电脑->属性->高级系统设置
2.5.3打开环境变量
2.5.4新建
2.5.5配置PATH并确定
2.5.6测试环境变量是否生效
按win+r键输入cmd
输入mysql-uroot-p123456
3.1.2方式2
win+rcmd
netstartmysql启动
netstopmysql停止
mysql-uroot-p123456
root是默认的用户名---超级管理员
123456是之前我们安装的时候设置的默认密码
showdatabases;
information_schema数据库是MySQL自带的,它提供了访问数据库元数据的方式。什么是元数据呢?元数据是关于数据的数据,如数据库名或表名,列的数据类型,或访问权限等。有些时候用于表述该信息的其他术语包括“数据词典”和“系统目录”。
这个是mysql的核心数据库,主要负责存储数据库的用户、权限设置、关键字等mysql自己需要使用的控制和管理信息。不可以删除,如果对mysql不是很了解,也不要轻易修改这个数据库里面的表信息。
mysql5.5版本新增了一个性能优化的引擎:PERFORMANCE_SCHEMA这个功能默认是关闭的:
需要设置参数:performance_schema才可以启动该功能,这个参数是静态参数,只能写在
my.cnf中不能动态修改。
windows里面叫my.ini文件C:\ProgramData\MySQL\MySQLServer5.7【默认在这个目录】
linux里面叫my.cnf文件
通过这个库可以快速的了解系统的元数据信息
这个库确实可以方便DBA发现数据库的很多信息,解决性能瓶颈都提供了巨大帮助
这个库就是一个测试库而已
以上的所有默认库。只有、test库能删除,其它的都不要删除
--语法
--use<数据库名称>;
--例如:使用mysql数据库
usemysql;
--1使用mysql数据库
--2修改密码命令(将root用户的密码修改为123456)
updatemysql.usersetauthentication_string=PASSWORD('123456')whereuser='root;
--3刷新
flushprivileges;
CREATEUSER'username'@'host'IDENTIFIEDBY'password';
username:你将创建的用户名
host:指定该用户在哪个主机上可以登陆,如果是本地用户可用localhost,如果想让该用户可以从任意远程主机登陆,可以使用通配符%
password:该用户的登陆密码,密码可以为空,如果为空则该用户可以不需要密码登陆服务器
CREATEUSER'dog'@'localhost'IDENTIFIEDBY'123456';
CREATEUSER'pig'@'192.168.1.101_'IDENDIFIEDBY'123456';
CREATEUSER'pig'@'%'IDENTIFIEDBY'123456';
CREATEUSER'pig'@'%'IDENTIFIEDBY'';
CREATEUSER'pig'@'%';
GRANTprivilegesONdatabasename.tablenameTO'username'@'host'
privileges:用户的操作权限,如SELECT,INSERT,UPDATE等,如果要授予所的权限则使用ALL
databasename:数据库名
tablename:表名,如果要授予该用户对所有数据库和表的相应操作权限则可用*表示,如*.*
GRANTSELECT,INSERTONtest.userTO'pig'@'%';
GRANTALLON*.*TO'pig'@'%';
GRANTALLONmaindataplus.*TO'pig'@'%';
GRANTprivilegesONdatabasename.tablenameTO'username'@'host'WITHGRANTOPTION;
--创建数据库有两种方式:
--第一种语法如下--注意:<>里面的内容都是可变的--createdatabase<数据库名称>;--例如createdatabasewhpowernode;--第二种语法如下--ifnotexists创建之前会做check操作,如果数据库名称在MySQL数据库管理系统中不存在才创建--charsetdefault为数据库指定默认编码--createdatabase[ifnotexists]<数据库名称>defaultcharset<数据库字符集编码>;--例如:createdatabaseifnotexistswhpowernodedefaultcharsetUTF8;createdatabaseifnotexistswhpowernodedefaultcharsetUTF8MB4;
--第三种:createdatabase<数据库名称>[charactersetutf8];--UTF8和UTF8MB4区别?--绝大多数情况下使用UTF8编码的字符集一个汉字占据3个字符,但是有极少数情况下一个汉字占据4个字符--占据4个字符的汉字不能使用UTF8存储,必须使用UTF8MB4存储--UTF8MB4字符集编码表示一个汉字最多占据4个字符MMostBByte--注意:创建数据库指定的字符集编码必须跟MySQL数据库里面的my.ini字符集编码保持一致
--第一种方式语法:--dropdatabase<数据库名称>;--例如:dropdatabasewhpowernode;--第二种方式语法:--删除之前先判断存在才删除--dropdatabaseifexists<数据库名称>;--例如:dropdatabaseifexistswhpowernode;
alterdatabase<数据库名称>characterset新编码名;
--查询mysql管理系统软件下所有的库:
--查询指定库的详细信息
showcreatedatabase<数据库名称>;
--查看当前用户使用的是具体哪个库
selectdatabase();
SQL是一个面向过程的结构化查询语言,全称StructQueryLanguage。我们对MySQL做的任何操作(命令)都建立在结构化查询语言中。
SQL由以下四部分组成:DDL、DML、DQL、TCL
全称DataDefinitionLanguage,主要用于创建和删除结构。
删除数据关键字:delete
修改数据关键字:update
对表和视图进行操作,全称:DataQueryLanguage
DQL关键字select
TCL全称TransactionControlLanguage,使用事务管理DML操作。
提交事务关键字commit;
回滚事务关键字rollback;
开启事务关键字starttransaction;
5.2小数类型
5.3定点数
浮点型在数据库中存放的是近似值,而定点类型在数据库中存放的是精确值。decimal(m,d)参数m是总个数,d是小数位。
char:存储定长数据很方便,CHAR字段上的索引效率级高,必须在括号里定义长度,可以有默认值,比如定义char(10),那么不论你存储的数据是否达到了10个字节,都要占去10个字节的空间(自动用空格填充),且在检索的时候后面的空格会隐藏掉,所以检索出来的数据需要记得用什么trim之类的函数去过滤空格。
varchar:存储变长数据,但存储效率没有CHAR高,必须在括号里定义长度,可以有默认值。保存数据的时候,不进行空格自动填充,而且如果数据存在空格时,当值保存和检索时尾部的空格仍会保留。另外,varchar类型的实际长度是它的值的实际长度+1,这一个字节用于保存实际使用了多大的长度。
text:存储可变长度的非Unicode数据,最大长度为2^31-1个字符。text列不能有默认值,存储或检索过程中,不存在大小写转换,后面如果指定长度,不会报错误,但是这个长度是不起作用的,意思就是你插入数据的时候,超过你指定的长度还是可以正常插入。
1、经常变化的字段用varchar;
2、知道固定长度的用char;
3、超过255字节的只能用varchar或者text;
4、能用varchar的地方不用text;
5、能够用数字类型的字段尽量选择数字类型而不用字符串类型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接回逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了;
6、同一张表出现多个大字段,能合并时尽量合并,不能合并时考虑分表
MySQL数据类型含义
date日期'2021-1-1'
Logic类型使用bit表示,它只能存储两个值0或者1
createtable<表名称>
(
列名称1数据类型1,
列名称2数据类型2,
..........
列名称n数据类型n
);
createtabletb_student(
idint(11),
stu_namevarchar(50),
stu_sexbit,
stu_hobbyvarchar(50),
stu_agesmallint
--注意:最后一列没有逗号
--列与列之间以半角逗号相隔
createtablestudent(
)defaultcharset=UTF8,ENGINE=INNODB;
DEFAULTCHARSET用于指定表中数据的字符集编码方式
ENGINE用于指定表的存储引擎
现在我们在数据库中创建了一个表,那么如我们把电脑重启,这个表还在不在呢?
测试结果为在那是为什么呢?
原因是当我们创建表个表之后,在我们数据库的文件系统里面就会有一个对应的文件存在
.frm文件:保存了每个表的元数据,包括表结构的定义等;
.ibd文件:InnoDB引擎开启了独立表空间(my.ini中配置innodb_file_per_table=1)产生的存放该表的数据和索引的文件。
创建表之后如何查看是否创建成功?可以通过desc命令来查看表结构
--第一种方式查看表结构
--desc<表名称>;
descstudent;
--第二种方式查看表结构
--showcreatetable<表名称>;
showcreatetabletb_student;
基本语法如下:altertable<表名称>;
添加列,例如:向student表新增加一列stu_height学生身高
--语法如下:
--altertable<表名称>addcolumn<列名称><数据类型>;
altertablestudentaddcolumnstu_heightint(11);
--查看表结构
删除某一个列,例如:删除student表中的学生身高
--altertable<表名称>dropcolumn<列名称>;
altertablestudentdropcolumnstu_height;
修改列名称和数据类型:例如修改stu_sex为student_sex数据类型修改为varchar(30)
--altertable<表名称>changecolumn<原列名称><新列名称><数据类型>;
--修改stu_sex为student_sex数据类型修改为varchar(30)
altertablestudentchangecolumnstu_sexstudent_sexvarchar(30);
修改某一列的数据类型,例如:将学生年龄修改为int类型
--altertable<表名称>modifycolumn<原来的列名称><新的数据类型>;
--例如:
altertablestudentmodifycolumnstu_ageint(11);
--droptable<表名称>;
droptableEMP_bak;
--droptableifexists<表名称>;
--刪除之前先做校验,表在数据库中存在才删除
droptableifexistsEMP_bak;
showtables;
通过我们上面的操作,存放数据的容器创建好了,那么现在接着就是要向这个容器里面添加修改删除数据了
--往表中添加数据(insert,插入)
--插入方式1:insertinto表名(字段1,字段2,...字段N)values(值1,值2,...值N);
insertintostudents(stuNo,stuName)values('10086','王二麻子');
insertintostudents(stuNo,stuName,gender,age)values('10010','张三','男',20);
--插入方式2:insertinto表名values(值1,值2,...值N);
insertintostudentsvalues('10011','李四','男',28);
--插入方式3:insertinto表名set字段1=值1,字段2=值2,...字段n=值n;
insertintostudentssetstuNo='10000',stuName='凤姐';
--插入方式4:insertinto表名(字段1,字段2,...字段N)values(值1,值2,...值N),(值1,值2,...值N),(值1,值2,...值N)
insertintostudents(stuNo,stuName,gender,age)values
('10010','刘涛','男',18),('10020','刘西陵','男',28),('10030','张文杰','男',38);
--修改所有数据
--update表名set字段1=新值1,字段2=值2,...字段N=值N[where条件];
--例如:student表的年龄全部修改为50
updatestudentsetstu_age=50;
--如果只想修改某一行的数据,需要加上条件--例如:我只想修改id为1031的年龄为60--where表示行过滤--whereid=1031表示只选择id为1031的行进行修改--下面代码执行步骤:--首先执行updatestudent确定修改那张表--然后执行where条件确定修改表中的哪一行,不满住条件的行都会被过滤掉updatestudentsetstu_age=60whereid=1031;
updatestudentsetstu_age=60,name='习大大'whereid=1031;
--删除某一条数据语法:
--deletefrom表名[where条件];
--例如:删除id为108的student数据
deletefromstudentwhereid=108;
--deletefrom<表名称>;
--例如:删除student表所有数据
deletefromstudent;
truncate是DDL,删除所有数据,不支持where,不能回滚
delete是DML,可以删除部分数据,因为支持where,可以回滚
总体上truncate效率比delete高,他们都只能删除数据不能删除表结构,只有drop才能删除表结构
--truncate[table]表名;
为什么要学习数据完整性?我们上面创建的tb_student表缺乏安全性,还会有重复的id,这些都是脏数据。
什么是数据完整性?需要为MySQL数据库中的表定义一个规则,确保表中数据的有效性、一致性、安全性、尽最大的可能减少脏数据,重复的数据(冗余)。
什么是主键:主要的关键字,一个表如果创建了主键那么该行的所有数据必须在表中唯一。
身份证号就是身份证表的主键,能够确保在表中的唯一性,并且身份证编号是不能为空的
身份证编号
姓名
地址
所属公安局
性别
420193425324634256
张三
武汉
武昌分局
男
420193425324634258
李四
杭州
420193425324634251
王五
长沙
女
主键约束小结:非空并且唯一
在开发中,一般的主键为数值类型,呈递增趋势
如果不想每次都插入主键,我们可以让数据自动增长
语法:
createtabletb_class(
idint(11)auto_increment,--id是主键列,不用显示插入值让其自动增长
class_namevarchar(30)notnull,--notnull非空约束
class_descvarchar(100),--班级描述
primarykey(id)--为tb_class表的id列设置主键约束
)ENGINE=INNODB,DEFAULTCHARSETUTF8;
注意:MySQL数据库auto_increment自动增长和主键primarykey是配套的不能单独使用
制定一个规则,让表的某一列数据必须唯一
特征:某列数据可以为空,但是必须唯一
例如:学生的手机号码
createtabletb_1(
phoneintunique
)
为某一列制定一个默认规则
例如:性别默认为0
phoneintunique,
sexintDEFAULT‘0’
外部的关键字叫做外键,通常为多张表中建立联系,确保表与表之间的数据安全性,一致性,能够减少数据冗余(重复的数据)。
前面几个约束:在一张表建立约束(规则)
外键约束:多张表之间建立约束(关联,联系)
场景:创建一个tb_student表,为其添加默认约束、唯一约束、非空约束、外键约束
idint(11)auto_increment,
stu_namevarchar(50)notnull,
stu_mobilevarchar(20)unique,--unique唯一混熟
stu_sexbitdefault1,--默认约束性别默认为10女1男
class_idint(11)notnull,
primarykey(id),
--学生表的class_id建立外键去关联tb_class表的主键
foreignkey(class_id)referencestb_class(id)
约束名称关键字
主键约束primarykey
非空约束notnull
唯一约束unique
默认约束default
外键约束foreignkey.......references
约束分为两类:
1行级别约束:主键约束、外键约束
2列级别约束:非空约束、唯一约束、默认约束
外键约束小结:
foreignkey(外键列)references主表(主键列)references关键字的右边是主表的主键列,左边是从表的外键列,该关键字在主表和从边之间建立了联系。
创建表小结:没有外键的表是主表,有外键的表是从表。先创建主表后创建从表(有外键的表是从表)
插入数据小结:先插入主表数据后插入从表数据
删除数据小结:先删除从表数据后删除主表数据
一对一关系就如球队与球队所在地址之间的关系,一支球队仅有一个地址,而一个地址区也仅有一支球队。
数据表间一对一关系的表现有两种,一种是外键关联,一种是主键关联。图示如下:
一对一外键关联:
存在最普遍的映射关系,简单来讲就如球员与球队的关系;
一对多:从球队角度来说一个球队拥有多个球员即为一对多
多对一:从球员角度来说多个球员属于一个球队即为多对一
数据表间一对多关系如下图:
9.2.1案例:
--班级表
--学生表createtabletb_student(idint(11)auto_increment,stu_namevarchar(50)notnull,stu_mobilevarchar(20)unique,--unique唯一混熟stu_sexbitdefault1,--默认约束性别默认为10女1男class_idint(11)notnull,primarykey(id),--学生表的class_id建立外键去关联tb_class表的主键foreignkey(class_id)referencestb_class(id))ENGINE=INNODB,DEFAULTCHARSETUTF8;
多对多关系也很常见,例如学生与选修课之间的关系,一个学生可以选择多门选修课,而每个选修课又可以被多名学生选择。
数据库中的多对多关联关系一般需采用中间表的方式处理,将多对多转化为两个一对多。
数据表间多对多关系如下图:
9.3.1案例:
--多对多
--学生表
idbigint(20)primarykeyauto_incrementcomment'主键',
stu_namevarchar(50)comment'学生名',
stu_mobilevarchar(20)comment'手机号',
stu_sexvarchar(2)default'1'comment'性别默认为10女1男'
--课程表
createtabletb_course(
course_namevarchar(30)notnullcomment'课程名称'
--学生课程中间表
createtabletb_student_course(
stu_idbigint(20)comment'学生id',
course_idbigint(11)comment'课程id',
course_scorefloat(4,1)comment'课程的成绩',
FOREIGNKEY(course_id)REFERENCEStb_course(id),
FOREIGNKEY(stu_id)REFERENCEStb_student(id)
现在要为一个公司设计一个系统
1,可以记录员工信息
2,可以记录公司所有的部门信息
3,可以记录工资等级信息
4,可以记录公司的职位
9.6.2员工表(emp)
9.6.3工资等级表(salgrade)
9.6.4表关系分析
9.6.5创建表的脚本
------------------------------
--创建部门表
DROPTABLEIFEXISTSdept;
CREATETABLEdept(
DEPTNOint(11)NOTNULLCOMMENT'部门编号',
DNAMEvarchar(20)NOTNULLCOMMENT'部门名称',
LOCvarchar(20)COMMENT'部门所在的位置',
PRIMARYKEY(DEPTNO)
)ENGINE=InnoDBCHARACTERSET=utf8mb4COMMENT='部门表';
--创建员工表
DROPTABLEIFEXISTSemp;
CREATETABLEemp(
EMPNOint(11)NOTNULLCOMMENT'雇员的编号',
ENAMEvarchar(50)NOTNULLCOMMENT'雇员的姓名',
JOBvarchar(50)NOTNULLCOMMENT'职位',
MGRint(11)NULLDEFAULTNULLCOMMENT'雇员对应的领导编号',
HIREDATEdateNULLDEFAULTNULLCOMMENT'雇员的雇佣日期',
SALdecimal(7,2)NULLDEFAULTNULLCOMMENT'基本工资',
COMMdecimal(7,2)NULLDEFAULTNULLCOMMENT'奖金,佣金',
DEPTNOint(11)NULLDEFAULTNULLCOMMENT'雇员所在的部门编号',
PRIMARYKEY(EMPNO),
CONSTRAINTFK_EMP_DEPTNOFOREIGNKEY(DEPTNO)REFERENCESdept(DEPTNO)ONDELETERESTRICTONUPDATERESTRICT
)ENGINE=InnoDBCHARACTERSET=utf8mb4COMMENT='员工表';
--工资等级表
DROPTABLEIFEXISTSsalgrade;
CREATETABLEsalgrade(
GRADEint(11)NOTNULLCOMMENT'工资的等级主键',
LOSALdecimal(7,2)NOTNULLCOMMENT'此等级的最低工资',
HISALdecimal(7,2)NOTNULLCOMMENT'此等级的最高工资',
PRIMARYKEY(GRADE)
)ENGINE=InnoDBCHARACTERSET=utf8mb4COMMENT='工资等级表';
--部门表插入数据
INSERTINTODEPTVALUES(10,'财务部','武汉');
INSERTINTODEPTVALUES(20,'研发部','武汉');
INSERTINTODEPTVALUES(30,'销售部','深圳');
INSERTINTODEPTVALUES(40,'业务部','上海');
--员工表插入数据
INSERTINTOEMPvalues(7369,'SMITH','CLERK',7902,'1980-12-17',800.00,null,20);
INSERTINTOEMPvalues(7499,'ALLEN','SALESMAN',7698,'1981-02-20',1600.00,300.00,30);
INSERTINTOEMPvalues(7521,'WARD','SALESMAN',7698,'1981-02-22',1250.00,500.00,30);
INSERTINTOEMPvalues(7566,'JONES','MANAGER',7839,'1981-04-02',2975.00,null,20);
INSERTINTOEMPvalues(7654,'MARTIN','SALESMAN',7698,'1981-09-28',1250.00,1400.00,30);
INSERTINTOEMPvalues(7698,'BLAKE','MANAGER',7839,'1981-05-01',2850.00,null,30);
INSERTINTOEMPvalues(7782,'CLARK','MANAGER',7839,'1981-06-09',2450.00,null,10);
INSERTINTOEMPvalues(7788,'SCOTT','ANALYST',7566,'1987-04-19',3000.00,null,20);
INSERTINTOEMPvalues(7839,'KING','PRESIDENT',null,'1981-11-17',5000.00,null,10);
INSERTINTOEMPvalues(7844,'TURNER','SALESMAN',7698,'1981-09-08',1500.00,0.00,30);
INSERTINTOEMPvalues(7876,'ADAMS','CLERK',7788,'1987-05-23',1100.00,null,20);
INSERTINTOEMPvalues(7900,'JAMES','CLERK',7698,'1981-12-03',950.00,null,30);
INSERTINTOEMPvalues(7902,'FORD','ANALYST',7566,'1981-12-03',3000.00,null,20);
INSERTINTOEMPvalues(7934,'MILLER','CLERK',7782,'1982-01-23',1300.00,null,10);
--工资等级表插入数据
INSERTINTOSALGRADE(GRADE,LOSAL,HISAL)values(1,700,1200);
INSERTINTOSALGRADE(GRADE,LOSAL,HISAL)values(2,1201,1400);
INSERTINTOSALGRADE(GRADE,LOSAL,HISAL)values(3,1401,2000);
INSERTINTOSALGRADE(GRADE,LOSAL,HISAL)values(4,2001,3000);
INSERTINTOSALGRADE(GRADE,LOSAL,HISAL)values(5,3001,9999);
select*fromemp;
select*fromdept;
selectempno,ename,sal,jobfromemp;
selectempno,ename,sal,sal*1.08fromemp;
selectempno,ename,sal,sal*12fromemp;
selectempno,ename,sal,sal*12+1000fromemp;
注:在Select语句中,对数值型数据可以使用算术运算符创建表达式
selectempnoas员工编号,ename员工姓名,sal*12年薪fromemp;
selectempno,ename"Ename",sal*12"AnualSalary"fromemp;
selectsal*12+5000as"年度工资(加年终奖)"fromemp;
重命名查询结果中的字段,以增强可读性
别名如果含有空格或其他特殊字符或大小写敏感,需用双引号引起来。
AS可以省略
缺省情况下,查询结果中包含所有符合条件的记录行,包括重复行
selectdeptnofromemp;
使用DISTINCT关键字可从查询结果中清除重复行
selectdistinctdeptnofromemp;
selectdistinctjobfromemp;
DISTINCT的作用范围是后面所有字段的组合
select,distinctdeptnojobfromemp;
--排序:SELECT*FROM表名orderby排序字段1,排序字段2desc(降序)/asc(升序)
使用orderby子句对查询结果进行排序
排序方式包括升序(asc,缺省)和降序(desc)两种:
selectempno,ename,salfromemporderbysal;
selectempno,ename,salfromemporderbysaldesc;
按多字段排序
selectdeptno,empno,ename,salfromemporderbydeptno,sal;
使用字段别名排序
selectempno,ename,sal*12annsalfromemporderbyannsal;
关键字:limit
--select*from表名limit起始下标,pageSize;
select*fromemplimit3
小结:limit后面跟1个参数,例如limit3此时表示最多返回3行
limit参数1,参数2
参数1:表示伪列(index)的偏移量(下标从0开始)参数2:表示返回最大限制的行数
select*fromemplimit3,6
将多个select语句联合(合并)为一个select语句,涉及的关键字union和unionall。
unionall不管是否重复,全部合并
union如果有重复的,过滤掉重复的
select*fromempwhereDEPTNO=10
unionall
select*fromempwhereDEPTNO=20
union
作用:指定查询条件使用where子句
select*fromempwheredeptno=10;
select*fromempwhereename='JACK';
select*fromempwherehiredate='2020-12-12';
注意:
字符串和日期值要用单引号扩起来
字符串大小写敏感
日期值格式敏感,缺省的日期格式是'YYYY-MM-DDHH:mm:ss'
MySQL支持如下比较运算符:>>=<<=!=<>
注意:MySQL使用=运算符来判断表达式是否相等,它没有==
Java使用!=表示不等于,MySQL也支持。但是我们不要使用,效率太低了
MySQL使用的不等于使用<>
select*fromempwheresal>2900;
select*fromempwheredeptno<>30;
select*fromempwheresalbetween1600and2900;
select*fromempwhereenamein('SMITH','CLARK','KING');
%表示零或多个字符_表示一个字符
对于特殊符号可使用ESCAPE标识符来查找
用法举例
select*fromempwhereenamelike'S%';
select*fromempwhereenamelike'_A%';
select*fromempwhereenamelike'%\_%'escape'\';
select*fromempwherecommisnull;
select*fromempwherecommisnotnull;
select*fromempwheredeptno=10andsal>1000;
select*fromempwheredeptno=10orjob=‘CLERK’;
select*fromempwheresalnotin(800,1500,2000);
AND:把检索结果较少的条件放到后面
OR:把检索结果较多的条件放到后面
可使用小括号强行改变运算顺序
select*fromempwherejob='SALESMAN'orjob='CLERK'andsal>=1280;
select*fromempwhere(job='SALESMAN'orjob='CLERK')andsal>=1280;
使用函数可以大大提高SELECT语句操作数据库的能力。它给数据的转换和处理提供了方便。
函数只是将取出的数据进行处理,不会改变数据库中的值。
数学函数
字符串函数
条件判断函数
系统信息函数
加密函数
格式化函数
仅适用数值型的多行函数
nsum()求和
navg()求平均值
适用任何类型数据
ncount()计数
nmax()求最大值
nmin()求最小值
(1)ABS(x)返回x的绝对值
(2)PI()返回圆周率π,默认显示6位小数
(3)SQRT(x)返回非负数的x的二次方根
(4)MOD(x,y)返回x被y除后的余数
(5)CEIL(x)、CEILING(x)返回不小于x的最小整数
(6)FLOOR(x)返回不大于x的最大整数
(7)ROUND(x)、ROUND(x,y)前者返回最接近于x的整数,即对x进行四舍五入;后者返回最接近x的数,其值保留到小数点后面y位,若y为负值,则将保留到x到小数点左边y位
(8)SIGN(x)返回参数x的符号,-1表示负数,0表示0,1表示正数
(9)POW(x,y)和、POWER(x,y)返回x的y次乘方的值
(10)EXP(x)返回e的x乘方后的值
(11)LOG(x)返回x的自然对数,x相对于基数e的对数
(12)LOG10(x)返回x的基数为10的对数
(13)RADIANS(x)返回x由角度转化为弧度的值
(14)DEGREES(x)返回x由弧度转化为角度的值
(15)SIN(x)、ASIN(x)前者返回x的正弦,其中x为给定的弧度值;后者返回x的反正弦值,x为正弦
(16)COS(x)、ACOS(x)前者返回x的余弦,其中x为给定的弧度值;后者返回x的反余弦值,x为余弦
(17)TAN(x)、ATAN(x)前者返回x的正切,其中x为给定的弧度值;后者返回x的反正切值,x为正切
(18)COT(x)返回给定弧度值x的余切
(19)RAND(x)返回0-1之间的随机小数
(1)CHAR_LENGTH(str)计算字符串字符个数
(2)CONCAT(s1,s2,...)返回连接参数产生的字符串,一个或多个待拼接的内容,任意一个为NULL则返回值为NULL
(3)CONCAT_WS(x,s1,s2,...)返回多个字符串拼接之后的字符串,每个字符串之间有一个x
(4)INSERT(s1,x,len,s2)返回字符串s1,其子字符串起始于位置x,被字符串s2取代len个字符
(5)LOWER(str)和LCASE(str)、UPPER(str)和UCASE(str)前两者将str中的字母全部转换成小写,后两者将字符串中的字母全部转换成大写
(6)LEFT(s,n)、RIGHT(s,n)前者返回字符串s从最左边开始的n个字符,后者返回字符串s从最右边开始的n个字符
(7)LPAD(s1,len,s2)、RPAD(s1,len,s2)前者返回s1,其左边由字符串s2填补到len字符长度,假如s1的长度大于len,则返回值被缩短至len字符;前者返回s1,其右边由字符串s2填补到len字符长度,假如s1的长度大于len,则返回值被缩短至len字符
(8)LTRIM(s)、RTRIM(s)前者返回字符串s,其左边所有空格被删除;后者返回字符串s,其右边所有空格被删除
(9)TRIM(s)返回字符串s删除了两边空格之后的字符串
(10)TRIM(s1FROMs)删除字符串s两端所有子字符串s1,未指定s1的情况下则默认删除空格
(11)REPEAT(s,n)返回一个由重复字符串s组成的字符串,字符串s的数目等于n
(12)SPACE(n)返回一个由n个空格组成的字符串
(13)REPLACE(s,s1,s2)返回一个字符串,用字符串s2替代字符串s中所有的字符串s1
(14)STRCMP(s1,s2)若s1和s2中所有的字符串都相同,则返回0;根据当前分类次序,第一个参数小于第二个则返回-1,其他情况返回1
(15)SUBSTRING(s,n,len)、MID(s,n,len)两个函数作用相同,从字符串s中返回一个第n个字符开始、长度为len的字符串
(16)LOCATE(str1,str)、POSITION(str1INstr)、INSTR(str,str1)三个函数作用相同,返回子字符串str1在字符串str中的开始位置(从第几个字符开始)
(17)REVERSE(s)将字符串s反转
(18)ELT(N,str1,str2,str3,str4,...)返回第N个字符串
(1)CURDATE()、CURRENT_DATE()将当前日期按照"YYYY-MM-DD"或者"YYYYMMDD"格式的值返回,具体格式根据函数用在字符串或是数字语境中而定
(3)MONTH(date)和MONTHNAME(date)前者返回指定日期中的月份,后者返回指定日期中的月份的名称
(4)WEEK(d)、WEEKOFYEAD(d)前者计算日期d是一年中的第几周,后者计算某一天位于一年中的第几周
(5)DAYOFYEAR(d)、DAYOFMONTH(d)前者返回d是一年中的第几天,后者返回d是一月中的第几天
(6)EXTRACT(typeFROMdate)从日期中提取一部分,type可以是YEAR、YEAR_MONTH、DAY_HOUR、DAY_MICROSECOND、DAY_MINUTE、DAY_SECOND
(7)TimeStampDiff(间隔类型,前一个日期,后一个日期)
例如距离现在差了多少年:TimeStampDiff(year,前一个日期,now())
间隔类型有:second秒,minute分,hour时,day天,week周,month月,quarter季度,year年
(1)IF(expr,v1,v2)如果expr是TRUE则返回v1,否则返回v2
(2)IFNULL(v1,v2)函数如果v1的值不为NULL,则返回v1,否则返回v2。
(2)CASEexprWHENv1THENr1[WHENv2THENv2][ELSErn]END如果expr等于某个vn,则返回对应位置THEN后面的结果,如果与所有值都不想等,则返回ELSE后面的rn
(1)VERSION()查看MySQL版本号
(2)CONNECTION_ID()查看当前用户的连接数
(3)USER()、CURRENT_USER()、SYSTEM_USER()、SESSION_USER()查看当前被MySQL服务器验证的用户名和主机的组合,一般这几个函数的返回值是相同的
(4)CHARSET(str)查看字符串str使用的字符集
(5)COLLATION()查看字符串排列方式
(1)PASSWORD(str)从原明文密码str计算并返回加密后的字符串密码,注意这个函数的加密是单向的(不可逆),因此不应将它应用在个人的应用程序中而应该只在MySQL服务器的鉴定系统中使用
(2)MD5(str)为字符串算出一个MD5128比特校验和,改值以32位十六进制数字的二进制字符串形式返回
(3)ENCODE(str,pswd_str)使用pswd_str作为密码,加密str
(4)DECODE(crypt_str,pswd_str)使用pswd_str作为密码,解密加密字符串crypt_str,crypt_str是由ENCODE函数返回的字符串
(1)FORMAT(x,n)将数字x格式化,并以四舍五入的方式保留小数点后n位,结果以字符串形式返回
(2)CONV(N,from_base,to_base)不同进制数之间的转换,返回值为数值N的字符串表示,由from_base进制转换为to_base进制
(3)INET_ATON(expr)给出一个作为字符串的网络地址的点地址表示,返回一个代表该地址数值的整数,地址可以使4或8比特
(4)INET_NTOA(expr)给定一个数字网络地址(4或8比特),返回作为字符串的该地址的点地址表示
对一组数据进行运算,针对一组数据(多行记录)只返回一个结果,也称分组函数。
sum()avg()仅适用数值型
count()max()min()适用任何类型数据
selectavg(sal),max(sal),min(sal),sum(sal)fromemp;
selectmax(hiredate),min(hiredate)fromemp;
多行函数除了count(*)外,都跳过空值而处理非空值
selectcount(comm),sum(comm),avg(comm)fromemp;
可使用IF()函数强制多行函数处理空值
selectcount(IF(commisnull,0,comm)),sum(IF(commisnull,0,comm)),avg(IF(commisnull,0,comm))fromemp;
count(*)返回组中总记录数目;
count(exp)返回表达式exp值非空的记录数目;
count(distinct(exp))返回表达式exp值不重复的、非空的记录数目。
selectcount(*)fromemp;
selectcount(comm)fromemp;
selectcount(distinct(deptno))fromemp;
selectcount(IF(commISnull,0,comm))fromemp;
GROUPBY子句将表中数据分成若干小组
selectcolumn,group_function(column)
fromtable
[wherecondition]
[groupbygroup_by_expression]
[orderbycolumn];
selectdeptno,avg(sal)fromempgroupbydeptno;
selectdeptno,,count(*),avg(sal)fromempgroupbydeptno;
出现在SELECT列表中的字段,如果不是包含在多行函数中,那么该字段必须同时在GROUPBY子句中出现。
错误:selectename,deptno,sum(sal)fromempgroupbydeptno;
|--以上的SQL在MYSQL中是不错的,但是在oracle是错的,在开发中避免这样写
包含在GROUPBY子句中的字段则不必须出现在SELECT列表中。
如果没有GROUPBY子句,SELECT列表中不允许出现字段(单行函数)与多行函数混用的情况
selectempno,salfromemp;//合法
selectavg(sal)fromemp;//合法
selectempno,avg(sal)fromemp;//非法
不允许在WHERE子句中使用多行函数
selectdeptno,avg(sal)fromemp
whereavg(sal)>2000;//执行where时尚未执行groupby及其他
groupbydeptno;
对分组查询的结果进行过滤,要使用having从句。
having从句过滤分组后的结果,它只能出现在groupby从句之后,而where从句要出现在groupby从句之前。
where过滤行,having过滤分组。having支持所有where操作符。
[havinggroup_condition]
执行过程:from--where--groupby–having–select--orderby
列出平均工资大于8000元的部门ID
groupbydeptnohavingavg(sal)>8000orderbydeptno;
统计人数小于4的部门的平均工资。
统计各部门的最高工资,排除最高工资小于8000的部门。
显示部门编号大于101的部门的人数,要求人数大于3
笛卡尔集
等值连接
非等值连接
外连接
自连接
说明。SQL92是1992年提出的查询语法,向上兼容
SELECTtable1.column,table2.column
FROMtable1,table2
WHEREtable1.column1=table2.column2;
在WHERE子句中写入连接条件
当多个表中有重名列时,必须在列的名字前加上表名作为前缀
等值连接--Equijoin
非等值连接--Non-equijoin
左连接--LEFTJOIN
右连接--RIGHTJOIN
自连接--Selfjoin
select*fromdept;//4条记录select*fromemp;;//14条记录select*fromdept,emp;;//4*14=56条记录总结检索出的行的数目将是第一个表中的行数乘以第二个表中的行数检索出的列的数目将是第一个表中的列数加上第二个表中的列数应该保证所有联结都有where子句,不然数据库返回比想要的数据多得多的数据
select*fromdept,empwheredept.deptno=emp.deptno;
select*fromdeptd,empewhered.deptno=e.deptno;
selectd.deptno,dname,loc,empno,ename,jobfromdeptd,empewhered.deptno=e.deptno;
selectd.deptno,dname,loc,empno,ename,jobfromdeptd,empewhered.deptno=e.deptnoandd.deptno=10
selectd.deptno,dname,loc,empno,ename,jobfromdeptd,empewhered.deptno=e.deptnoandloc='武汉';
注意点
当被连接的多个表中存在同名字段时,须在该字段前加上"表名."前缀
可使用AND操作符增加查询条件;
使用表别名可以简化查询
使用表名(表别名)前缀可提高查询效率;
要求:查询员工的工资等级
selectempno,ename,job,sal,grade
fromempe,salgrades
wheree.sal
wheree.sal
特点:将一个表当两个表使用
使用举例:查询每个员工的工号、姓名、直接领导姓名
使用举例:查询每个员工的工号、姓名、经理姓名
selecte1.empno,e1.ename,e1.job,e2.ename
fromempe1,empe2
wheree1.mgr=e2.empno
orderbye1.empno;
SQL92的语法规则的缺点:
语句过滤条件和表连接的条件都放到了where子句中。
当条件过多时,联结条件多,过滤条件多时,就容易造成混淆
SQL99修正了整个缺点,把联结条件,过滤条件分开来,包括以下新的TABLEJOIN的句法结构:
交叉连接(Crossjoin)
自然连接(Naturaljoin)
使用Using子句建立连接
使用On子句建立连接
连接(Outerjoin)
n内连接INNERJOIN
n左连接LEFTJOIN
n右连接RIGHTJOIN
select字段列表
fromtable1
[crossjointable2]|//1:交叉连接
[naturaljointable2]|//2:自然连接
[jointable2using(字段名)]|//3:using子句
[jointable2on(table1.column_name=table2.column_name)]|//4:on子句
[(left|right|fullouter)jointable2
on(table1.column_name=table2.column_name)];//5:左/右/满外连接
Crossjoin产生了一个笛卡尔集,其效果等同于在两个表进行连接时未使用WHERE子句限定连接条件;
可以使用where条件从笛卡尔集中选出满足条件的记录。
selectdept.deptno,dname,ename
fromdeptcrossjoinemp;
等价于
fromdept,emp;
Naturaljoin基于两个表中的全部同名列建立连接
从两个表中选出同名列的值均对应相等的所有行
如果两个表中同名列的数据类型不同,则出错
不允许在参照列上使用表名或者别名作为前缀
自然连接的结果不保留重复的属性
举例:
selectempno,ename,sal,deptno,dname
fromempnaturaljoindept
如果不希望参照被连接表的所有同名列进行等值连接,自然连接将无法满足要求,可以在连接时使用USING子句来设置用于等值连接的列(参照列)名。
using子句引用的列在sql任何地方不能使用表名或者别名做前缀
selecte.ename,e.ename,e.sal,deptno,d.dname
fromempejoindeptd
using(deptno)
wheredeptno=101
自然连接的条件是基于表中所有同名列的等值连接
为了设置任意的连接条件或者指定连接的列,需要使用ON子句
连接条件与其它的查询条件分开书写
使用ON子句使查询语句更容易理解
1----selectename,dname
fromempinnerjoindeptonemp.deptno=dept.deptno
whereemp.deptno=30;
2---selectempno,ename,sal,emp.deptno,dname
fromempinnerjoindept
on(emp.deptno=dept.deptnoandsal>5000);
3---select*
fromdept,emp
wheredept.deptno=emp.deptnoandsal>5000;
第三种是SQL92里面的查询方法,和上面两种等价
两个表在连接过程中除返回满足连接条件的行以外,还返回左表中不满足条件的行,这种连接称为左外联接。
两个表在连接过程中除返回满足连接条件的行以外,还返回右表中不满足条件的行,这种连接称为右外联接。
两个表在连接过程中除返回满足连接条件的行以外,还返回两个表中不满足条件的所有行,这种连接称为满外联接。
内连接:在SQL99规范中,内连接只返回满足连接条件的数据。
左外连接
selectdeptno,dname,empno,enamefromdeptleftjoinempusing(deptno);
右外连接
selectdeptno,dname,empno,enamefromdeptrightjoinempusing(deptno);
满外连接
selectdeptno,dname,empno,enamefromdeptfulljoinempusing(deptno);
如何查得所有比“CLARK”工资高的员工的信息
select*fromemp
wheresal>(selectsalfromempwhereename='CLARK');
思考:查询工资高于平均工资的雇员名字和工资。
思考:查询和Lucy同一部门且比他工资低的雇员名字和工资。
select字段列表fromtable
where表达式operator(select字段列表fromtable);
子查询在主查询前执行一次
主查询使用子查询的结果
使用子查询注意事项
在查询是基于未知值时应考虑使用子查询
子查询必须包含在括号内
建议将子查询放在比较运算符的右侧,以增强可读性。
除非进行Top-N分析,否则不要在子查询中使用ORDERBY子句。
如果子查询返回单行结果,则为单行子查询,可以在主查询中对其使用相应的单行记录比较运算符
如果子查询返回多行结果,则为多行子查询,此时不允许对其使用单行记录比较运算符
单行子查询只返回一行记录
对单行子查询可使用单行记录比较运算符
<、>、=、>=、<=、<>
wheresal>(selectsalfromempwhereempno=101);
思考:查询工资最高的雇员名字和工资。
SELECTempno,ename,jobFROMemp
WHEREjob=(SELECTjobFROMempWHEREempno=7788)
ANDhiredate<(SELECThiredateFROMempWHEREempno=7788);
selectempno,ename,sal,hiredate
fromemp
wheresal>(selectsalfromempwhereename='SCOTT')orhiredate<(selecthiredatefromempwhereename='SCOTT')
多行子查询返回多行记录
对多行子查询只能使用多行记录比较运算符
ALL和子查询返回的所有值比较
ANY和子查询返回的任意一个值比较
IN等于列表中的任何一个
查询工资低于任何一个'CLERK'的工资的雇员信息。
查询工资比所有的'SALESMAN'都高的雇员的编号、名字和工资。
查询部门20中职务同部门10的雇员一样的雇员信息。
SELECTempno,ename,job,sal
FROMemp
WHEREsal
SELECTempno,ename,sal
WHEREsal>ALL(SELECTsalFROMempWHEREjob='SALESMAN');
WHEREjobIN(SELECTjobFROMempWHEREdeptno=10)
ANDdeptno=20;
分析:有哪些人的empno号在mgr这个字段中出现过,应首先查询mgr中有哪些号码,然后再看有哪些人的雇员号码在此出现
selectempno,enamefromemp
whereempnoin(
selectdistinctmgrfromemp
wheresal>=all(
selectsalfromempwheredeptno=20)
anddeptno=20
分析:首先将每个部门平均薪水求出来,然后把结果当成一张表,再用这张结果表和salgrade表做连接,以此求得薪水等级
先求出每个部门平均薪水的表t。
将t和salgrade进行关联查询就可以了。
select*from
salgrades,(selectdeptno,avg(sal)avg_sal
fromempgroupbydeptno)t
wheret.avg_salbetweens.losalands.hisal;
视图是从若干基本表和(或)其他视图构造出来的表。
在创建一个视图时,只是存放的视图的定义,也即是动态检索数据的查询语句,而并不存放视图对应的数据
在用户使用视图时才去求相对应的数据。所以视图被称作“虚表”
可以限制对数据的访问,可以给用户授予表的特定部分的访问权限而不是整个表的访问权限。
可以使复杂的查询变的简单。在编写查询后,可以方便地重用它而不必知道他的基本查询细节。
提供了对相同数据的不同显示
CREATEVIEWMYVIEW1
AS
SELECT*FROMEMP;
CREATE【ORREPLACE】VIEWMYVIEW2
SELECTEMPNO,ENAME,JOB,SAL,DEPTNO
FROMEMP
WHEREDEPTNO>=102
INSERTINTOMYVIEW1(EMPNO,ENAME,SAL)VALUES(9999,'AAAA',3000);
CREATEORREPLACEVIEWMYVIEW2
WHEREDEPTNO>=20
DROPVIEWMYVIEW4;
CREATEORREPLACEVIEWMYVIEW4
SELECTDEPTNO部门编号,DNAME部门名称,AVG(SAL)平均工资,MAX(SAL)最高工资,COUNT(*)人数
FROMEMPE
JOINDEPTDUSING(DEPTNO)
WHEREDEPTNOISNOTNULL
GROUPBYDEPTNO,DNAME
ORDERBYAVG(SAL)
CREATEORREPLACEVIEWMYVIEW5
SELECT*FROMMYVIEW4
WHERE部门编号=10
1.视图对应一个查询语句;视图是(从若干基本表和(或)其他视图构造出来的)表
2.视图进行查询,添加,修改,删除,其实就是对背后的表进行相应操作
3虚表在创建一个视图时,只是存放的视图的定义,也即是动态检索数据的查询语句,而并不存放视图对应的数据
4.视图的好处
1.安全可以只显示部分行部分列的数据;可以对添加的数据进行检查;可以设置为只读视图
2.操作简单
列出薪金高于公司平均薪金的所有员工。
3.只显示多个数据库表的部分列,部分行的视图
MySQL5.5及以后版本中的默认存储引擎,他的优点如下:
灾难恢复性好
支持事务
使用行级锁
支持外键关联
支持热备份
对于InnoDB引擎中的表,其数据的物理组织形式是簇表(ClusterTable),主键索引和数据是在一起的,数据按主键的顺序物理分布
实现了缓冲管理,不仅能缓冲索引也能缓冲数据,并且会自动创建散列索引以加快数据的获取
特性如下:
不支持事务
使用表级锁,并发性差
主机宕机后,MyISAM表易损坏,灾难恢复性不佳
可以配合锁,实现操作系统下的复制备份、迁移
只缓存索引,数据的缓存是利用操作系统缓冲区来实现的。可能引发过多的系统调用且效率不佳
数据紧凑存储,因此可获得更小的索引和更快的全表扫描性能
1)InnoDB支持事务,MyISAM不支持,这一点是非常之重要。事务是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以了。
2)MyISAM适合查询以及插入为主的应用,InnoDB适合频繁修改以及涉及到安全性较高的应用
3)InnoDB支持外键,MyISAM不支持
4)从MySQL5.5.5以后,InnoDB是默认引擎
5)InnoDB不支持FULLTEXT类型的索引
6)InnoDB中不保存表的行数,如selectcount(*)fromtable时,InnoDB需要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count(*)语句包含where条件时MyISAM也需要扫描整个表。
7)对于自增长的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中可以和其他字段一起建立联合索引。
8)清空整个表时,InnoDB是一行一行的删除,效率非常慢。MyISAM则会重建表。
9)InnoDB支持行锁(某些情况下还是锁整表,如updatetableseta=1whereuserlike'%lee%'
有人说MYISAM只能用于小型应用,其实这只是一种偏见。
如果数据量比较大,这是需要通过升级架构来解决,比如分表分库,读写分离,而不是单纯地依赖存储引擎。
现在一般都是选用InnoDB了,主要是MyISAM的全表锁,读写串行问题,并发效率锁表,效率低,MyISAM对于读写密集型应用一般是不会去选用的。
1.MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持。
2.MyISAM类型的表强调的是性能,其执行速度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持已经外部键等高级数据库功能。
存储引擎:数据在数据库中的组织方式(存储方式)。
例如:
createtabletest_table(
idint(11)
)ENGINE=INNODB,DEFAULTCHARSET=UTF8;
创建表的时候指定存储引擎为INNODB,这个存储引擎在MySQL中用的非常广泛,因为它支持外键、支持事务、支持行级锁。
查看MySQL支持的存储引擎命令如下:
SHOWENGINES;
除了上述操作之外,还可以在数据库安装盘的my.ini文件可以配置数据库表默认的存储引擎。
default-storage-engine=INNODB;
完成一个事情需要的一系列步骤(操作),这些操作要么同时成功,要么同时失败
场景:有两个账户分别为张三和李四,他们默认都有1000块钱的余额。在这两个账户之间进行转账。
--1创建tb_account表
createtabletb_account(
idint(11)auto_incrementcomment'主键',
uNamevarchar(20)comment'用户名',
accountvarchar(30)comment'银行卡账号',
pwdvarchar(30)comment'银行卡密码',
account_blancefloat(12,2)comment'账户余额',
primarykey(id)
insertintotb_accountvalues
(null,'宝强','110','123456',1000),
(null,'马蓉','120','123456',1),
(null,'宋喆','911','123456',0.5);
--转账:宝强转账1000万给马蓉
--操作1:娃娃
updatetb_accountsetaccount_blance=account_blance-1000whereaccount='110'andpwd='123456';
--操作2:打气筒
updatetb_accountsetaccount_blance=account_blance+1000whereaccount='120'andpwd='123456';
如何解决上面的问题呢?
使用事务来介入(管理)转账的操作
原子性:事务每一步都是不可再分
一致性:张三和李四账户一共2000块钱,不管转账多少次总金额不变
持久性:当一个事务执行成功(完毕),数据会持久化到磁盘的数据文件中。例如转账成功:张三余额变为500,李四余额变为1500.
隔离性:A事务和B事务同时操作一份数据,相互之间不影响。
1自动提交,MySQL默认为自动提交。
不需要写commit;就会自动将DML语句持久化提交
2手动提交,Oracle默认手动提交。
如何在MySQL中查看提交方式:
1开启事务
starttransaction;
2提交事务
commit;
3回滚事务
rollback;
注意:一旦使用starttransaction;开启事务那么自动提交将失效
如果所有操作都正常执行使用commit;提交事务
当发生异常情况回滚事务,数据(此时为tb_account表)通常回滚到开启事务之前的状态
隔离级别1最低,4最高。隔离级别越高就越安全,同时内存资源消耗也越大。隔离级别越高效率越低下。
工作中:1和4都不用,只会在2和3之间切换
MySQL默认的事务隔离级别为3,Oracle默认隔离界别默认为2
问题:不使用事务隔离级别会引发啥问题?使用事务隔离级别能够解决什么问题
一个事务(A)读取到另一个事务(B)没有提交的数据(破坏了隔离性).
例如:事务A开启事务做转账,DML语句执行成功但是没有commit;事务B在另一个窗口开启了,执行Select语句读取tb_account数据,读取到的结果是事务A没有提交的数据。
同一个事务中多次读取到的数据不一致(破坏了一致性,update和delete)
例如:事务A开启事务做转账,DML语句执行成功但是没有commit;;事务B在另一个窗口开启了,执行Select语句读取tb_account数据,读取的结果正确(1000,1000).
事务A里面提交了事务。然后事务B再次做Select操作查询结果也正确(500,1500)
问题:事务B在一次事务中对tb_account表做了两次select操作,两次操作查询的结果不一致。
事务A插入一条数据,能够使用select获取结果,此时事务B几乎同时插入了一条或者大量数据,此时事务A看不到事务B的更新(破坏了一致性,insert)。
查看事务隔离级别:
--设置事务的隔离级别读未提交setsessiontransactionisolationlevelREADUNCOMMITTED;
使用隔离级别为readuncommitted;完成转账,目的观察会出现脏读问题,如何解决?
--开启事务starttransaction;--执行事务的转账操作updatetb_accountsetaccount_blance=account_blance-500whereid=1;updatetb_accountsetaccount_blance=account_blance+500whereid=2;
18.7.4readuncommitted脏读问题解决
如何解决脏读问题?修改事务隔离级别:读已提交
setsessiontransactionisolationlevelreadcommitted;
进行转账操作
将事务隔离级别设置为"可重复读"repeatableread,解决不可重复读的问题。
setsessiontransactionisolationlevelrepeatableread;--还原为默认值updatetb_accountsetaccount_blance=1000;--开启事务starttransaction;--执行事务的转账操作updatetb_accountsetaccount_blance=account_blance-500whereid=1;updatetb_accountsetaccount_blance=account_blance+500whereid=2;
18.7.6幻读问题演示
能够解决所有的问题,但是效率低下,它类似Java的synchronized
Java使用synchronized用来锁对象。MySQL使用serializable锁表,事务A开启事务,做了DML操作,但是没有提交。此时事务B开启事务,执行select操作,没有查询到数据,因为此时tb_account表被事务A占用了(锁住了)。
setsessiontransactionisolationlevelserializable;
MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度。
打个比方,如果合理的设计且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车。
拿汉语字典的目录页(索引)打比方,我们可以按拼音、笔画、偏旁部首等排序的目录(索引)快速查找到需要的字。
索引分单列索引和组合索引。单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引。组合索引,即一个索引包含多个列。
创建索引时,你需要确保该索引是应用在SQL查询语句的条件(一般作为WHERE子句的条件)。
实际上,索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录。
上面都在说使用索引的好处,但过多的使用索引将会造成滥用。因此索引也会有它的缺点:虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。
建立索引会占用磁盘空间的索引文件。
--创建Person表
DROPTABLEIFEXISTSperson;
CREATETABLEperson(
PIDint(11)NOTNULLAUTO_INCREMENTCOMMENT'编号',
PNAMEvarchar(50)NOTNULLCOMMENT'姓名',
PSEXvarchar(10)NULLDEFAULTNULLCOMMENT'性别',
PAGEint(11)NOTNULLCOMMENT'年龄',
SALdecimal(7,2)NULLDEFAULTNULLCOMMENT'工资',
PRIMARYKEY(PID)
)ENGINE=InnoDBAUTO_INCREMENT=7001CHARACTERSET=utf8mb4COMMENT='人员表';
如何创建普通索引?
--语法--CREATEINDEX索引名称on表名称(列名称);
场景1:为person表的pname列建立普通索引
CREATEINDEXINDEX_PERSON_PNAMEONPERSON(PNAME);
如何查看SQL语句是否使用索引?可以使用EXPLAIN关键字来查看
EXPLAINselect*frompersonwherepname='Jack';
在MySQL中为表创建主键的同时默认也创建了一个索引
--下面的DQL语句使用了索引。EXPLAINselect*frompersonwhereid=1;
场景2:wherepnamelike模糊查询用到索引没有
--前后模糊中间精确不会使用索引EXPLAINselect*frompersonwherepnamelike'%e%';
--前面模糊后面精确也不会使用索引EXPLAINselect*frompersonwherepnamelike'%e';--只有前面精确后面模糊才会使用索引,工作中数据量大的表模糊查询尽量不要使用'%%',也不要使用'%a',他们都不支持索引EXPLAINselect*frompersonwherepnamelike'e%';
删除之前的pname的索引
ALTERTABLEPERSONDROPINDEXINDEX_PERSON_PNAME;
插入500W条数据
createprocedureinsert_person(inmax_numint(10))
begin
declareiintdefault0;
setautocommit=0;--把autocommit设置成0,这样可以只提交一次,否则。。。。。
repeat
seti=i+1;
insertintoperson(PID,PNAME,PSEX,PAGE,SAL)values(i,rand_string(5),IF(RAND()>0.5,'男','女'),FLOOR((RAND()*100)+10),FLOOR((RAND()*19000)+1000));
untili=max_num
endrepeat;
end;
--调用
callinsert_person(5000000);
--我查询耗时
不创建索引测试
select*frompersonwherePNAME="oQlJN";--耗时
查看索引文件
创建索引测试
创建耗时
查询测试
再次查看索引文件
问题1:为person表的pname创建索引消耗了9.472秒,为什么耗时这么长?
问题2:没有创建索引执行WHEREPNAME耗时3.892秒,创建索引执行耗时0.03秒,为什么这么快?
没有创建索引,进行全文检索(从person.ibd数据文件中检索所有的输入,然后在判断是否满足where条件),创建索引不进行全文检索,直接查询索引
问题3:创建索引之前数据文件person.ibd大小229376,创建索引之后大小319488,为什么会有这么大的变化?
创建索引就相当于创建目录,表中的数据越大,数据文件就越多。
问题4:INDEX_PERSON_PNAME索引的内部结构是啥?
类似TreeMap的B+Tree
如何创建一个复合索引,例如:我要对年龄和性别两个列创建复合索引
--为多个列创建复合索引
CREATEINDEXINDEX_PERSON_AGE_SEXONPERSON(PAGE,PSEX);
--没有创建索执行下面DQL语句引耗时1.427秒,创建索引执行耗时1毫秒
SELECT*FROMPERSONWHEREPAGE=22ANDPSEX='男';
--创建的复合索引,但是只对第二个索引列单独进行where条件,也会使用索引
--如果创建复合索引,经常使用的列放在前面
EXPLAIN
SELECT*FROMPERSONWHEREPSEX='男';
PERSON表的PSEX列只有2个有效值,为该列建立索引会提高查询效率吗?
CREATEINDEXINDEX_CUSTOMER_SEXONPERSON(PSEX);
--没有建立索引耗时2.7秒--建立索引耗时之后耗时12.5秒
1如果为某个列创建索引,那么就会在数据文件中创建一个类似TreeMap的文件。如果一个表的数据很多,那么索引会大量的占据数据文件的磁盘空间。
2不是所有的列都适合建立索引,如果某个列的有效数据很少不要建立索引。
3可以为表的多个列创建复合索引,经常使用的列放在前面。
4创建主键的同时默认也创建了一个索引。
唯一索引关键字:unique
之前学习了唯一约束,当我们创建了一个唯一约束的时候就创建了一个唯一索引,唯一约束就是唯一索引。
前置条件:先删除唯一约束
--删除唯一索引ALTERTABLEPERSONDROPINDEXUNIQUE_INDEX_PERSON_MOBILE;
--创建唯一索引语法--CREATEUNIQUEINDEX索引名称ON表名称(列名称);
--PERSON表有500W数据,创建唯一索引耗时:16.491秒CREATEUNIQUEINDEXUNIQUE_INDEX_PERSON_MOBILEONPERSON(MOBILE);
--没有创建唯一索引耗时1.8秒,创建唯一索引查询耗时2毫秒EXPLAINselect*fromPERSONwheremobile='15004613234';
小结:工作中如果表中的某个列数据全部唯一,可以考虑创建唯一索引
ALTERTABLEPERSONDROPINDEXINDEX_PERSON_AGE_SEX;
CREATEINDEXINDEX_PERSON_AGE_SEXONPERSON(PNAME,PSEX);
--没有创建索执行下面DQL语句引耗时11.427秒,创建索引执行耗时1毫秒
SELECT*FROMPERSONWHEREPNAME="oQlJN"ANDPSEX='男';
EXPLAINSELECT*FROMPERSONWHEREPSEX='男'--ANDPNAME="oQlJN";
--创建的复合索引,但是只对第二个索引列单独进行where条件,不会使用索引
--如果创建复合索引,经常使用的列放在前面,并且查询时一定要带上第一列的条件
【概述】
通过数值比较、范围过滤等就可以完成绝大多数我们需要的查询,但是,如果希望通过关键字的匹配来进行查询过滤,那么就需要基于相似度的查询,而不是原来的精确数值比较。全文索引就是为这种场景设计的。
你可能会说,用like+%就可以实现模糊匹配了,为什么还要全文索引?like+%在文本比较少时是合适的,但是对于大量的文本数据检索,是不可想象的。全文索引在大量的数据面前,能比like+%快N倍,速度不是一个数量级,但是全文索引可能存在精度问题。
你可能没有注意过全文索引,不过至少应该对一种全文索引技术比较熟悉:各种的搜索引擎。虽然搜索引擎的索引对象是超大量的数据,并且通常其背后都不是关系型数据库,不过全文索引的基本原理是一样的。
【版本支持】
开始之前,先说一下全文索引的版本、存储引擎、数据类型的支持情况
MySQL5.6以前的版本,只有MyISAM存储引擎支持全文索引;
MySQL5.6及以后的版本,MyISAM和InnoDB存储引擎均支持全文索引;
只有字段的数据类型为char、varchar、text及其系列才可以建全文索引。
【使用】
工作中用的不多不是重点。
关键字:fulltext
--语法:
--CREATEFULLTEXTINDEX索引名称ON表名称(列名称);
--示例:
--为EMP表的ENAME列创建全文索引CREATEFULLTEXTINDEXFULLTEXT_EMP_ENAMEONEMP(ENAME);
--测试
EXPLAINselect*fromEMPWHEREMATCH(ENAME)AGAINST('JACK')
坑点:只有建立了全文索引后,才能使用MATCH(ENAME)AGAINST('JACK')的语法
--语法--ALTERTABLE表名称DROPINDEX索引名称;--示例ALTERTABLEPERSONDROPINDEXINDEX_PERSON_PNAME;
数据库设计关系整个系统的架构,关系到后续的开发效率和运行效率
数据库的设计主要包含了设计表结构和表之间的联系
结构合理
冗余较小
尽量避免插入删除修改异常
遵循一定的规则
在关系型数据库中这种规则就称为范式
范式是符合某一种设计要求的总结。
要想设计一个结构合理的关系型数据库,必须满足一定的范式。
第一范式
第二范式
第三范式
BoyceCodd范式=NCNF
由Boyce和Codd提出的,
比3NF又进了一步
通常认为是修正的第三范式.
第四范式
第五范式
各个范式是依次嵌套包含的
范式越高,设计质量越高,在现实设计中也越难实现
一般数据库设计,只要达到第三范式,即可避免异常的出现
最基本的范式
数据库表每一列都是不可分割的基本数据项,同一列中不能有多个值
简单说就是要确保每列保持原子性
第一范式的合理遵循需要根据系统的实际需求来定
用户表(用户名,家庭地址)
|
用户表(用户名,省,城市,详细地址)
系(系名称,系主任,系高级职称人数)
系(系名称,系主任,系教授人数,系副教授人数)
即在一个数据库表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。
学号和课程编号作为联合主键
课程名称只依赖于课程编号,而和学号没有关系
分析以上的设计发现数据冗余
如何解决呢?
提取出学生表
提取成课程表
提取选课表,存放选课记录
1,学生表
2,课程表
3,选课表
属性不依赖于其他非主属性。
分析以上的表,发现有问题存在班级名称和班级信息出现了数据冗余
如何解决
学生表
班级表
2,班级表
20.5范式的优缺点
性能降低
多表查询比单表查询速度慢
在实际设计中,要整体遵循范式理论。
如果在某些特定的情况下还死死遵循范式也是不可取的,因为可能降低数据库的效率,此时可以适当增加冗余而提高性能。
比如经常购物车条目的中除了条目编号,商品编号,商品数量外,可以增加经常使用的商品名称,商品价格等