- 数据库是一门独立的学科,只要是做软件开发的,数据库都要学。
- 数据库(电子化的文件柜)是“按照数据结构来组织、存储和管理数据的仓库”。是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。
- 它的存储空间很大,可以存放百万条、千万条、上亿条数据。
- 数据库并不是随意地将数据进行存放,是有一定的规则的,否则查询的效率会很低。
- 当今世界是一个充满着数据的互联网世界,充斥着大量的数据。即这个互联网世界就是数据世界。数据的来源有很多,比如出行记录、消费记录、浏览的网页、发送的消息等等。除了文本类型的数据,图像、音乐、声音都是数据。
- 数据库对应的英文单词是DataBase,简称DB。
- 关系型数据库
- 关系型数据库是依据关系模型来创建的数据库。所谓关系模型就是“一对一、一对多、多对多”等关系模型,关系模型就是指二维表格模型,因而一个关系型数据库就是由二维表及其之间的联系组成的一个数据组织。
- 关系型数据可以很好地存储一些关系模型的数据,比如一个老师对应多个学生的数据(“多对多”),一本书对应多个作者(“一对多”),一本书对应一个出版日期(“一对一”)。
- 关系模型包括数据结构(数据存储的问题,二维表)、操作指令集合(SQL语句)、完整性约束(表内数据约束、表与表之间的约束)。
- 非关系型数据库(NoSQL)
- NoSQL,泛指非关系型的数据库。随着互联网web2.0网站的兴起,传统的关系数据库在处理web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,出现了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。
- NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,特别是大数据应用难题。NoSQL最常见的解释是“non-relational”, “Not Only SQL”也被很多人接受。
- NoSQL仅仅是一个概念,泛指非关系型的数据库,区别于关系数据库,它们不保证关系数据的ACID特性。NoSQL是一项全新的数据库革命性运动,其拥护者们提倡运用非关系型的数据存储,相对于铺天盖地的关系型数据库运用,这一概念无疑是一种全新的思维的注入。
- NoSQL有如下优点:易扩展,NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展。无形之间也在架构的层面上带来了可扩展的能力。大数据量,高性能,NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。
- 数据库管理系统(Database Management System,简称DBMS)是为管理数据库而设计的电脑软件系统,一般具有存储、截取、安全保障、备份等基础功能。
- 数据库管理系统是数据库系统的核心组成部分,主要完成对数据库的操作与管理功能,实现数据库对象的创建、数据库存储数据的查询、添加、修改与删除操作和数据库的用户管理、权限管理等。
- 常见的数据库管理系统有:MySQL、Oracle、DB2、MS SQL Server、SQLite、PostgreSQL、Sybase等。
- 结构化查询语言(Structured Query Language)简称SQL,是一种特殊目的的编程语言,是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统。
- 结构化查询语言是高级的非过程化编程语言,允许用户在高层数据结构上工作。它不要求用户指定对数据的存放方法,也不需要用户了解具体的数据存放方式,所以具有完全不同底层结构的不同数据库系统, 可以使用相同的结构化查询语言作为数据输入与管理的接口。结构化查询语言语句可以嵌套,这使它具有极大的灵活性和强大的功能。
- SQL的分类
- DQL
- 数据查询语言(Data Query Language, DQL)是SQL语言中,负责进行数据查询而不会对数据本身进行修改的语句,这是最基本的SQL语句。保留字==SELECT==是DQL(也是所有SQL)用得最多的动词,其他DQL常用的保留字有FROM,WHERE,GROUP BY,HAVING和ORDER BY。这些DQL保留字常与其他类型的SQL语句一起使用。
- DDL
- 数据定义语言 (Data Definition Language, DDL) 是SQL语言集中,负责数据结构定义与数据库对象定义的语言,由==CREATE、ALTER与DROP==三个语法所组成,最早是由 Codasyl (Conference on Data Systems Languages) 数据模型开始,现在被纳入 SQL 指令中作为其中一个子集。
- DML
- 数据操纵语言(Data Manipulation Language, DML)是SQL语言中,负责对数据库对象运行数据访问工作的指令集,以==INSERT、UPDATE、DELETE==三种指令为核心,分别代表插入、更新与删除。
- DCL
- 数据控制语言 (Data Control Language) 在SQL语言中,是一种可对数据访问权进行控制的指令,它可以控制特定用户账户对数据表、查看表、预存程序、用户自定义函数等数据库对象的控制权。由 ==GRANT 和 REVOKE== 两个指令组成。DCL以控制用户的访问权限为主,GRANT为授权语句,对应的REVOKE是撤销授权语句。
- TPL
- 数据事务管理语言(Transaction Processing Language)它的语句能确保被DML语句影响的表的所有行及时得以更新。TPL语句包括BEGIN TRANSACTION,COMMIT和ROLLBACK。
- CCL
- 指针控制语言(Cursor Control Language),它的语句,像DECLARE CURSOR,FETCH INTO和UPDATE WHERE CURRENT用于对一个或多个表单独行的操作。
- ==DBMS、SQL、DB之间的关系==
- DBMS通过执行SQL来操作DB中的数据。
name | age | gender |
---|---|---|
张三 | 20 | 男 |
李四 | 22 | 女 |
- 以上就是数据库表格的直观展示形式。
- 表格英文单词table。
- 表是数据库存储数据的基本单元,数据库存储数据的时候,是将数据存储在表对象当中的。为什么将数据存储在表中呢?因为表存储数据非常直观。
- 任何一张表都有行和列:
- 行:记录(一行就是一条数据)
- 列:字段(name字段、age字段、gender字段)
- 每个字段包含以下属性:
- 字段名:name、age、gender都是字段的名字
- 字段的数据类型:每个字段都有数据类型,比如:字符类型、数字类型、日期类型
- 字段的数据长度:每个字段有可能会有长度的限制
- 字段的约束:比如某些字段要求该字段下的数据不能重复、不能为空等,用来保证表格中数据合法有效
MySQL 的数据类型可以分为整数类型、浮点数类型、定点数类型、日期和时间类型、字符串类型、二进制类型等。
- tinyint:1个字节(微小整数)
- smallint:2个字节(小整数)
- mediumint:3个字节(中等大小的整数)
- int(integer):4个字节(普通大小整数)
- bigint:8个字节(大整数)
- float:4个字节,单精度(最多5位小数)
- double:8个字节,双精度(最多16位小数)
- decimal:定点数类型。底层实际上采用字符串的形式存储数字。 语法:decimal(m, d) 例如:decimal(3, 2) 表示3个有效数字,2个小数。(有效数字最多65个,小数位最多30个)
- year:1个字节,只存储年,格式YYYY
- time:3个字节,只存储时间,格式HH:MM:SS / HHMMSS
- date:3个字节,只存储年月日,格式:YYYY-MM-DD
- datetime:8个字节,存储年月日+时分秒,格式:YYYY-MM-DD HH:MM:SS(从公元1000年~公元9999年)
- timestamp:4个字节,存储年月日+时分秒,格式:YYYY-MM-DD HH:MM:SS(从公元1980年~公元2040年)或者格式为 YYYYMMDDHHMMSS(采用这种格式不需要使用单引号,当然你使用单引号也可以)
- char(m):固定长度字符串,在定义时指定字符串列长。当保存时,在右侧填充空格以达到指定的长度。m表示列的长度,范围是 0~255 个字符。
- 注意:例如,CHAR(4) 定义了一个固定长度的字符串列,包含的字符个数最大为 4。当插入的字符长度大于4,则报错(除非超过4个长度之后都是空格字符,则空格字符会自动被删除用来保证插入的成功)。
- varchar(m):长度可变的字符串。varchar 的最大实际长度由最长的行的大小和使用的字符集确定,而实际占用的空间为字符串的实际长度加 1。m长度是0~16383个字符
- 注意:例如,varchar(50) 定义了一个最大长度为 50 的字符串,如果插入的字符串只有 10 个字符,则实际存储的字符串为 10 个字符和一个字符串结束字符。varchar在值保存和检索时尾部的空格仍保留。
- text:
- tinytext 表示长度为 255字符的 TEXT 列。
- text 表示长度为 65535字符的 TEXT 列。
- mediumtext 表示长度为 16777215字符的 TEXT 列。
- longtext 表示长度为 4294967295 或 4GB 字符的 TEXT 列。
- enum:
- 语法:<字段名> enum('值1','值2',...)
- 该字段插入值时,只能是指定的枚举值。
- set:
- 语法:<字段名> set('值1','值2','值3',...) 注意:值不可重复。
- 该字段插入值时,只能是指定的值。
BLOB类型:二进制大对象,可以存储图片、声音、视频等文件。
- blob:小的,最大长度65535个字节
- mediumblob:中等的,最大长度16777215个字节
- longblob:大的,最大长度4GB的字节
在MySQL当中,如何统计一个SQL语句的执行时长?
- 可以使用这个命令:show profiles; 这个命令可以查看在mysql中执行的所有SQL以及命令的耗费时长。
- show profiles; 是在mysql5.0.37之后添加的。所以要确保你的mysql版本没问题。
- 如何开启时长统计功能:set profiling = 1;
- 查看时长统计功能是否开启:show variables like '%pro%';
- 查看每条SQL的耗时:show profiles;
- 查看其中某条SQL耗时明细:show profile for query query_id;
- 查看最新一条SQL的耗时明细:show profile;
- 查看cpu,io等信息:show profile block io, cpu for query query_id;
/* DOS中登录数据库 mysql -uroot -p DOS中查看MySQL版本信息 mysql --version */ #数据库中查看MySQL版本 select version(); #退出MySQL 1. exit 2. quit #终止错误的SQL语句 1.\c 2.ctrl+c #注意: #SQL语句可以单行或多行书写,注意以分号结尾。 #单行注释在前面加-- 或者# #多行注释用/**/
#先将要执行的SQL指令全部写入.sql脚本文件中 #再通过source命令执行该脚本文件 source 脚本文件的位置(一般直接拖拽入DOS命令窗口) #相较于MySQL客户端工具优点:可执行内存大的脚本文件
#查看当前数据库管理系统有哪些数据库 show databases; #创建一个数据库 create database [if not exists] 数据库名 [character set字符集] [collate 排序规则]; #删除一个数据库 drop database [if exists] 数据库名; #查询当前处于哪一个数据库里面 select database(); #切换数据库 use 数据库名; #修改数据库的字符集和排序规则: alter database 数据库名 [character set字符集] [collate 排序规则]; #注意数据库一般没有改名方法
#查看当前数据库有哪些表 show tables [from 数据库名]; #创建一张表:多个约束用空格隔开 create table [if not exists] 表名( 字段名1 数据类型 [列级约束] [comment'注释内容'], 字段名2 数据类型 [列级约束] [comment'注释内容'], ...... 字段名3 数据类型 [列级约束] [comment'注释内容'], 表级约束(没有时前面这个逗号不写) ) [约束] [comment'注释内容']; #删除一张表: drop table [if exists] 表名; #查看表的结构: desc 表名;=========describe 表名; #查看表的建表语句: show create table 表名; #修改表名: alter table 表名 rename to 新表名;
#添加表的字段: alter table 表名 add 字段名 数据类型 [约束] [comment '注释内容']; #删除表的字段: alter table 表名 drop 字段名; #修改某个字段的数据类型: alter table 表名 modify 字段名 新数据类型; #修改某个字段的字段名: alter table 表名 change 旧字段名 新字段名 数据类型 [comment ‘注释内容’]; #注意modify只能修改数据类型,不能修改字段名;而change都可以修改,且change后面必须跟新字段名和数据类型两者,少一个都不行
#给指定的字段添加数据: insert into 表名 (字段名1,字段名2,字段名3,……,字段名n) values (字段1的值1,字段2的值1,字段三的值1,……,字段n的值1),(字段1的值2,字段2的值2,字段三的值2,……,字段n的值2),……,(字段1的值n,字段2的值n,字段三的值n,……,字段n的值n); #注意:表名后面的小括号当中的字段名如果省略掉,表示自动将所有字段都列出来了,并且字段的顺序和建表时的顺序一致。
#删除符合条件的一行数据记录: delete from 表名 [where 条件]; #注意:不带条件则默认删除整张表的数据;delete无法删除某一字段的值,应该采用update 置null来操作。以上的删除属于DML的方式删除,这种删除的数据是可以通过事务回滚的方式重新恢复的,但是删除的效率较低。(这种删除是支持事务的。) #注意:另外还有一种删除表中数据的方式,但是这种方式不支持事务,不可以回滚,删了之后数据是永远也找不回来了。这种删除叫做:表被截断。这个语句删除效率非常高,巨大的表,瞬间干掉所有数据,但不可恢复。该删除如下: #清空一张表: truncate table 表名;
#修改符合条件的数据: update 表名 set 字段名1=新值,字段名2=新值,……,字段n=新值 [where 条件]; #注意:不带条件则默认修改整张表的数据。
#查询整张表的数据:以后java中编写SQL语句不建议这样写,效率低,可读性差 select * from 表名; #查询制定字段的数据:查询时可以做计算 select 字段名1 [as '别名'],字段名2 [as '别名'],…… from 表名; #注意:as可以省略,将别名紧跟原名空格写出也是可以的;而如果别名中没有空格或没有中文的话,可以去掉引号 #字段的去重查询:distinct只能出现在所有字段的最前面,当distinct出现后,后面多个字段一定是联合去重的 select distinct 字段列表 from 表名;
条件 说明 = 等于 <>或!= 不等于 >= 大于等于 <= 小于等于 > 大于 < 小于 between...and... 等同于 >= and <=,在使用时一定是左小右大 is null 为空 is not null 不为空 <=> 安全等于(可读性差,很少使用了)。 and 或 && 并且 or 或 || 或者 in() 在指定的值当中,等同于采用=和or连接 not in() 不在指定的值当中,等同于采用<>和and连接 like 模糊查询 and和or同时出现时,and优先级较高,会先执行,如果希望or先执行,这个时候需要给or条件添加小括号。另外,以后遇到不确定的优先级时,可以通过添加小括号的方式来解决。
==判断某个数据是否为null,不能使用等号,只能使用 is null== ==判断某个数据是否不为null,不能使用不等号,只能使用 is not null== 在数据库中null不是一个值,不能用等号和不等号衡量,null代表什么也没有,没有数据,没有值
in后面有一个小括号,小括号当中有多个值,值和值之间采用逗号隔开;not in同理 ==in是自动忽略NULL的。==因为等同于=和or连接,而有NULL表明=NULL一定为false,再通过or连接会忽略NULL,只进行其他条件判断 ==not in是不会自动忽略NULL的。==因为等同于<>和and连接,而有NULL表明<>NULL一定为false,再通过and连接表明结果一定为false,查不到任何结果
==如果in和or所在列有索引或者主键的话,or和in没啥差别;如果in和or所在列没有索引的话,in的效率越高==
==在模糊查询中,通配符主要包括两个:一个是%,一个是下划线_。其中%代表任意多个字符。下划线_代表任意一个字符。==想让这个下划线变成一个普通的下划线字符,就要使用转义字符了,在mysql当中转义字符是“\”,这个和java语言中的转义字符是一样的。
#查询符合条件的数据: select 字段列表 from 表名 where 过滤条件; # 第一步:先执行from # 第二步:再通过where条件过滤 # 第三步:最后执行select,查询并将结果展示到控制台
#排序方法有:ASC(升序=>默认值);DESC(降序) #如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序 ; select 字段列表 from 表名 order by 字段名1 排序方法,字段名2 排序方法;
常用聚合函数:
#统计个数:count(字段名) #求该字段中的最大值:max(字段名) #求该字段中的最小值:min(字段名) #求该字段中的平均值:avg(字段名) #求该字段的和:sum(字段名) #注意:聚合函数是作用于字段的,也就是表中的一列数据,注意null不参与聚合函数的计算,即所有的聚合函数都是自动忽略NULL的。 select count(distinct job) from emp;#统计岗位数量 select count(ename) from emp;#统计的是这个ename字段中不为NULL个数总和。 #count(*)和count(1)的效果一样,统计该组中总记录行数。 select count(*) from emp; select count(1) from emp;
select 字段列表 from 表名 [where 过滤条件] group by 分组字段名 [having 分组后的过滤条件] #注意:当select语句中有group by的话,select后面只能跟聚合函数或参加分组的字段 #where和having的区别: #1、执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组之后对结果进行过滤 #2、判断条件不同:where不能对聚合函数进行判断,而having可以 #使用原则:尽量在where中过滤,实在不行,再使用having。越早过滤效率越高。
select 字段列表 from 表名 limit 起始索引,查询记录数; #计算:先有要查询的页码和每页要显示几条记录,则起始索引=(查询页码-1)*每页显示记录数,注意起始索引从0开始 #如果下标是从0开始,可以省略起始索引,简写为: select 字段列表 from 表名 limit 查询记录数;
#编写顺序: select 字段列表 from 表名 where 条件 group by 要分组的字段 having 条件 order by 排序字段及规律 limit 分页参数; #执行顺序: From > where > group by > having > select > order by > limit
连接查询:从两张或更多张表中联合查询数据称为多表查询,又叫做连接查询。一般过滤条件要用到多张表的数据,注意与union和union all的区别。
内连接:满足条件的记录查询出来。也就是两张表的交集。
#内连接: #包括: #1、等值连接:连接时,条件为等量关系。 #2、非等值连接:连接时,条件是非等量关系。 #3、自链接:连接时,一张表看做两张表,自己和自己进行连接。 select 字段列表 from 主表名 主表别名 [inner] join 副表名 副表别名 on 过滤条件;
外连接:是除了满足条件的记录查询出来,再将其中一张表的记录全部查询出来,另一张表如果没有与之匹配的记录,自动模拟出NULL与其匹配。
左外连接:
右外连接:
#左外连接: select 字段列表 from 主表名 主表别名 left [outer] join 副表名 副表别名 on 过滤条件; #右外连接: select 字段列表 from 主表名 主表别名 right [outer] join 副表名 副表别名 on 过滤条件;
全连接:两张表数据全部查询出来,没有匹配的记录,各自为对方模拟出NULL进行匹配。
==MySQL不支持full join。oracle数据库支持。==
select 字段列表 from 主表名 主表别名 full join 副表名 副表别名 on 过滤条件;
不管是union还是union all都可以将两个查询结果集进行合并。过滤条件一般各用各的数据,不同于连接查询的过滤条件一般要互相利用。
- union会对合并之后的查询结果集进行去重操作。
- union all是直接将查询结果集合并,不进行去重操作。(union all和union都可以完成的话,优先选择union all,union all因为不需要去重,所以效率高一些。)
- 以上案例采用or也可以完成,那or和union all有什么区别?考虑走索引优化之类的选择union all,其它选择or。两个结果集合并时,列数量要相同。
#如: select name,salary from firsttable where job='MANAGER' union all select name,salary from firsttable where job='SALESMAN';
子查询:select语句中嵌套select语句就叫做子查询。
select语句可以嵌套在哪里?===>where后面、from后面、select后面都是可以的。
技巧:将里面的select看作一张临时表,通过小括号括起来使用
exists、not exists
在 MySQL 数据库中,EXISTS(存在)用于检查子查询的查询结果行数是否大于0。如果子查询的查询结果行数大于0,则 EXISTS 条件为真。(即存在查询结果则是true。)
- EXISTS 可以与 SELECT、UPDATE、DELETE 一起使用,用于检查另一个查询是否返回任何行;
- EXISTS 可以用于验证条件子句中的表达式是否存在;
- EXISTS 常用于子查询条件过滤,例如查询有订单的用户等。
NOT EXISTS 用于检查一个子查询是否返回任何行,如果没有行返回,那么 NOT EXISTS 将返回 true。
#如:t_customer表示顾客表,t_order表示订单表 select * from t_customer c where exists(select * from t_order o where o.customer_id=c.customer_id);
IN 和 EXISTS 都是用于关系型数据库查询的操作符。不同之处在于:
- IN 操作符是根据指定列表中的值来判断是否满足条件,而 EXISTS 操作符则是根据子查询的结果是否有返回记录集来判断。
- EXISTS 操作符通常比 IN 操作符更快,尤其是在子查询返回记录数很大的情况下。因为 EXISTS 只需要判断是否存在符合条件的记录,而 IN 操作符需要比对整个列表,因此执行效率相对较低。
- IN 操作符可同时匹配多个值,而 EXISTS 只能匹配一组条件。
#查询用户数:select * from mysql.user; #创建用户:create user '用户名'@'主机名' identified by '密码'; #删除用户:drop user '用户名'@'主机名'; #修改用户密码:alter user '用户名'@'主机名' identified with mysql_native_password by '新密码'; #MySQL8版本以后:alter user '用户名'@'主机名' identified by '密码'; #修改用户名:rename user '原始用户名'@'主机名' to '新用户名'@'主机名'; #注意:当前主机名:localhost 所有主机名:% #删除用户后、修改密码后、修改用户名后,都需要刷新权限才能生效:flush privileges
#查询某人的权限:show grants for '用户名'@'主机名'; #授予某人权限:grant 权限1,权限2...,权限n on 数据库名.表名 to '用户名'@'主机名'; #撤销某人权限:revoke 权限1,权限2...,权限n on 数据库名.表名 from '用户名'@'主机名'; #注意:撤销权限时 “数据库名.表名” 不能随便写,要求和授权语句时的 “数据库名.表名” 一致。 #所有权限:all privileges;库名可以使用 * ,它代表所有数据库;表名可以采用 * ,它代表所有表 #其他权限:select、insert、delete、update、alter、create、drop、index(索引)、usage(登录权限)...... #授权和撤权后必须刷新权限,才能生效:flush privileges # with grant option的作用是:java2用户也可以给其他用户授权了。 grant select,insert,delete,update on *.* to 'java2'@'%' with grant option;
注意:请在登录mysql数据库之前进行
# 导出powernode这个数据库中所有的表 mysqldump powernode > e:/powernode.sql -uroot -p1234 --default-character-set=utf8 # 导出powernode中emp表的数据 mysqldump powernode emp > e:/powernode.sql -uroot -p1234 --default-character-set=utf8
第一种方式:(请在登录mysql之前进行)
# 现在登录mysql状态下新建一个数据库 create database powernode; # 在登录mysql之前执行以下命令 mysql powernode < e:/powernode.sql -uroot -p1234 --default-character-set=utf8
第二种方式:(请在登录mysql之后操作)
create database powernode; use powernode; source d:/powernode.sql
函数的执行方法:select+空格+函数;
如:select now();
#拼接字符串:concat(s1,s2,s3,……) #注意:在mysql8之前,双竖线||也是可以完成字符串拼接的。但在mysql8之后,||只作为逻辑运算符,不能再进行字符串拼接了。mysql中可以使用+进行字符串的拼接吗?不可以,在mysql中+只作加法运算,在进行加法运算时,会将加号两边的数据尽最大的努力转换成数字再求和,如果无法转换成数字,最终运算结果通通是0 #将str字符串转成小写:lower(str)或lcase(str) #将str字符串转成大写写:upper(str)或ucase(str) #用pad字符串对str字符串的左侧进行填充,达到n个字符串长度:lpad(str,n,pad) #用pad字符串对str字符串的右侧进行填充,达到n个字符串长度:rpad(str,n,pad) #去掉str字符串头部和尾部空格:trim(str) #也可以去除指定前缀:如去除前缀0:select trim(leading '0' from '000111000'); #也可以去除指定后缀:如去除后缀0:select trim(trailing '0' from '000111000'); #去除指定前后缀:如去除前后缀0:select trim(both '0' from '000111000'); #返回字符串的长度:length(str),注意一个汉字两个长度 #返回字符串中字符的个数:char_length(str) #返回str字符串从start位置起的len个长度的字符串:substring(str,start,len),当len缺少时,默认截取到字符串尾,注意字符串的起始下标从1开始,不是0
#向上取整:ceil(x); #向下取整:floor(x); #取绝对值:abs(x); #返回x/y的模:mod(x,y); #返回0~1的随机数浮点数:rand(); #返回一个0~1的固定的浮点数:rand(x);====>即每次使用相同的x得到的值都是相等的 #对x四舍五入取整:round(x); #对x保留y位小数,符合四舍五入规则:round(x,y); #对x的小数位截断,留下y位小数:truncate(x,y);
#在SQL语句中,凡是有NULL参与的数学运算,最终的计算结果都是NULL: #当x为NULL时,将x当做y处理:ifnull(x, y); #表示如果员工的津贴是NULL时当做0处理:ifnull(comm, 0);
#当前日期:curdate(); #当前时间:curtime(); #当前日期和时间:now()或sysdate() #区别: #now():获取的是执行select语句的时刻。 #sysdate():获取的是执行sysdate()函数的时刻。 #查询指定date是一周中的第几天,注意周日是第一天:dayofweek(date); #查询指定date是一月中的第几天:dayofmonth(date); #查询指定date是一年中的第几天:dayofyear(date); #获取指定date的年:year(date); #获取指定date的月:month(date); #获取指定date的日:day(date); #获取指定datetime的时:hour(datetime); #获取指定datetime的分:minute(datetime); #获取指定datetime的秒:second(datetime); #获取指定date的年月日:date(date); #获取指定datetime的时分秒:time(datetime); #获取指定date所在月的最后一天的日期:last_day(date); #在datetime的基础上加上exper个typer类型的时间:date_add(datetime,interval expr typer); #datetime:一个日期类型的数据 #interval:关键字,翻译为“间隔”,固定写法 #expr:指定具体的间隔量,一般是一个数字。也可以为负数,如果为负数,效果和date_sub函数相同 #type:值year表年;值month表月;值day表日;值hour表时;值minute表分;值second表秒;值microsecond表微秒(1秒等于1000毫秒,1毫秒等于1000微秒);值week表周;值quarter表季度 #注意:type还有复合型单位,此时注意expr和type的写法 #将datetime日期格式化为某种形式:date_format(datetime,'日期格式'); #形式:%Y:四位年份 %y:两位年份 %m:月份(1..12) %d:日(1..30) %H:小时(0..23) %i:分(0..59) %s:秒(0..59) #如:select date_format(now(),'%Y-%m-%d %H:%i:%s');等同于select now(); #将某种形式日期转换成datetime日期格式:str_to_date('日期格式','对应位置') #如:select str_to_date('10/01/1998','%m/%d/%Y');等同于select date('1998-10-1'); #注意:1998-10-01; 98-10-01; 1998/10/01; 98/10/01,这四种输入格式能被MySQL直接识别自动转为date类型 #计算天数差,时分秒不算:datediff(date1,date2); #计算时间差,时分秒也计算:timediff(datetime1,datetime2);
#Value为true返回t,否则返回f:if(value,t,f); #Case when 条件 then 结果 when 条件 then 结果 …… else 结果 end;
#cast函数用于将值从一种数据类型转换为表达式中指定的另一种数据类型:cast(值 as 数据类型) #在使用cast函数时,可用的数据类型包括: # date:日期类型 # time:时间类型 # datetime:日期时间类型 # signed:有符号的int类型(有符号指的是正数负数) # char:定长字符串类型 # decimal:浮点型 #如:cast('2020-10-11' as date);表示将字符串'2020-10-11'转换成日期date类型。
约束(constraint)通常包括:
- 非空约束:not null
- 检查约束:check
- 唯一性约束:unique
- 主键约束:primary key
- 外键约束:foreign key
所有的约束都存储在一个系统表当中:table_constraints。这个系统表在这个数据库当中:information_schema
列级约束:在字段后面添加的约束
表级约束:一般在字段全部写完后,进行统一添加
#建表时添加:具体见上述建表语句。一般采用列级约束 #对已有表字段约束的修改(添加和删除): alter table 表名 modify 字段名 类型名 约束;#该语句还可以修改数据类型 #注意使用这条语句后,所得到的结果是该字段只会存在该语句列出的约束 #即:原来存在的约束,但该语句未列出,则删除该约束 #原来不存在的约束,但该语句列出,则添加该约束
非空约束(not null):限制该字段的数据不能为null
自增约束(auto_increment):实现数据的自动增长,由数据库维护
默认约束(default 默认值):保存数据时,如果未指定该字段的值,则采用默认值
检查约束(check(条件)):保证字段值满足某一个条件
#建表时添加:具体见上述建表语句,一般使用列级约束 #对已有表字段添加约束:alter table 表名 add constraint 约束名 check(条件表达式); #对已有表字段删除约束:alter table 表名 drop constraint 约束名;
唯一约束(unique):保证该字段的所有数据都是唯一、不重复的。唯一性的字段值是可以为NULL的。但不能重复。
#建表时添加:具体见上述建表语句,列级约束和表级约束都可以使用 #对已有表字段添加约束:alter table 表名 add constraint 约束名 unique(字段名); #对已有表字段删除约束:alter table 表名 drop constraint 约束名;
主键约束(primary key):
- 主键约束的字段不能为NULL,并且不能重复。即非空且唯一
- 该约束常与自增约束写一起
- 主键是一行数据的唯一标识,任何一张表都应该有主键,没有主键的表可以视为无效表。
- 主键值是这行记录的身份证号,是唯一标识。在数据库表中即使两条数据一模一样,但由于主键值不同,我们也会认为是两条完全的不同的数据。
- 主键分类:
- 根据字段数量分类:
- 单一主键(1个字段作为主键)==>建议的
- 复合主键(2个或2个以上的字段作为主键)
- 根据业务分类:
- 自然主键(主键和任何业务都无关,只是一个单纯的自然数据)===>建议的
- 业务主键(主键和业务挂钩,例如:银行卡账号作为主键)
- 单一主键:建议使用列级约束
- 复合主键:建议使用表级约束,不过该类型很少见
#建表时添加:具体见上述建表语句 #对已有表字段添加约束:alter table 表名 add primary key(字段名); #对已有表字段删除约束:alter table 表名 drop primary key;
外键约束(foreign key):用来让两张表的数据之间建立连接,保证数据的一致性和完整性
1、添加了外键约束的字段中的数据必须来自其他字段,不能随便填。
2、假设给a字段添加了外键约束,要求a字段中的数据必须来自b字段,b字段不一定是主键,但至少要有唯一性。
3、外键约束可以给单个字段添加,叫做单一外键。也可以给多个字段联合添加,叫做复合外键。复合外键很少用。
4、a表如果引用b表中的数据,可以把b表叫做父表,把a表叫做子表。
- 创建表时,先创建父表,再创建子表。
- 插入数据时,先插入父表,在插入子表。
- 删除数据时,先删除子表,再删除父表。
- 删除表时,先删除子表,再删除父表。
#建表时添加:具体见上述建表语句 #对已有表字段添加约束:alter table 表名 add [constraint 外键名] foreign key(字段名) references 主表名(主表字段名) [on update 更新行为] [on delete 删除行为]; #对已有表字段删除约束:alter table 表名 drop foreign key 外键名; #更新删除行为: #1、no action:当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新。(与restrict一致)默认行为 #2、restrict:当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新。(与no action一致)默认行为=====>推荐用于删除行为 #3、cascade:当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有,则也删除/更新外键在子表中的记录======>推荐用于更新行为 #4、set null:当在父表中删除对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键值为null(这就要求该外键允许取null) #5、set default:父表有变更时,子表将外键列设置成一个默认的值(Innodb不支持)
数据表三范式指:数据库表设计的原则。教你怎么设计数据库表有效,并且节省空间。
第一范式:任何一张表都应该有主键,每个字段是原子性的不能再分
第二范式:建立在第一范式基础上的,另外要求所有非主键字段完全依赖主键,不能产生部分依赖;其实就是只能有一个主键
第三范式:建立在第二范式基础上的,非主键字段不能传递依赖于主键字段
第一范式:任何一张表都应该有主键,每个字段是原子性的不能再分
- 以下表的设计不符合第一范式:无主键,并且联系方式可拆分。
- 应该这样设计:
第二范式:建立在第一范式基础上的,另外要求所有非主键字段完全依赖主键,不能产生部分依赖;其实就是只能有一个主键
以下表存储了学生和老师的信息,虽然符合第一范式,但是违背了第二范式,学生姓名、老师姓名都产生了部分依赖。导致数据冗余。
以下这种设计方式就是符合第二范式的:
第三范式:建立在第二范式基础上的,非主键字段不能传递依赖于主键字段
- 以下设计方式就是违背第三范式的,以上因为产生了传递依赖,导致班级名称冗余。
- 以下这种方式就是符合第三范式的:
一对多怎么设计
口诀:一对多两张表,多的表加外键。
多对多怎么设计
多对多三张表,关系表添加外键。
一对一怎么设计
两种方案:
- 第一种:主键共享
- 第二种:外键唯一
最终的设计原则
最终以满足客户需求为原则,有的时候会拿空间换速度。
视图(view)其实是当DQL语句太长时,不便于多次书写,所以可以将该DQL语句变为视图,需要使用时直接调用该视图即可。视图的本质其实是一张DQL语句生成的临时表
- 只能将select语句创建为视图。
- 视图可以隐藏表的字段名。
- 对视图增删改(DML:insert delete update)可以影响到原表数据。
- 如果开发中有一条非常复杂的SQL,而这个SQL在多处使用,会给开发和维护带来成本。使用视图可以降低开发和维护的成本。
#创建视图: create [or replace] view 视图名 as DQL语句; #删除视图 drop view [if exists] 视图名;
事务是一个最小的工作单元。一个业务的完成(银行转账)可能需要多条DML语句共同配合才能完成。在数据库当中,事务表示一件完整的事儿,其出现是为了保证在多条DML语句执行时同时成功或同时失败。
注意:事务只针对DML语句有效:因为只有这三个语句是改变表中数据的。
- 原子性(Atomicity):是指事务包含的所有操作要么全部成功,要么同时失败。
- 一致性(Consistency):是指事务开始前,和事务完成后,数据应该是一致的。例如张三和李四的钱加起来是5000,中间不管进行过多少次的转账操作(update),总量5000是不会变的。这就是事务的一致性。
- 隔离性(Isolation):隔离性是当多个⽤户并发访问数据库时,⽐如操作同⼀张表时,数据库为每⼀个⽤户开启的事务,不能被其他事务的操作所⼲扰,多个并发事务之间要相互隔离。
- 持久性(Durability):持久性是指⼀个事务⼀旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
#MySQL默认情况下采用的事务机制是:自动提交。所谓自动提交就是只要执行一条DML语句则提交一次。 #在dos命令窗口中关闭自动提交,并开启手动提交MySQL事务:start transaction或者begin; #回滚事务:rollback; #提交事务:commit; #只要执行以上的rollback或者commit,事务都会结束。
#mysql默认的隔离级别:可重复读(REPEATABLE READ)。 #设置事务隔离级别: #会话级(只对当前会话起作用):set session transaction isolation level 隔离级别英文; #全局级(对所有会话起作用):set global transaction isolation level 隔离级别英文; #每打开一次DOS命令窗口就是一次会话 #查看当前会话的隔离级别:select @@transaction_isolation; #查看全局的隔离级别:select @@gobal.transaction_isolation;
- 隔离级别从低到高排序:读未提交 < 读提交 < 可重复读 < 串行化
- ==脏读==:指的是**读取了另一个事务中的脏数据(Dirty Data),(即一个事务读取了另一个事务尚未提交的数据。)**在此情况下,如果另一个事务回滚了或者修改了这些数据,那么读取这些脏数据的事务所处理的数据就是不准确的。
- ==不可重复读==:指**在一个事务内,多次读取同一个数据行,得到的结果可能是不一样的(即同一行数据重复读取结果不同)。**这是由于其他事务对数据行做出了修改操作,导致数据的不一致性。
- ==幻读==:指在事务执行过程中,前后两次相同的查询条件得到的结果集不一致,可能会变多或变少(即相同查询语句查询的结果条数不一致。)
- 不同隔离级别会存在不同的现象,现象按照严重性从高到低排序:脏读 > 不可重复读 > 幻读
读未提交(read uncommitted)
- 演示:
- 第一步:进入MySQL,开启全局级"read uncommitted"事务隔离级别
- 第二步:退出MySQL重进,打开两个会话窗口,查询当前会话级别是否是"read uncommitted"
- 第三步:两窗口都关闭自动提交事务,打开手动提交事务
- 第四步:在A窗口查询某数据,在B窗口更改该数据(增加或删除或修改)
- 第五步:在A和B窗口都还未手动提交前,再次在A窗口查询该数据,发现此数据已被增加(幻读现象)或删除(幻读现象)或修改(不可重复读现象);由于此时B窗口事务未提交,但A窗口却能查询到(脏读现象)。
读提交(read committed)
- 演示:
- 第一步:进入MySQL,开启全局级"read committed"事务隔离级别
- 第二步:退出MySQL重进,打开两个会话窗口,查询当前会话级别是否是"read committed"
- 第三步:两窗口都关闭自动提交事务,打开手动提交事务
- 第四步:在A窗口查询某数据,在B窗口更改该数据(增加或删除或修改),此时在A窗口再次查询该数据发现该数据还未更改,与先前一样(无脏读现象)
- 第五步:此时B窗口提交事务,而A窗口不提交,再次在A窗口查询该数据,发现该数据已被增加(幻读现象)或删除(幻读现象)或修改(不可重复读现象)
可重复读(repeatable read)
- 演示:
- 第一步:进入MySQL,开启全局级"repeatable read"事务隔离级别
- 第二步:退出MySQL重进,打开两个会话窗口,查询当前会话级别是否是"repeatable read"
- 第三步:两窗口都关闭自动提交事务,打开手动提交事务
- 第四步:在A窗口查询某数据,在B窗口更改该数据(增加或删除或修改),此时在A窗口再次查询该数据发现该数据还未更改,与先前一样(无脏读现象)
- 第五步:此时B窗口提交事务,而A窗口不提交,再次在A窗口查询该数据,发现该数据还未更改,与先前一样(可重复读现象+MySQL会在该隔离级别尽可能避免幻读现象),而当再次在A窗口查询该数据时在查询语句后面加'for update'发现数据增加(幻读现象)或删除(幻读现象)或修改(不可重复读现象)
串行化(serializable)
- 演示:
- 第一步:进入MySQL,开启全局级"serializable"事务隔离级别
- 第二步:退出MySQL重进,打开两个会话窗口,查询当前会话级别是否是"serializable"
- 第三步:两窗口都关闭自动提交事务,打开手动提交事务
- 第四步:在A窗口查询某数据,在B窗口更改该数据(增加或删除或修改),此时发现B窗口无法向下执行,光标一直停留等A窗口事务提交或回滚之后才继续往下执行
可重复读的细节
在上面讲解过程中我提到,MySQL默认的隔离级别可重复读,在很大程度上避免了幻读问题(并不能完全解决),那么它是如何解决幻读问题的呢,解决方案包括两种:
- 快照读(普通的select语句):通过MVCC(多版本并发控制)方式解决了幻读,实现的方式是开始事务后,在执行第一个查询语句后,会创建一个 Read View,后续的查询语句利用这个 Read View,通过这个 Read View 就可以在 undo log 版本链找到事务开始时的数据,所以事务过程中每次查询的数据都是一样的,即使中途有其他事务插入了新纪录,是查询不出来这条数据的,所以就很好的避免了幻读问题。
开始事务后,在执行第一个快照读后,不能使用当前读语句了,否则如果其他事务刚好在使用当前读语句前提交了修改的数据,那么两次查询的结果不同(幻读现象)
开始事务后,在执行第一个快照读后,不能使用DML语句了,因为在执行DML语句前会默认自动执行一次当前读操作,如果其他事务刚好在使用DML语句前提交了修改的数据,那么之后不管你再用快照读还是当前读,此时查询的结果与一开始查询的结果不同(幻读现象)
- 当前读(select ... for update 等语句):包括DML语句(在执行DML语句前会默认自动执行一次当前读操作)和select...for update。通过 next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行 select ... for update 语句的时候,会对查询范围内的数据进行加锁,不允许其它事务对这个范围内的数据进行增删改。也就是说这个select语句范围内的数据是不允许并发的,只能排队执行,从而避免幻读问题。select...for update加的锁叫做:next-key lock。我们可以称其为:间隙锁 + 记录锁。间隙锁用来保证在锁定的范围内不允许insert操作。记录锁用来保证在锁定的范围内不允许delete和update操作。
- 总结:MySQL 可重复读隔离级别并没有彻底解决幻读,只是很大程度上避免了幻读现象的发生。要避免这类特殊场景下发生幻读的现象的话,就是尽量在开启事务之后,马上执行 select ... for update 这类当前读的语句,因为它会对记录加 next-key lock,从而避免其他事务插入一条新记录。