一.数据库的基本定义:
数据库(DB):是一个按数据结构来存储和管理数据的计算机软件系统,其实在磁盘中数据库就是一堆文件.
二.数据库的发展历程:
①:网状型数据库:处理以记录类型为结点的网状数据模型的数据库
②:层次型数据库:用树形结构表示实体及其联系的模型称为层次模型
③:关系型数据库:在关系模型中,数据的逻辑结构为满足一定条件的二维表,表具有固定的列数和任意的行数,在数学上称为“关系”;
常见的关系型数据库:
1,Oracle:大型企业数据库,支持大数据量,速度较快,安全性非常好,提供完善的存储过程支持;新的版本提供了众多新功能;
2,DB2(IBM):大型企业数据库,支持数据量,速度较快,安全性较好;
3,SQL Server(MS):中小型企业数据库,支持数据量,速度较快,安全性较好,操作便捷;
4,MySQL(Oracle):性能不错,使用方便,体积小,易扩展;是目前使用最广的关系型数据库;
5,PostgreSQL(postgres):免费,实现更完整,功能更强大,更稳定;
④:面向对象的数据库:把面向对象的方法和数据库技术结合起来可以使数据库系统的分析、设计最大程度地与人们对客观世界的认识相一致;
NOSQL:Not Only SQL 泛指非关系型的数据库;
(1)NoSQL分类:
1,键值(Key-Value)存储数据库:这一类数据库主要会使用到一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据;
2,列存储数据库:这部分数据库通常是用来应对分布式存储的海量数据。键仍然存在,但是它们的特点是指向了多个列;
3,文档型数据库:同第一种键值存储相类似。该类型的数据模型是版本化的文档,半结构化的文档以特定的格式存储;
4,图形(Graph)数据库:使用图模型存储;
(2)常见NoSQL数据库:
1,MongoDB:文档型数据库;功能丰富;查询语言强大;并发效率不高,查询效率不算高;
2,Hbase:高可靠性、高性能、面向列、可伸缩的分布式存储系统;
3,Redis:key-value存储系统(主要面对内存数据库,也可持久化);安全性;主从复制;运行异常快,支持事务,丰富类型的key类型;
关系型数据库的三范式(减少数据库中数据冗余的过程)(很重要);
第一范式(1NF)是对关系模式的基本要求,不满足第一范式(1NF)的数据库就不是关系数据库,是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值(一个列中只能存储一个值);
第二范式(2NF)要求数据库表中的每个实例或行必须可以被惟一地区分(主键)。可能被打破
第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主键列的信息。可能被打破
三.MySQL安装文件夹结构和文件分析:
(1).MySQL安装文件夹结构:
默认目录在:C:\Program Files (x86)\MySQL\MySQL Server 5.5.
bin:MySQL相关二进制文件存储目录,存放了很多MySQL提供的系统工具;如:mysql;mysqld;mysqldump;
data:MySQL默认的数据存放目录,一般不会存放在这里;
win7在C:\ProgramData\MySQL\MySQL Server 5.5路径.
include:包含一些头文件,提供其他程序连接mysql的API接口;
lib:MySQL依赖的库;
share:存放几个初始表文件和每种语言的errmsg.sys错误日志;
mysql-test:mysql测试套件;
scripts:这个Perl脚本的作用是创建mysql最初需要的几个系统表,它的建表sql文件在share目录中;
sql-bench:对数据库进行压力测试的工具;
my.ini:MySQL的基础配置文件;
(2).MySQL的配置文件:
1,MySQL服务的属性:
“C:\Program Files (x86)\MySQL\MySQL Server 5.5\bin\mysqld” --defaults-file=“C:\Program Files (x86)\MySQL\MySQL Server 5.5\my.ini” MySQL
2,my.ini是MySQL服务的配置文件,MySQL的启动可以通过配置文件,也可以直接通过命令行参数;
1,配置项中的[client]表示以下的配置是针对于客户端做的配置.[mysqld]表示以下配置是针对服务端的配置;
2,一些比较常见的配置项:
1,port:启动端口;
2,basedir:MySQL的主目录;
3,datadir:MySQL数据文件存放的位置,默认就是在MySQL主目录的data文件夹下;一般会进行调整;
4,character-set-server:默认的字符编码;
5,default-storage-engine:默认的存储引擎;
6,max_connections:最大允许连接数;
3,MySQL的启动也可以添加启动参数,可使用的参数同my.ini相同
mysqld --defaults-file="…/my.ini" --port 3307
(3).MySQL数据文件简介:
默认的路径:C:\ProgramData\MySQL\MySQL Server 5.5\data.
1,数据文件存放格式,注意MyISAM和InnoDB的文件不同格式;
2,常见的文件类型:
.frm:与表相关的元数据(meta)信息,比如表结构定义;所有表都需要这个文件;
.MYD(MyISAM Data):MyISAM 存储引擎专用,存放MyISAM 表的数据;
.MYI(MyISAM Index):MyISAM 存储引擎专用,主要存放MyISAM 表的索引相关信息;
.ibd和ibdata:存放InnoDB数据;
ibdata1:共享存储空间,相当于所有InnoDB的数据都存放在公共的这个ibdata1文件中;
.ibd:独享存储空间,相当于每一个表的数据还是保存在自己独立的文件中,可以通过innodb_file_per_table=1参数来设置,但是独立表空间和共享表空间不能同时存在;
db.opt:该数据库的默认配置;
四.MySQL数据库中的权限
(1)权限组成结构:
用 户:权限的主体,一个用户拥有自己的用户名和密码,并拥有能够操作的权限;
操作权限:限制一个用户能够做什么事情,在MySQL中,操作权限可以配置到全局/数据库/表/列上;
使用Navicat工具操作:
(2)权限分布:针对不同的对象能够设置哪些权限;
全局:
数据库:
表权限:‘Select’, ‘Insert’, ‘Update’, ‘Delete’, ‘Create’, ‘Drop’, ‘Grant’, ‘References’, ‘Index’, ‘Alter’
列权限:‘Select’, ‘Insert’, ‘Update’, ‘References’
过程权限:‘Execute’, ‘Alter Routine’, ‘Grant’
*references:操作外键
MySQL中权限的设置也是存储在数据库中的:
mysql.user:用户表,包括用户名,密码,主机,全局权限,性能限制;
mysql.db:数据库权限设置;
mysql.table_priv:表权限设置;
mysql.column_priv:列权限设置;
(3).权限相关命令:
先通过图形化工具来创建账号为will,密码为admin的用户.
MySQL中用于赋权限的命令是GRANT,语法为:
完整语法:
GRANT 权限 (columns)
ON 数据库对象(哪个数据库中的哪一张表)
TO 用户 IDENTIFIED BY “密码”
WITH GRANT OPTION
三种具体的语法:
给一个存在用户赋予权限:
语法1:GRANT 权限 (columns) ON 数据库对象 TO 用户:
赋予lucy账户:SELECT和INSERT权限.
GRANT select,insert ON . TO lucy@localhost;
创建用户,设置密码,赋予权限:
语法2:GRANT 权限 (columns) ON 数据库对象 TO 用户 IDENTIFIED BY “密码”
创建lucy用户:
GRANT ALL ON . TO lucy@localhost IDENTIFIED BY ‘1234’;
创建用户,设置密码,赋予权限,并且该用户可以继续授权给其他用户:
语法3:GRANT 权限 (columns) ON 数据库对象 TO 用户 IDENTIFIED BY “密码” WITH GRANT OPTION
1,ALL PRIVILEGES即代表所有权限,简写ALL;
2,.:所有数据库对象。普通格式为dbName.tableName,比如test.*;
3,lucy@‘localhost’:admin用户,只能在本机访问。
1,用户账户包括user@host;
2,host为
%:从任意地址访问;
10.250.7.%:只能从指定网段访问;
192.168.1.2:只能从某个IP访问;
4,WITH GRANT OPTION:是否允许用户继续授权(注意,这个用户能够授权只能是该用户的权限子集)
5,注意,在授权操作之后,使用flush privileges命令刷新权限;
查看当前用户的权限:
show grants
查看特定用户的权限:
show grants for root@localhost
使用REVOKE命令回收对用户的授权:
REVOKE 权限 ON 数据库对象 FROM 用户;
如:赋予lucy用户所有的权限之后,再回收用户will所有的权限.
赋予权限:GRANT all ON . TO lucy@’%’;
回收权限:REVOKE all ON . FROM lucy@’%’;
注意:使用REVOKE撤销全部权限,操作者必须拥有MySQLl数据库的全局CREATE USER权限或UPDATE权限;
删除用户:DROP USER 用户名称
注意,user必须包括user@host;
如:DROP USER lucy@’%’;
五.sql的分类:
SQL的分类,包含6个部分(数据表准备和分析):
一:数据查询语言(DQL:Data Query Language):
其语句,也称为“数据检索语句”,用以从表中获得数据,确定数据怎样在应用程序给出。保留字SELECT是DQL(也是所有SQL)用得最多的动词,其他DQL常用的保留字有WHERE,ORDER BY,GROUP BY和HAVING。这些DQL保留字常与其他类型的SQL语句一起使用。
二:数据操作语言(DML:Data Manipulation Language):
其语句包括动词INSERT,UPDATE和DELETE。它们分别用于添加,修改和删除表中的行, Insert / Update / Delete。也称为动作查询语言。
三:事务处理语言(TCL):
它的语句能确保被DML语句影响的表的所有行及时得以更新。TPL语句包括BEGIN TRANSACTION,COMMIT和ROLLBACK。
四:数据库控制语言(DCL):
它的语句通过GRANT或REVOKE获得许可,确定单个用户和用户组对数据库对象的访问。某些RDBMS可用GRANT或REVOKE控制对表单个列的访问。
五:数据定义语言(DDL):
其语句包括动词CREATE和DROP。在数据库中创建新表或删除表(CREAT TABLE 或 DROP TABLE);为表加入索引等。DDL包括许多与人数据库目录中获得数据有关的保留字。它也是动作查询的一部分。
六:指针控制语言(CCL):
它的语句,像DECLARE CURSOR,FETCH INTO和UPDATE WHERE CURRENT用于对一个或多个表单独行的操作。
六.单表查询:
基本语法:
全列/投影查询: 查询表中所有列,查询表中部分的列.
select 列名 from 表名
消除重复:
distinct关键字可以用于一列,也可以用于多列
SELECT distinct job,deptno FROM emp;
只有当job和deptno相同,才认为是重复的数据.
算术运算符:
对NUMBER型数据可以使用算数操作符创建表达式(+ - * /)
对DATE型数据可以使用算数操作符创建表达式(+ -)
空值,表示NULL,不表示空字符串:
1、空值是指不可用、未分配的值,也就是没有值。
2、空值不等于零或空格
3、任意类型都可以支持空值,也就是说任何类型的字段都可以允许空值作为值的存在
**注意:
- 包括空值的任何算术表达式都等于空
函数:IFNULL(expr1,expr2)
如果expr1不是NULL,IFNULL()返回expr1,否则它返回expr2**
限定查询,使用WHERE做过滤查询:
注意:
1、字符串和日期要用单引号扩起来
2、数字类型直接书写
3、字符串是大小写不敏感的,日期值是格式敏感的
4、字符串敏感,需要添加binary关键字
比较运算符:
常用算术比较运算符:
1,=,!=,<>,>,>=,<,<=:等于和不等于
2,BETWEEN … AND …:在两值之间 (包含开始和结尾);
3,IN(list):匹配列出的值;
4,LIKE :匹配字符串模式; _ %
5,IS NULL:是否为空;
使用LIKE运算符
LIKE运算符必须使用通配符才有意义:
匹配单个字符:_; -> 1个
匹配任意多个字符:%; -> 0个、1个、多个
逻辑运算符:
AND:如果组合的条件都是true,返回true.
OR:如果组合的条件 之一是true,返回true.
NOT:如果下面的条件是false,返回true.
优先级规则: 比较运算符 > NOT > AND > OR
使用ORDER BY 子句将记录排序
ASC : 升序,缺省
DESC: 降序
ORDER BY 子句出现在SELECT语句的最后
ORDER BY 可以使用别名
#1,查询所有员工信息
SELECT * FROM emp
#2,查询每个员工的编号、姓名、职位
SELECT empno,ename,job FROM emp
#3查询所有部门信息
SELECT * FROM dept
#1,查询所有有员工的部门编号
SELECT DISTINCT deptno FROM emp
#2,查询哪些部门和职位有员工
SELECT DISTINCT deptno,job FROM emp
#1,查询所有员工的年薪
SELECT ename,sal*12 FROM emp
#2,查询所有员工的年薪(使用别名)
SELECT ename,sal*12 nx FROM emp
#3,查询每月都有500元的餐补和200元交通补助并且年底多发一个月工资的年薪
SELECT ename,((sal+500+200)*12+sal) nx FROM emp
#4,演示date类型数据的运算:查询员工的雇佣日期加上10
SELECT ename,DATE_ADD(hiredate,INTERVAL 10 DAY) FROM emp
#1,查询有奖金的员工信息
SELECT * FROM emp WHERE comm IS NOT NULL
#2,查询公司的老板
SELECT * FROM emp WHERE MGR IS NULL
#1,查询名字叫SCOTT的员工所从事的工作
SELECT job FROM emp WHERE ename='SCOTT'
#2,查询1981年入职的员工信息.
select * from emp where date_format(HIREDATE,'%Y')='1981'
#1,查询1981年之后入职的员工信息
select * from emp where date_format(HIREDATE,'%Y')>'1981'
#2,查询年薪小于3W的员工
SELECT ename,sal*12 nx FROM emp WHERE (sal * 12)<30000
#3,查询所有不是销售人员的员工信息
SELECT * FROM emp WHERE job != 'SALESMAN'
#1,查询工资在2000-3000之间的员工信息
SELECT * FROM emp WHERE sal BETWEEN 2000 AND 3000
#2,查询工资不在2000-3000之间的员工信息
SELECT * FROM emp WHERE !(sal BETWEEN 2000 AND 3000)
#1,查询工资为800或1600或3000的员工
SELECT * FROM emp WHERE sal in(800,1600,3000)
#2,查询工资不为800或1600或3000的员工
SELECT * FROM emp WHERE !(sal in(800,1600,3000))
#1.查询出所有雇员姓名是以A开头的全部雇员信息。
SELECT * FROM emp WHERE ename like 'A%'
#2.查询出雇员姓名第二个字母是M的全部雇员信息。
SELECT * FROM emp WHERE ename like '_M%'
#3.查询出雇员姓名任意位置上包含字母A的全部雇员信息。
SELECT * FROM emp WHERE ename like '%A%'
#1,查询姓名中有e或者a的员工姓名
SELECT * FROM emp WHERE ename like '%A%' or ename like '%E%'
#3,查询出职位是办事员(CLERK)或者是销售人员(SALESMAN)的全部信息,并且工资在1000以上.
SELECT * FROM emp WHERE job in ('CLERK','SALESMAN') AND sal >1000
#1,查询所有员工信息,按照工资排序 order by sal asc
SELECT * FROM emp order by sal asc
#2,查询所有员工信息,按照年薪降序排序;
SELECT *,(sal+IFNULL(comm,0))*12 nx FROM emp ORDER BY nx DESC
#3,查询所有员工信息,按照部门和年薪降序排序;
SELECT *,(sal+IFNULL(comm,0))*12 nx FROM emp ORDER BY deptno,nx DESC
七.MySQL查询函数:
(1)统计函数:
COUNT():查询表中的数据记录;
AVG():求出平均值;
SUM():求和;
MAX():求出最大值;
MIN():求出最小值;
分组函数在计算时省略列中的空值
不能在where语句中使用分组函数
#查询所有员工每个月支付的平均工资及总工资 avg(sal), sum(sal)
SELECT AVG(sal),SUM(sal) FROM emp
#查询月薪在2000以上的员工总人数.
select count(*) from emp where sal > 2000
#查询员工最高工资和最低工资差距
SELECT (MAX(sal)-MIN(sal)) FROM emp
(2)字符串函数:
用户处理单行的字符数据,比如大小写转换,字符串截取,拼接等;
①LOWER(str):返回字符串str变为小写字母的字符
UPPER(str):返回字符串str变为大写字母的字符
#把Hello转为全小写和全大写
SELECT LOWER('Hello') from DUAL
SELECT UPPER('Hello') from DUAL
②CONCAT(str1,str2,…):
1,返回结果为连接参数产生的字符串。
2,如有任何一个参数为NULL ,则返回值为 NULL
3,允许有一个或多个参数
③.CHAR_LENGTH和LENGTH
CHAR_LENGTH:字符串长度;
LENGTH:字符串长度(单位为字节);
SELECT CHAR_LENGTH('龙哥17岁') FROM DUAL 5
SELECT LENGTH('龙哥17岁') FROM DUAL 11
④LPAD/RPAD
LPAD(str,len,padstr)
1,返回字符串str, 其左边由字符串padstr 填补到len 字符长度。
2,假如str 的长度大于len, 则返回值被缩短至len 字符。
RPAD(str,len,padstr)
1,返回字符串str, 其右边被字符串padstr填补至len 字符长度。
2,假如字符串str 的长度大于 len,则返回值被缩短到与len 字符相同长度。
SELECT LPAD('ABC',7,'X'),LPAD('ABC',5,'X'),LPAD('ABC',3,'X'),LPAD('ABC',1,'X') FROM DUAL;
⑤.LTRIM/RTRIM/TRIM
LTRIM(str):左边空格被trim掉;
RTRIM(str):右边空格被trim掉;
TRIM(str)=LTRIM+RTRIM
TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str):高级用法,从str中按照指定方式截取remstr;
TRIM(remstr FROM] str):相当于TRIM(BOTH remstr FROM str);
SELECT LTRIM(’ A B C ‘),RTRIM(’ A B C ‘),TRIM(’ A B C ') FROM DUAL;
⑥.REPLACE(str,from_str,to_str):
1,在str中把from_str全部替换为to_str;
2,大小写敏感;
SELECT REPLACE('ABCDaBCABA','A','X') FROM DUAL;
⑦.SUBSTRING(str,pos):
SUBSTRING(str,pos):
从字符串str返回一个子字符串,起始于位置pos
SUBSTRING(str,pos,len):
从字符串str返回一个长度同len字符相同的子字符串,起始于位置pos
如果pos是负数,从字符串尾部开始计算;
SELECT SUBSTRING('ABCDEF',3),SUBSTRING('ABCDEF',3,3) FROM DUAL;
(3)数字函数:
用户处理单行的数字数据,用于基本的计算,但是注意一点,一般不要在查询条件的WHERE语句中使用列的计算来做条件过滤;
在POS系统中,都会有这样一个功能,在系统中可以设置找零的方式,比如常见的有下面几种:
1,角以下四舍五入
2,元以下四舍五入
3,不管分
4,不管角
5,不管元
①ABS/MOD
ABS(x):返回一个数字的绝对值;
MOD(N,M):返回N 被 M除后的余数;
SELECT ABS(-17),ABS(17),MOD(10,5),MOD(10,3) FROM DUAL;
②CEIL/FLOOR/ROUND/TRUNCATE
CEIL(x):返回不小于X 的最小整数值;
FLOOR(x):返回不大于X的最大整数值;
SELECT CEIL(0.5),CEIL(-1.5),CEIL(1.5) FROM DUAL;
SELECT FLOOR(0.5),FLOOR(-1.5),FLOOR(1.5) FROM DUAL;
ROUND(X) /ROUND(X,D):
1,返回参数X, 其值接近于最近似的整数。
2,在有两个参数的情况下,返回X ,其值保留到小数点后D位,而第D位的保留方式为四舍五入。
3,若要接保留X值小数点左边的D 位,可将 D 设为负值。
TRUNCATE(X,D)
1,返回被舍去至小数点后D位的数字X。
2,若D 的值为 0, 则结果不带有小数点或不带有小数部分。可以将D设为负数,若要截去(归零) X小数点左起第D位开始后面所有低位的值.
(4).日期函数:
日期函数是非常有用的函数,MYSQL提供了很多用于处理时间计算的函数;
①NOW():获取当前系统时间
②.DATE_ADD/DATE_SUB
DATE_ADD(date,INTERVAL expr type)/DATE_SUB(date,INTERVAL expr type)向日期添加指定的时间间隔
1,执行日期运算;
2,date 是一个 DATETIME 或DATE值,用来指定起始时间;
3,expr 是一个字符串表达式,用来指定从起始日期添加或减去的时间间隔值;
4,type 为关键词,它指示了表达式被解释的方式
SELECT DATE_ADD(‘2015-01-01’,INTERVAL 100 DAY) FROM DUAL;
SELECT DATE_ADD(NOW(),INTERVAL 100 DAY) FROM DUAL;
使用方法参考MYSQL 5.5中文参考手册547页;
③.CURRENT_DATE/CURRENT_TIME(当前日期,当前时间)
SELECT CURRENT_DATE(),CURRENT_TIME() FROM DUAL;
④.DATEDIFF:
DATEDIFF(expr,expr2):返回起始时间expr和结束时间expr2之间的天数
SELECT DATEDIFF(‘2015-12-31’,‘2015-01-01’) FROM DUAL;
⑤.获取日期时间中某个段
DAY(date):获取日期中的天数(DAYOFMONTH)
HOUR(time)返回time 对应的小时数。对于日时值的返回值范围是从 0 到 23
MINUTE(time)返回 time 对应的分钟数,范围是从 0 到 59
MONTH(date)\返回date 对应的月份,范围时从 1 到 12
YEAR(date)返回date 对应的年份,范围是从1000到9999
LAST_DAY(date)获取一个日期或日期时间值,返回该月最后一天对应的值
⑥.UNIX_TIMESTAMP/FROM_UNIXTIME
UNIX_TIMESTAMP(date):它会将参数值以’1970-01-01 00:00:00’ GMT后的秒数的形式返回
FROM_UNIXTIME(unix_timestamp) , FROM_UNIXTIME(unix_timestamp,format):返回’YYYY-MM-DD HH:MM:SS’或指定format的日期
SELECT UNIX_TIMESTAMP(NOW()) FROM DUAL;
SELECT FROM_UNIXTIME(UNIX_TIMESTAMP(NOW())) FROM DUAL;
(5).转换函数
①数字和字符串(FORMAT)
FORMAT(X,D)
将数字X 的格式写为’#,###,###.##’,以四舍五入的方式保留小数点后D 位, 并将结果以字符串的形式返
回。若D 为 0, 则返回结果不带有小数点,或不含小数部分。
SELECT FORMAT(123456.789,2) FROM DUAL;
②.日期和字符串
1.DATE_FORMAT:
格式:DATE_FORMAT(date,format):把日期转换为字符串.
SELECT DATE_FORMAT(NOW(),’%Y-%m-%d’) FROM DUAL;
format参考549页
2.STR_TO_DATE
STR_TO_DATE(str,format):把字符串转换为日期。
SELECT STR_TO_DATE(‘2015/11/29’,’%Y/%m/%d’) FROM DUAL;
单行函数可被嵌入到任何层
在嵌套的时候,最内层的函数最先被执行,执行的结果被传递到它的上层函数,作为参数使用,然后依次从内向外执行,直到所有的函数执行完。
八.分组查询:
分组语句:
(1)GROUP BY语法:
SELECT [DISTINCT] *|分组字段1 [别名] [,分组字段2 [别名] ,…] | 统计函数
FROM 表名称 [别名], [表名称 [别名] ,…]
[WHERE 条件(s)]
[GROUP BY 分组字段1 [,分组字段2 ,…]]
[ORDER BY 排序字段 ASC | DESC [,排序字段 ASC | DESC]];
使用GROUP BY子句将表分成小组
组函数忽略空值,可以使用ifnull
结果集隐式按升序排列,如果需要改变排序方式可以使用order by 子句
(2).使用GROUP BY
1,出现在SELECT列表中的字段,如果出现的位置不是在组函数中,那么必须出现在GROUP BY子句中
2,在GROUP BY 子句中出现的字段,可以不出现在SELECT列表中
3,如果没有GROUP BY子句SELECT列表中的任何列或表达式不能使用统计函数:
分组函数单独使用:
SELECT COUNT(empno) FROM emp;
如果现在要进行分组的话,则SELECT子句之后,只能出现分组的字段和统计函数,其他的字段不能出现:
组函数的错误用法:
1,不能在 WHERE 子句中限制组.
2,限制组必须使用 HAVING 子句.
3,不能在 WHERE 子句中使用组函数.
(3).使用HAVING子句对分组的结果进行限制:
SELECT [DISTINCT] *|分组字段1 [别名] [,分组字段2 [别名] ,…] | 统计函数
FROM 表名称 [别名], [表名称 [别名] ,…]
[WHERE 条件(s)]
[GROUP BY 分组字段1 [,分组字段2 ,…]]
[HAVING 分组后的过滤条件(可以使用统计函数)]
[ORDER BY 排序字段 ASC | DESC [,排序字段 ASC | DESC]];
(4).对多列进行分组
在group by 子句中,可以按单列进行分组,也可以在多列上进行分组,多列分组就是按照多个字段的组合进行分组,最终的结果也会按照分组字段进行排序显示。
(5).分组函数执行流程
查询在80,81,82,83年各进公司多少人
SELECT COUNT(empno), YEAR(hiredate) FROM emp WHERE YEAR(hiredate) IN (‘1980’,‘1981’,‘1982’,‘1983’) GROUP BY YEAR(hiredate)
在整个语句执行的过程中,最先执行的是WHERE子句,在对表数据进行过滤后,符合条件的数据通过Group by进行分组,分组数据通过Having子句进行组函数过滤,最终的结果通过order by子句进行排序,排序的结果被返回给用户。
注意点:WHERE和HAVING的区别
WHERE:是在执行GROUP BY操作之前进行的过滤,表示从全部数据之中筛选出部分的数据,在WHERE之中不能使用统计函数;
HAVING:是在GROUP BY分组之后的再次过滤,可以在HAVING子句中使用统计函数;
1,按照职位分组,求出每个职位的最高和最低工资
SELECT MAX(sal),MIN(sal),job FROM emp GROUP BY job
2,查询出每一个部门员工的平均工资
SELECT AVG(sal),deptno FROM emp GROUP BY deptno
3,查询平均工资高于2000的部门和其平均工资
SELECT AVG(sal),deptno FROM emp GROUP BY deptno HAVING AVG(sal) >= 2000
4,查询各个部门和岗位的平均工资
SELECT AVG(sal),deptno,job FROM emp GROUP BY deptno,job
5,查询各个管理人员下员工的平均工资,其中最低工资不能低于1300,不计算老板
SELECT AVG(sal),mgr FROM emp WHERE mgr IS NOT NULL GROUP BY mgr HAVING AVG(sal) >= 1300
6,查询在80,81,82,83年各进公司多少人
SELECT COUNT(empno), YEAR(hiredate) FROM emp WHERE YEAR(hiredate) IN ('1980','1981','1982','1983') GROUP BY YEAR(hiredate)
九.多表查询
笛卡尔积: 多表查询会产生笛卡尔积:
假设集合A={a,b},集合B={0,1,2},则两个集合的笛卡尔积为{(a,0),(a,1),(a,2),(b,0),(b,1),(b,2)}。
实际运行环境下,应避免使用全笛卡尔集。
select * from emp,dept
解决方案: 在WHERE加入有效的连接条件---->等值连接
注意:
连接 n张表,至少需要 n-1个连接条件。
为什么需要多表查询:
需要查询的数据分散在多张表中,只有联合多张表才能查询出期望的数据.
比如: emp表存储了员工的信息, dept表存储了部门的信息,需求:查询出员工的编号,姓名,以及对应部门的名称.
多表查询:
内连接:
隐式内连接:
显示内连接:
外连接查询:
左外连接:
右外连接查询:
全外连接查询:
子连接查询:
把一张表看成是多张表,自己和自己做联合查询,一般用于树状结构中.
(1).内连接查询:
1.隐式连接
SELECT [DISTINCT] * | 字段 [别名] [,字段 [别名] ,…]
FROM 表名称 [别名], [表名称 [别名] ,…]
[WHERE 条件(S)]
[ORDER BY 排序字段 [ASC|DESC] [,排序字段 [ASC|DESC] ,…]];
隐式内连接:
SELECT [DISTINCT] * | 字段 [别名] [,字段 [别名] ,…]
FROM 表名称 [别名], [表名称 [别名] ,…]
[WHERE 条件(S)/等值连接]
[ORDER BY 排序字段 [ASC|DESC] [,排序字段 [ASC|DESC] ,…]];
使用表连接从多个表中查询数据
SELECT table1.column, table2.column
FROM table1, table2
WHERE table1.column1 = table2.column2;
在 WHERE 子句中写入连接条件
当多个表中有重名列时,必须在列的名字前加上表名作为前缀或者表的别名(使用别名更简单,性能更高).
等值连接是连接操作中最常见的一种,通常是在存在主外键约束条件的多表上建立的,连接条件中的两个字段通过等号建立等值关系。
需求:查询员工编号,员工名称,员工所属部门的编号和名称.
查询员工的姓名,工资,所在部门的名称,以及工资的等级.
SELECT e.ename, e.sal,d.dname,sg.grade
FROM emp e,dept d,salgrade sg
WHERE e.deptno = d.deptno AND e.sal BETWEEN s.losal AND s.hisal
SELECT e.ename, e.sal,d.dname,sg.grade
FROM emp e JOIN dept d ON d.deptno = e.deptno JOIN salgrade sg ON e.sal BETWEEN sg.losal AND sg.hisal
2.显示内连接查询:
显示内连接查询:查询的结果和隐式内连接一模一样.
区别在于,显示内连接可以看到 INNER JOIN.
SELECT table1.column, table2.column
FROM table1 [INNER] JOIN table2 ON(table1.column1 = table2.column2)
WHERE 条件
自然连接的条件是基于表中所有同名列的等值连接
为了设置任意的连接条件或者指定连接的列,需要使用ON子句
连接条件与其它的查询条件分开书写
使用ON 子句使查询语句更容易理解
练习,使用显式内连接查询:
需求:查询员工编号,员工名称,员工所属部门的编号和名称.
隐式内连接:
SELECT e.empno,e.ename,e.deptno,d.dname
FROM emp e,dept d
WHERE e.deptno = d.deptno
显示内连接:
SELECT e.empno,e.ename,e.deptno,d.dname
FROM emp e [INNER] JOIN dept d ON e.deptno = d.deptno
特殊情况,如果连接条件中的两个列同名,则可以简写使用USING.
SELECT e.empno,e.ename,e.deptno,d.dname
FROM emp e [INNER] JOIN dept d USING (deptno)
(2).外连接查询:
左外连接:查询出JOIN左边表的全部数据查询出来,JOIN右边的表不匹配的数据使用NULL来填充数据.
右外连接:查询出JOIN右边表的全部数据查询出来,JOIN左边的表不匹配的数据使用NULL来填充数据.
全外连接查询:
FULL OUTER JOIN中会返回所有右边表中的行和所有左边表中的行,即使在左边的表中没有可对应的列值或者右边的表中没有可对应的列
SELECT table1.column, table2.column
FROM table1 FULL [OUTER] JOIN table2 ON (table1.column_name = table2.column_name);
MYSQL中暂时不支持全连接
可以通过union +左右连接来完成;
(3).自连接查询:
把一张表看成是多张表,自己和自己做联合查询,一般用于树状结构中.
举例: 查询出所有部门的上级部门/员工的领导.
在查询语句中,一张表可以重复使用多次,完成多次连接的需要;
十.子查询:
(1)单行单列子查询:
子查询返回一行一列记录
1、返回一行记录
2、使用单行记录比较运算符:=;>;>=;<;<=;<>
1、查询大于公司平均工资的员工姓名
SELECT ename,sal FROM emp WHERE sal >
(SELECT AVG(sal) FROM emp)
2、查询出工资比MARTIN还要高的全部雇员信息
SELECT * FROM emp WHERE sal >
(SELECT sal FROM emp WHERE ename = 'MARTIN')
(2)多行单列子查询
多行子查询返回多行单列
1、返回多行
2、使用多行比较运算符
IN:与列表中的任意一个值相等 :需求:查询工资等于部门经理的员工信息.
ANY:与子查询返回的任意一个值比较
1): = ANY:此时和IN操作符相同. :需求:查询工资等于任意部门经理的员工信息.
2): > ANY:大于子查询中最小的数据. :需求:查询工资大于任意部门经理的员工信息.
3): < ANY:大于子查询中最大的数据. :需求:查询工资小于任意部门经理的员工信息.
ALL:与子查询返回的每一个值比较
1): > ALL:大于子查询中最大的数据.
2): < ALL:小于子查询中最小的数据.
查询平均工资高于公司平均工资的部门信息
SELECT deptno,AVG(sal) FROM dept JOIN emp USING (deptno) GROUP BY deptno HAVING AVG(sal) >
(
SELECT AVG(sal) FROM emp
)
(3)多行多列:
子查询返回的结果是多行多列
一般会把子查询返回的结果当成一个临时表,接着在临时表上继续查询或者连接查询;
注意,多行多列的子查询返回的结果必须要设置一个临时表名;
查询出每个部门的编号、名称、部门人数、平均工资:
SELECT d.deptno,d.dname,COUNT(e.empno),IFNULL(AVG(e.sal),0)
FROM dept d JOIN emp e USING (deptno)
GROUP BY d.deptno,d.dname
分析性能:笛卡尔积数量
可以先把每一个部门的编号,总人数,平均工资先查询出来.
SELECT deptno dno,COUNT(empno) count ,AVG(sal) avg FROM emp GROUP BY dno
再和dept表联合查询部门名称.
SELECT dept.deptno,temp.count,temp.avg FROM dept JOIN (SELECT deptno dno,COUNT(empno) count ,AVG(sal) avg FROM emp GROUP BY deptno) temp ON dept.deptno = temp.dno
(4).UNION/UNION ALL:
JOIN是用于把表横向连接,UNION/UNION ALL是用于把表纵向连接(一般用于做查询的临时表)
UNION 操作符用于合并两个或多个 SELECT 语句的结果集。
注意,
1,UNION 内部的 SELECT 语句必须拥有相同数量的列。
2,列也必须拥有兼容的数据类型。
3,每条 SELECT 语句中的列的顺序必须相同。
4,UNION 结果集中的列名总是等于 UNION 中第一个 SELECT 语句中的列名
5,UNION 操作符选取不同的值。如果允许重复的值,请使用 UNION ALL(性能高).
语法:
SELECT column_name(s) FROM table_name1
UNION|UNION ALL
SELECT column_name(s) FROM table_name2
在MYSQL 中实现FULL JOIN:
查询员工的编号,名称和部门名称.
1:先在emp表中插入一条数据,并设置depto为NULL.
2:查询
SELECT empno,ename,dname FROM emp LEFT JOIN dept USING (deptno)
UNION
SELECT empno,ename,dname FROM emp RIGHT JOIN dept USING (deptno)
十一.DML操作:
DML语句:执行之后,会返回受影响的行数.
插入语句:
INSERT INTO table [(column [, column…])] VALUES (value [, value…]);
一般来说,一条INSERT只能插入一条数据,因为MYSQL没有batch操作,所以,MYSQL也支持在一条INSERT中插入多条数据
INSERT INTO table [(column [, column…])] VALUES (value [, value…]),(value [, value…]),…
当然,一条SQL的长度是有限的,可以通过调整max_allowed_packet参数;
把查询的结果插入到表中:
如:INSERT INTO t_user (username,password) SELECT username,password FROM t_user;
更新语句:
UPDATE table
SET column = value [, column = value] …
[WHERE condition];
UPDATE语句也可以使用表连接,子查询等多种方式执行;
删除操作:
DELETE [FROM] table
[WHERE condition];
在delete语句中,where子句是可选的部分,如果使用了where子句,则删除的数据是符合where条件的所有记录;如果省略了where子句,则全表的数据都会被删除,delete语句的where条件也同样支持子查询,但是一定注意,删除语句中的where条件不能是要删除的数据表中的数据;所以,在涉及到删除的数据是通过要删除的表中的数据查询出来的,一般会把查询结果临时保存到另一张表,再通过delete语句删除;
十二.TCL(事务控制语句):
(1).数据库事务概念
在数据库中,所谓事务是指一组逻辑操作单元,使数据从一种状态变换到另一种状态。
为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元:当它全部完成时,数据的一致性可以保持,而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。
事务的操作:先定义开始一个事务,然后对数据作修改操作,这时如果提交(COMMIT),这些修改就永久地保存下来,如果回退(ROLLBACK),数据库管理系统将放弃您所作的所有修改而回到开始事务时的状态。
事务的ACID
原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
一致性: 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
隔离性: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
持久性:一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
并发事务可能产生的问题:
*脏读(Dirty read): **当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
**丢****失修改(Lost to modify): **指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
**不可重复读(Unrepeatableread): **指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
**幻读(Phantom read): **幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
不可重复度和幻读区别:
不可重复读的重点是修改,幻读的重点在于新增或者删除。
数据库的事务由下列语句组成:
一组DML语句,修改的数据在他们中保持一致
一个 DDL (Data Define Language) 语句
一个 DCL (Data Control Language)语句
* DCL(Grant(赋于权限 ) / Revoke(回收权限 ))
1、开始于第一个执行的语句
2、结束于:
用户执行COMMIT 或 ROLLBACK
单个的DDL or DCL 语句
用户连接异常错误,或者用户断开连接
系统崩溃
COMMIT和 ROLLBACK可以显示的控制事务。
好处:
1、保证数据一致性,修改过的数据在没有提交之前是不能被其他用户看到的。
2、在数据永久性生效前重新查看修改的数据
3、将相关操作组织在一起,一个事务中相关的数据改变或者都成功,或者都失败。
(2).数据库的事务并发问题
事务隔离级别有哪些?MySQL的默认隔离级别是?
SQL 标准定义了四个隔离级别:
READ-UNCOMMITTED(读取未提交):最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-COMMITTED(读取已提交):允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
REPEATABLE-READ(可重复读):对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
SERIALIZABLE(可串行化):最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。我们可以通过SELECT @@tx_isolation;命令来查看
这里需要注意的是:与 SQL 标准不同的地方在于InnoDB 存储引擎在 REPEATABLE-READ(可重读)事务隔离级别下使用的是Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server)是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读) 已经可以完全保证事务的隔离性要求,即达到了 SQL标准的SERIALIZABLE(可串行化)隔离级别。
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容):,但是你要知道的是InnoDB 存储引擎默认使用 REPEATABLE-READ(可重读)并不会有任何性能损失。
InnoDB 存储引擎在 分布式事务 的情况下一般会用到SERIALIZABLE(可串行化)隔离级别。
(3).乐观锁和悲观锁:
1.悲观锁:
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
2.乐观锁
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。
3.两种锁的使用场景
从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。
乐观锁常见的两种实现方式
乐观锁一般会使用版本号机制或CAS算法实现。
乐观锁示例(版本号version):
锁机制与InnoDB锁算法
MyISAM和InnoDB存储引擎使用的锁:
MyISAM 采用表级锁(table-level locking)。
InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁
表级锁和行级锁对比:
表级锁: Mysql中锁定 粒度最大 的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM和 InnoDB引擎都支持表级锁。
行级锁: Mysql中锁定 粒度最小 的一种锁,只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
InnoDB存储引擎的锁的算法有三种:
Record lock:单个行记录上的锁
Gap lock:间隙锁,锁定一个范围,不包括记录本身
Next-key lock:record+gap 锁定一个范围,包含记录本身
相关知识点:
innodb对于行的查询使用next-key lock
Next-locking keying为了解决Phantom Problem幻读问题
当查询的索引含有唯一属性时,将next-key lock降级为record key
Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生
有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock) A. 将事务隔离级别设置为RC B. 将参数innodb_locks_unsafe_for_binlog设置为1
十三.数据库的对象
数据库对象:表,索引,视图,图表,缺省值,规则,触发器,语法,函数等。
1、对象名称必须以字母开头
2、有效的字符包括数字、字母和三个特殊字符(# _ $)
3、不要使用保留字作为对象名称
4、同一用户下的对象不能同名,即使是不同的对象类型
(1)表:
1.创建表:
定义表的结构(有哪些列,每一列存储什么类型的数据)
create table 表名(
列名 类型 约束,
列名 类型 约束,
列名 类型 约束
);
简单的创建表语句
CREATE TABLE emp_copy AS SELECT * FROM emp
CREATE TABLE emp_copy AS SELECT * FROM emp WHERE 1=2
使用select的方式拷贝表结构,不会拷贝表的索引等结构,
所以一般使用
CREATE TABLE emp_bak LIKE emp :来复制表结构(不会拷贝外键);
2.修改表结构
①增加表字段
ALTER TABLE table
ADD (column datatype [DEFAULT expr]
[, column datatype]…);
使用 ADD 子句增加字段,新的字段只能被加到整个表的最后,并且不能与表中原有的字段重名
alter table employee add column sex char(1);
②.修改表字段
ALTER TABLE table
MODIFY (column datatype [DEFAULT expr]
[, column datatype]…);
可修改列的数据类型,大小
alter table employees modify sex tinyint;
不是任何情况都可以修改的,当字段只包含空值时,类型、大小都可以修改,否则修改可能不能成功
③.删除表字段
ALTER TABLE table
DROP column (columns);
可以从表中删除列:
ALTER TABLE employee DROP COLUMN sex;
从每行中删除掉字段占据的长度和数据,释放在数据块中占用的空间。删除大表中的字段将需要比较长的时间.
3.删除表
drop table:删除表,但并不释放表所占空间;
TRUNCATE TABLE 语句
清除表中所有的记录,delete可以选择删除表中的一部分
是DDL语句,不可以回滚,delete可以使用rollback回滚,放弃修改。
释放表的存储空间,delete不释放空间
2、 是删除数据的方法之一
3、TRUNCATE TABLE table_name;
注意:
1、表中所有数据将被删除
2、没有完成的事务被提交
3、所有相关的索引被删除
4、这个删除操作不能回滚
(2).表的约束:
表的约束(针对于某一列):
1.非空约束:NOT NULL,不允许某列的内容为空。
①,确保字段值不允许为空
②,与其他约束相比是唯一只能在字段级定义
CREATE TABLE employees(
last_name VARCHAR2(25) NOT NULL,
)
在表的外部定义约束:
非空约束必须使用MODIFY子句增加:
alter table employees modify column email not null
2.设置列的默认值:DEFAULT。
3.唯一约束:UNIQUE,在该表中,该列的内容必须唯一。
唯一性约束条件确保所在的字段或者字段组合不出现重复值
唯一性约束条件的字段允许出现(1或多个)空值
MySQL唯一性约束是添加在索引上的;
ALTER TABLE t1
ADD UNIQUE INDEX (t_value
) ;
4.主键约束:PRIMARY KEY, 非空且唯一。
主键设计:
①:单字段主键,单列作为主键,建议使用。
复合主键,使用多列充当主键,不建议。
②:主键分为两种:
1).自然主键:使用有业务含义的列作为主键(不推荐使用);
2).代理主键:使用没有业务含义的列作为主键(推荐使用);
③:复合主键:多列共同作为主键;作为复合主键的列不能为空
5.主键自增长:AUTO_INCREMENT,从1开始,步长为1。
6.外键约束:FOREIGN KEY,A表中的外键列. A表中的外键列的值必须参照于B表中的某一列(B表主键)。
外键是构建于一个表的两个字段或者两个表的两个字段之间的关系
外键确保了相关的两个字段的关系:
*子表外键列的值必须在主表参照列值的范围内,或者为空
主表主键值被子表参照时,主表记录不允许被删除
外键约束条件参照的是主表的一个或者多个字段的值,通常被外键参照的 是主表的主键或者唯一键
(3)索引:
1.原理:
索引是:
一个数据库对象
用来加速对表的查询
通过使用快速路径访问方法快速定位数据,减少了磁盘的I/O
与表独立存放
由数据库自动维护
2.MySQL的基本存储结构是页 (记录都存在页里边) :
各个数据页可以组成一个双向链表
每个数据页中的记录又可以组成一个单向链表
- 每个数据页都会为存储在它里边儿的记录生成一个页目录,在通过主键查找某条记录的时候可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录- 以其他列(非主键)作为搜索条件:只能从最小记录开始依次遍历单链表中的每条记录。
所以说,如果我们写select * from user where indexname = 'xxx’这样没有进行任何优化的sql语句,默认会这样做:
定位到记录所在的页:需要遍历双向链表,找到所在的页
从所在的页内中查找相应的记录:由于不是根据主键查询,只能遍历所在页的单链表了
很明显,在数据量很大的情况下这样查找会很慢!这样的时间复杂度为O(n)。
索引做了些什么可以让我们查询加快速度呢?****其实就是将无序的数据变成有序(相对):
要找到id为8的记录简要步骤:
很明显的是:没有用索引我们是需要遍历双向链表来定位对应的页,现在通过 “目录” 就可以很快地定位到对应的页上了!(二分查找,时间复杂度近似为O(logn))
其实底层结构就是B+树,B+树作为树的一种实现,能够让我们很快地查找出对应的记录。
什么是最左前缀原则?
MySQL中的索引可以以一定顺序引用多列,这种索引叫作联合索引。如User表的name和city加联合索引就是(name,city),而最左前缀原则指的是,如果查询的时候查询条件精确匹配索引的左边连续一列或几列,则此列就可以被用到。如下:
select * from user where name=xx and city=xx ; //可以命中索引select * from user where name=xx ; // 可以命中索引select * from user where city=xx ; // 无法命中索引
这里需要注意的是,查询的时候如果两个条件都用上了,但是顺序不同,如 city= xx and name =xx,那么现在的查询引擎会自动优化为匹配联合索引的顺序,这样是能够命中索引的。
由于最左前缀原则,在创建联合索引时,索引字段的顺序需要考虑字段值去重之后的个数,较多的放前面。ORDER BY子句也遵循此规则。
注意避免冗余索引
冗余索引指的是索引的功能相同,能够命中就肯定能命中 ,那么 就是冗余索引如(name,city )和(name )这两个索引就是冗余索引,能够命中后者的查询肯定是能够命中前者的 在大多数情况下,都应该尽量扩展已有的索引而不是创建新索引。
MySQLS.7 版本后,可以通过查询 sys 库的 schema_redundant_indexes 表来查看冗余索引
3…创建索引
自动: 当在表上定义一个PRIMARY KEY时,自动创建一个对应的唯一索引.
当在表上定义一个外键时,自动创建一个普通索引;
手动: 用户可以创建索引以加速查询.
在一列或者多列上创建索引.
CREATE INDEX index ON table (column[, column]…);
如果多列在一起,就叫做复合索引;在很多情况下,复合索引比单个索引更好(理解原理即可);
1.添加PRIMARY KEY(主键索引)
ALTER TABLE table_name
ADD PRIMARY KEY ( column
)
2.添加UNIQUE(唯一索引)
ALTER TABLE table_name
ADD UNIQUE ( column
)
3.添加INDEX(普通索引)
ALTER TABLE table_name
ADD INDEX index_name ( column
)
4.添加FULLTEXT(全文索引)
ALTER TABLE
table_nameADD FULLTEXT (
column)
5.添加多列索引
ALTER TABLE table_name
ADD INDEX index_name ( column1
, column2
, column3
)
4.优化索引
哪些值可以创建索引?
①,外键一般要创建索引
②,经常使用的查询条件要创建索引。如果使用like ‘%’操作,不会使用索引。
③,索引不是越多越好
④,不要在可选值很少的属性上面创建索引
⑤,MySQL索引的使用,并不是所有情况下都会使用索引,只有当MySQL认为索引足够能够提升查询性能时才会使用;
(4).视图
1.概念:
视图也就是虚表,实际上视图就是一个命名的查询,用于改变基表数据的显示。
可以限制对数据的访问
可以使复杂的查询变的简单
提供了数据的独立性
提供了对相同数据的不同显示
2.视图使用
①.创建视图
语法:
在CREATE VIEW语句后加入子查询.
CREATE [OR REPLACE] VIEW view
[(alias[, alias]…)]
AS subquery
[WITH READ ONLY];
创建视图
CREATE OR REPLACE VIEW emp_v_30
AS SELECT empno, ename, sal
FROM emp
WHERE deptno =30;
在子查询中使用别名创建视图.
CREATE VIEW sal_v_10
AS SELECT employee_id ID, last_name NAME,
salary*12 ANN_SALARY
FROM employees
WHERE department_id = 10;
在视图中的列名使用的是子查询中列的别名.
②.使用视图
在查询时,不需要再写完全的Select查询语句,只需要简单的写上从视图中查询的语句就可以了
SELECT * FROM sal_v_10;
默认情况下,可以直接通过对视图的DML操作去修改视图对应表中的内容(前提是视图中没有通过公式导出的列);
③.删除视图
删掉视图不会导致数据的丢失,因为视图是基于数据库的表之上的一个查询定义.
DROP VIEW view_name;
④.视图不能更新的情况:
(1)若视图的字段是来自字段表达式或常数,则不允许对此视图执行INSERT、UPDATE操作,允许执行DELETE操作;
(2)若视图的字段是来自库函数,则此视图不允许更新;
(3)若视图的定义中有GROUP BY子句或聚集函数时,则此视图不允许更新;
(4)若视图的定义中有DISTINCT任选项,则此视图不允许更新;
(5)若视图的定义中有嵌套查询,并且嵌套查询的FROM子句中涉及的表也是导出该视图的基表,则此视图不允许更新;
(6)若视图是由两个以上的基表导出的,此视图不允许更新;
(7)一个不允许更新的视图上定义的视图也不允许更新;
(8)由一个基表定义的视图,只含有基表的主键或候补键,并且视图中没有用表达式或函数定义的属性,才允许更新。
十四.存储引擎
(1).一些常用命令
查看MySQL提供的所有存储引擎
mysql> show engines;
从上图我们可以查看出 MySQL 当前默认的存储引擎是InnoDB,并且在5.7版本所有的存储引擎中只有 InnoDB 是事务性存储引擎,也就是说只有 InnoDB 支持事务。
查看MySQL当前默认的存储引擎
我们也可以通过下面的命令查看默认的存储引擎。
mysql> show variables like '%storage_engine%';
查看表的存储引擎
show table status like "table_name" ;
(2).MyISAM和InnoDB区别
yISAM是MySQL的默认数据库引擎(5.5版之前)。虽然性能极佳,而且提供了大量的特性,包括全文索引、压缩、空间函数等,但MyISAM不支持事务和行级锁,而且最大的缺陷就是崩溃后无法安全恢复。不过,5.5版本之后,MySQL引入了InnoDB(事务性数据库引擎),MySQL 5.5版本后默认的存储引擎为InnoDB。
大多数时候我们使用的都是 InnoDB 存储引擎,但是在某些情况下使用 MyISAM 也是合适的比如读密集的情况下。(如果你不介意 MyISAM 崩溃回复问题的话)。
两者的对比:
**1.是否支持行级锁 : MyISAM 只有表级锁(table-level locking),而InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。
2.是否支持事务和崩溃后的安全恢复:MyISAM 强调的是性能,每次查询具有原子性,其执行比InnoDB类型更快,但是不提供事务支持。但是InnoDB 提供事务支持事务,外部键等高级数据库功能。具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。
3.是否支持外键: MyISAM不支持,而InnoDB支持。
4.是否支持MVCC :仅 InnoDB 支持。应对高并发事务, MVCC比单纯的加锁更高效;MVCC只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下工作;MVCC可以使用 乐观(optimistic)锁 和 悲观(pessimistic)锁来实现;各数据库中MVCC实现并不统一。
总结:
一般情况下我们选择 InnoDB 都是没有问题的,但是某事情况下你并不在乎可扩展能力和并发能力,也不需要事务支持,也不在乎崩溃后的安全恢复问题的话,选择MyISAM也是一个不错的选择。但是一般情况下,我们都是需要考虑到这些问题的。
十五.大表优化
当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下:
(1).限定数据的范围
务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内;
(2).、读/写分离
经典的数据库拆分方案,主库负责写,从库负责读;
(3).垂直分区
根据数据库里面数据表的相关性进行拆分。 例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。
简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。 如下图所示,这样来说大家应该就更容易理解了。
垂直拆分的优点: 可以使得列数据变小,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。
垂直拆分的缺点: 主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应用层进行Join来解决。此外,垂直分区会让事务变得更加复杂;
(4).水平分区
保持数据表结构不变,通过某种策略存储数据分片。****这样每一片数据分散到不同的表或者库中,达到了分布式的目的。****水平拆分可以支撑非常大的数据量。
水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放。举个例子:我们可以将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。
水平拆分可以支持非常大的数据量。需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL并发能力没有什么意义,所以 水平拆分最好分库 。
水平拆分能够 支持非常大的数据量存储,应用端改造也少,但 分片事务难以解决 ,跨节点Join性能较差,逻辑复杂。《Java工程师修炼之道》的作者推荐 尽量不要对数据进行分片,因为拆分会带来逻辑、部署、运维的各种复杂度 ,一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络I/O。
最后,推荐几篇好的文章和提供mysql的使用手册
文章链接
提取码:8hfi