MySQL
数据库与数据库管理系统
数据库的相关概念
为什么要使用数据库
- 持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用。大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,而持久化的实现过程大多通过各种关系数据库来完成。
- 持久化的主要作用是将内存中的数据存储在关系型数据库中,当然也可以存储在磁盘文件、XML数据文件中。
数据库的相关概念
DB数据库(Database) |
---|
即存储数据的“仓库”,其本质是一个文件系统。它保存了一系列有组织的数据。 |
DBMS:数据库管理系统(Database Management System) |
是一种操纵和管理数据库的大型软件,用于建立、使用和维护数据库,对数据库进行统一管理和控制。用户通过数据库管理系统访问数据库中表内的数据。 |
SQL:结构化查询语言(Structured Query Language) |
专门用来与数据库通信的语言。 |
RDBMS与非RDBMS
**关系型数据库模型**是把复杂的数据结构归结为简单的二元关系 (即二维表格形式)。
关系型数据库以 行(row) 和 列(column) 的形式存储数据,以便于用户理解。这一系列的行和列被称为表(table),一组表组成了一个库(database)。
表与表之间的数据记录有关系(relationship)。现实世界中的各种实体以及实体之间的各种联系均用关系模型 来表示。关系型数据库,就是建立在关系模型基础上的数据库。
SQL 就是关系型数据库的查询语言。
非关系型数据库,可看成传统关系型数据库的功能 阉割版本 ,基于键值对存储数据,不需要经过SQL层的解析, 性能非常高 。同时,通过减少不常用的功能,进一步提高性能。
非关系型数据库的类型
键值型数据库
键值型数据库通过 Key-Value 键值的方式来存储数据,其中 Key 和 Value 可以是简单的对象,也可以是复杂的对象。Key 作为唯一的标识符,优点是查找速度快,在这方面明显优于关系型数据库,缺点是无法像关系型数据库一样使用条件过滤(比如 WHERE),如果你不知道去哪里找数据,就要遍历所有的键,这就会消耗大量的计算。
键值型数据库典型的使用场景是作为内存缓存 。 Redis 是最流行的键值型数据库
文档型数据库
此类数据库可存放并获取文档,可以是XML、JSON等格式。在数据库中文档作为处理信息的基本单位,一个文档就相当于一条记录。文档数据库所存放的文档,就相当于键值数据库所存放的“值”。MongoDB是最流行的文档型数据库。此外,还有CouchDB
等。
搜索引擎数据库
虽然关系型数据库采用了索引提升检索效率,但是针对全文索引效率却较低。搜索引擎数据库是应用在搜索引擎领域的数据存储形式,由于搜索引擎会爬取大量的数据,并以特定的格式进行存储,这样在检索的时候才能保证性能最优。核心原理是“倒排索引”。
典型产品:Solr、Elasticsearch、Splunk 等。
列式数据库
列式数据库是相对于行式存储的数据库(即关系型数据库),Oracle、MySQL、SQL Server 等数据库都是采用的行式存储(Row-based),而列式数据库是将数据按照列存储到数据库中,**这样做的好处是可以大量降低系统的I/O,适合于分布式文件系统,不足在于功能相对有限。典型产品:HBase等。**
- 要读来自于IO的数据,即直接从硬盘加载到内存中的,跟这个数据本身就在内存中相比,前者速度要慢得多得多。所以说有过多的冗余字段采用这种行式存储的方式容易字段过多,占用内存当然就多了,也就意味着我们加载更多的冗余字段进来了,就意味着有可能会出现过多的IO,那么这时候性能肯定就不会高。
图形数据库
NoSQL 对 SQL 做出了很好的补充,比如实际开发中,有很多业务需求,其实并不需要完整的关系型数据库功能,非关系型数据库的功能就足够使用了。这种情况下,使用 性能更高 、 成本更低 的非关系型数据库当然是更明智的选择。比如:日志收集、排行榜、定时器等。
关系型数据库设计规则
- 关系型数据库的典型数据结构就是
数据表
,这些数据表的组成都是结构化的(Structured)。 - 将数据放到表中,表再放到库中。
- 一个数据库中可以有多个表,每个表都有一个名字,用来标识自己。表名具有唯一性。
- 表具有一些特性,这些特性定义了数据在表中如何存储,类似Java和Python中“类”的设计。
记录、字段
E-R (entity-relationship,实体-联系)模型中有三个主要概念是:
实体集、属性、联系集
。一个实体集(class)对应于数据库中的一个表(table),一个实体(instance)则对应于数据库表中的一行(row),也称为一条记录(record)。一个属性(attribute)对应于数据库表中的一列(column),也称为一个字段(field)。
库可以看作data中的文件夹,表可以看作文件夹中的文件
ORM思想,即对象关系映射,Object Relational Mapping |
表的关联关系
- 表与表之间的数据记录有关系(relationship)。现实世界中的各种实体以及实体之间的各种联系均用关系模型来表示。
- 四种:一对一关联、一对多关联、多对多关联、自我引用
一对一关联(one-to-one)
- 在实际的开发中应用不多,因为一对一可以创建成一张表。
- 举例:设计学生表 :学号、姓名、手机号码、班级、系别、身份证号码、家庭住址、籍贯、紧急联系人、…
- 拆为两个表:两个表的记录是一一对应关系。
- 基础信息表 (常用信息):学号、姓名、手机号码、班级、系别
- 档案信息表 (不常用信息):学号、身份证号码、家庭住址、籍贯、紧急联系人、…
- 两种建表原则:
- 外键唯一:主表的主键和从表的外键(唯一),形成主外键关系,外键唯一。
- 外键是主键:主表的主键和从表的主键,形成主外键关系。
一对多关系(one-to-many****)
常见实例场景:客户表和订单表 ,分类表和商品表 ,部门表和员工表 。
举例:员工表:编号、姓名、…、所属部门
部门表:编号、名称、简介
一对多建表原则:在从表(多方)创建一个字段,字段作为外键指向主表(一方)的主键
多对多
要表示多对多关系,必须创建第三个表,该表通常称为 联接表 ,它将多对多关系划分为两个一对多关系。将这两个表的主键都插入到第三个表中。
自我引用(Self reference)
基本的SELECT语句
SQL的分类
DDL:数据定义语言。CREATE \ ALTER \ DROP \ RENAME \ TRUNCATE |
SQL语言的规则和规范
2.1 SQL的规则 —-必须要遵守
- SQL 可以写在一行或者多行。为了提高可读性,各子句分行写,必要时使用缩进
- 每条命令以 ; 或 \g 或 \G 结束
- 关键字不能被缩写也不能分行
- 关于标点符号
- 必须保证所有的()、单引号、双引号是成对结束的
- 必须使用英文状态下的半角输入方式
- 字符串型和日期时间类型的数据可以使用单引号(’ ‘)表示
- 列的别名,尽量使用双引号(” “),而且不建议省略as
2.2 SQL的规范 —-建议遵守
- MySQL 在 Windows 环境下是大小写不敏感的
- MySQL 在 Linux 环境下是大小写敏感的
- 数据库名、表名、表的别名、变量名是严格区分大小写的
- 关键字、函数名、列名(或字段名)、列的别名(字段的别名) 是忽略大小写的。
- 推荐采用统一的书写规范:
- 数据库名、表名、表别名、字段名、字段别名等都小写
- SQL 关键字、函数名、绑定变量等都大写
MySQL的三种注释的方式
/* |
导入现有的数据表、表的数据。
方式1:source 文件的全路径名举例:source d:\atguigudb.sql;
方式2:基于具体的图形化界面的工具可以导入数据
比如:SQLyog中 选择 “工具” -- “执行sql脚本” -- 选中xxx.sql即可。
#5. 最基本的SELECT语句: SELECT 字段1,字段2,… FROM 表名
SELECT 1 + 1,3 * 2;
SELECT 1 + 1,3 * 2
FROM DUAL; #dual:伪表
表中的所有的字段(或列)
SELECT * FROM employees;
SELECT employee_id,last_name,salary
FROM employees;
列的别名
as:全称:alias(别名),可以省略
列的别名可以使用一对""引起来,不要使用''。
SELECT employee_id emp_id,last_name AS lname,department_id “部门id”,salary * 12 AS “annual sal”
FROM employees;
去除重复行
#查询员工表中一共有哪些部门id呢? |
空值参与运算
1. 空值:null |
着重号 ``
SELECT * FROM `order`; |
查询常数
SELECT ‘尚硅谷’,123,employee_id,last_name
FROM employees;
显示表结构
DESCRIBE employees; #显示了表中字段的详细信息 |
过滤数据
#练习:查询90号部门的员工信息 |
运算符
算术运算符: + - * / div % mod
SELECT 100, 100 + 0, 100 - 0, 100 + 50, 100 + 50 * 30, 100 + 35.5, 100 - 35.5 |
比较运算符
#2.1 = <=> <> != < <= > >= |
① IS NULL \ IS NOT NULL \ ISNULL
#练习:查询表中commission_pct为null的数据有哪些SELECT last_name,salary,commission_pct
FROM employees
WHERE commission_pct IS NULL;
#或SELECT last_name,salary,commission_pct
FROM employees
WHERE ISNULL(commission_pct);
#练习:查询表中commission_pct不为null的数据有哪些
SELECT last_name,salary,commission_pct |
#或 |
② LEAST() \ GREATEST 最小最大
SELECT LEAST('g','b','t','m'),GREATEST('g','b','t','m') |
③ BETWEEN (条件下界1) AND (条件上界2) (查询条件1和条件2范围内的数据,包含边界)
查询工资在6000 到 8000的员工信息
SELECT employee_id,last_name,salary |
交换6000 和 8000之后,查询不到数据
SELECT employee_id,last_name,salary |
查询工资不在6000 到 8000的员工信息
SELECT employee_id,last_name,salary |
**#④ in (set)\ not in (set)**离散查找
练习:查询部门为10,20,30部门的员工信息
SELECT last_name,salary,department_id |
练习:查询工资不是6000,7000,8000的员工信息
SELECT last_name,salary,department_id |
⑤ LIKE :模糊查询
% : 代表不确定个数的字符 (0个,1个,或多个)
练习:查询last_name中包含字符’a’的员工信息
SELECT last_name |
练习:查询last_name中以字符’a’开头的员工信息
SELECT last_name |
练习:查询last_name中包含字符’a’且包含字符’e’的员工信息
#写法1: |
_ :代表一个不确定的字符
#练习:查询第3个字符是'a'的员工信息 |
⑥ REGEXP \ RLIKE :正则表达式
SELECT 'shkstart' REGEXP '^shk', 'shkstart' REGEXP 't$', 'shkstart' REGEXP 'hk' |
逻辑运算符: OR || AND && NOT ! XOR
or and
SELECT last_name,salary,department_id |
not
SELECT last_name,salary,department_id |
XOR :追求的”异”
SELECT last_name,salary,department_id |
注意:AND的优先级高于OR
位运算符: & | ^ ~ >> <<
SELECT 12 & 5, 12 | 5,12 ^ 5 |
排序与分页
排序
如果没有使用排序操作,默认情况下查询返回的数据是按照添加数据的顺序显示的。
SELECT * FROM employees;
基本使用
使用 ORDER BY
对查询到的数据进行排序操作。
升序:ASC (ascend)
降序:DESC (descend)
练习:按照salary从高到低的顺序显示员工信息
SELECT employee_id,last_name,salary |
练习:按照salary从低到高的顺序显示员工信息
SELECT employee_id,last_name,salary |
如果在ORDER BY 后没有显式指名排序的方式的话,则默认按照升序排列。
1.我们可以使用列的别名,进行排序
SELECT employee_id,salary,salary * 12 annual_sal |
2.列的别名只能在 ORDER BY 中使用,不能在WHERE中使用。
#如下操作报错!
SELECT employee_id,salary,salary * 12 annual_sal |
3.强调格式:WHERE 需要声明在FROM后,ORDER BY之前。
第三步 |
二级排序
练习:显示员工信息,按照department_id的降序排列,salary的升序排列
💗可以使用不在SELECT
💗列表中的列排序。
💗在对多列进行排序的时候,首先排序的第一列必须有相同的列值,才会对第二列进行排序。如果第一列数据中所有值都是唯一的,将不再对第二列进行排序。
SELECT employee_id,salary,department_id |
分页
2.1 mysql使用limit实现数据的分页显示
#需求1:每页显示20条记录,此时显示第1页 |
需求:每页显示pageSize条记录,此时显示第pageNo页:
公式:LIMIT (pageNo-1) * pageSize,pageSize;
2.2 WHERE … ORDER BY …LIMIT 声明顺序如下:
LIMIT的格式: 严格来说:LIMIT 位置偏移量,条目数 |
练习:表里有107条数据,我们只想要显示第 32、33 条数据怎么办呢?
SELECT employee_id,last_name |
2.3 MySQL8.0新特性:LIMIT … OFFSET …
练习:表里有107条数据,我们只想要显示第 32、33 条数据怎么办呢?
SELECT employee_id,last_name |
练习:查询员工表中工资最高的员工信息
SELECT employee_id,last_name,salary |
2.4 LIMIT 可以使用在MySQL、PGSQL、MariaDB、SQLite 等数据库中使用,表示分页。
不能使用在SQL Server、DB2、Oracle!
多表查询
/* |
🚩熟悉常见的几个表
DESC employees; |
🚩查询员工名为’Abel’的人在哪个城市工作?
SELECT * |
🚩出现笛卡尔积的错误
笛卡尔乘积是一个数学运算。假设我有两个集合 X 和 Y,那么 X 和 Y 的笛卡尔积就是 X 和 Y 的所有可能组合,也就是第一个对象来自于 X,第二个对象来自于 Y 的所有可能。组合的个数即为两个集合中元素个数的乘积数。
⛔错误的原因:缺少了多表的连接条件 |
🚩多表查询的正确方式:需要有连接条件
SELECT employee_id,department_name |
🚩如果查询语句中出现了多个表中都存在的字段,则必须指明此字段所在的表。
SELECT employees.employee_id,departments.department_name,employees.department_id |
#建议:从sql优化的角度,建议多表查询时,每个字段前都指明其所在的表。
🚩可以给表起别名,在SELECT和WHERE中使用表的别名。
SELECT emp.employee_id,dept.department_name,emp.department_id |
🚩结论:如果有n个表实现多表的查询,则需要至少n-1个连接条件
#练习:查询员工的employee_id,last_name,department_name,city |
🚩 多表查询的分类
/* |
等值连接 vs 非等值连接
🚩非等值连接的例子:
SELECT * |
🚩 自连接 vs 非自连接
SELECT * FROM employees; |
🚩 内连接 vs 外连接
内连接:合并具有同一列的两个以上的表的行, 结果集中不包含一个表与另一个表不匹配的行
SELECT employee_id,department_name
FROM employees e,departments d
WHERE e.department_id
= d.department_id; # 只有106条记录
外连接:合并具有同一列的两个以上的表的行, 结果集中除了包含一个表与另一个表匹配的行之外,还查询到了左表 或 右表中不匹配的行。
外连接的分类:左外连接、右外连接、满外连接
左外连接:两个表在连接过程中除了返回满足连接条件的行以外还返回左表中不满足条件的行,这种连接称为左外连接。
右外连接:两个表在连接过程中除了返回满足连接条件的行以外还返回右表中不满足条件的行,这种连接称为右外连接。
#练习:查询所有的员工的last_name,department_name信息 |
SQL92语法实现内连接:见上,略
SQL92语法实现外连接:使用 + ———-MySQL不支持SQL92语法中外连接的写法!
不支持:
SELECT employee_id,department_name |
SQL99语法中使用 JOIN …ON 的方式实现多表的查询。这种方式也能解决外连接的问题。MySQL是支持此种方式的。
SQL99语法如何实现多表的查询。
SQL99语法实现内连接:
SELECT last_name,department_name |
SQL99语法实现外连接:
练习:查询所有的员工的last_name,department_name信息 |
🚩7种JOIN的实现:
UNION的使用
合并查询结果 利用UNION关键字,可以给出多条SELECT语句,并将它们的结果组合成单个结果集。合并时,两个表对应的列数和数据类型必须相同,并且相互对应。各个SELECT语句之间使用UNION或UNION ALL关键字分隔。
语法格式:
SELECT column,... FROM table1 |
UNION操作符
UNION
操作符返回两个查询的结果集的并集,去除重复记录。
UNION ALL操作符
UNION ALL
操作符返回两个查询的结果集的并集。对于两个结果集的重复部分,不去重
举例:查询部门编号>90或邮箱包含a的员工信息
#方式1 |
🚩 UNION 和 UNION ALL的使用
UNION:会执行去重操作
UNION ALL:不会执行去重操作
结论:如果明确知道合并数据后的结果数据不存在重复数据,或者不需要去除重复的数据,
则尽量使用UNION ALL语句,以提高数据查询的效率。
🥇中图:内连接 |
🚩 SQL99语法的新特性1:自然连接NATURAL JOIN
SELECT employee_id,last_name,department_name |
🚩SQL99语法的新特性2:USING
SELECT employee_id,last_name,department_name |
🚩拓展:
SELECT last_name,job_title,department_name |
小结
表连接的约束条件可以有三种方式:WHERE, ON, USING
WHERE
:适用于所有关联查询ON
:只能和JOIN一起使用,只能写关联条件。虽然关联条件可以并到WHERE
中和其他条件一起写,但分开写可读性更好。USING
:只能和JOIN一起使用,而且要求两个关联字段在关联表中名称一致,而且只能表示关联字段值相等
单行函数
基本函数
函数 | |
---|---|
ABS(x) | 返回x的绝对值 |
SIGN(X) | 返回X的符号。正数返回1,负数返回-1,0返回0 |
PI() | 返回圆周率的值 |
CEIL(x),CEILING(x) | 返回大于或等于某个值的最小整数 |
FLOOR(x) | 返回小于或等于某个值的最大整数 |
LEAST(e1,e2,e3…) | 返回列表中的最小值 |
GREATEST(e1,e2,e3…) | 返回列表中的最大值 |
MOD(x,y) | 返回X除以Y后的余数 |
RAND() | 返回0~1的随机值 |
RAND(x) | 返回0~1的随机值,其中x的值用作种子值,相同的X值会产生相同的随机数 |
ROUND(x) | 返回一个对x的值进行四舍五入后,最接近于X的整数 |
ROUND(x,y) | 返回一个对x的值进行四舍五入后最接近X的值,并保留到小数点后面Y位 |
TRUNCATE(x,y) | 返回数字x截断为y位小数的结果 |
SQRT(x) | 返回x的平方根。当X的值为负数时,返回NULL |
数值函数
基本的操作
SELECT ABS(-123),ABS(32),SIGN(-23),SIGN(43),PI(),CEIL(32.32),CEILING(-43.23),FLOOR(32.32), |
取随机数
SELECT RAND(),RAND(),RAND(10),RAND(10),RAND(-1),RAND(-1) |
四舍五入,截断操作
SELECT ROUND(123.556),ROUND(123.456,0),ROUND(123.456,1),ROUND(123.456,2), |
单行函数可以嵌套
SELECT TRUNCATE(ROUND(123.456,2),0) |
角度与弧度的互换
函数 | 用法 |
---|---|
RADIANS(x) | 将角度转化为弧度,其中,参数x为角度值 |
DEGREES(x) | 将弧度转化为角度,其中,参数x为弧度值 |
SELECT RADIANS(30),RADIANS(45),RADIANS(60),RADIANS(90), |
三角函数
函数 | 用法 |
---|---|
SIN(x) | 返回x的正弦值,其中,参数x为弧度值 |
ASIN(x) | 返回x的反正弦值,即获取正弦为x的值。如果x的值不在-1到1之间,则返回NULL |
COS(x) | 返回x的余弦值,其中,参数x为弧度值 |
ACOS(x) | 返回x的反余弦值,即获取余弦为x的值。如果x的值不在-1到1之间,则返回NULL |
TAN(x) | 返回x的正切值,其中,参数x为弧度值 |
ATAN(x) | 返回x的反正切值,即返回正切值为x的值 |
ATAN2(m,n) | 返回两个参数的反正切值 |
COT(x) | 返回x的余切值,其中,X为弧度值 |
SELECT SIN(RADIANS(30)),DEGREES(ASIN(1)),TAN(RADIANS(45)),DEGREES(ATAN(1)) |
指数和对数
函数 | 用法 |
---|---|
POW(x,y),POWER(X,Y) | 返回x的y次方 |
EXP(X) | 返回e的X次方,其中e是一个常数,2.718281828459045 |
LN(X),LOG(X) | 返回以e为底的X的对数,当X <= 0 时,返回的结果为NULL |
LOG10(X) | 返回以10为底的X的对数,当X <= 0 时,返回的结果为NULL |
LOG2(X) | 返回以2为底的X的对数,当X <= 0 时,返回NULL |
SELECT POW(2,5),POWER(2,4),EXP(2) |
进制间的转换
函数 | 用法 |
---|---|
BIN(x) | 返回x的二进制编码 |
HEX(x) | 返回x的十六进制编码 |
OCT(x) | 返回x的八进制编码 |
CONV(x,f1,f2) | 返回f1进制数变成f2进制数 |
SELECT BIN(10),HEX(10),OCT(10),CONV(10,10,8) |
字符串函数
函数 | |
---|---|
ASCII(S) | 返回字符串S中的第一个字符的ASCII码值 |
CHAR_LENGTH(s) | 返回字符串s的字符数。作用与CHARACTER_LENGTH(s)相同 |
LENGTH(s) | 返回字符串s的字节数,和字符集有关 |
CONCAT(s1,s2,……,sn) | 连接s1,s2,……,sn为一个字符串 |
CONCAT_WS(x,s1,s2,……,sn) | 同CONCAT(s1,s2,…)函数,但是每个字符串之间要加上x |
REPLACE(str, a, b) | 用字符串b替换字符串str中所有出现的字符串a |
INSERT(str, idx, len,replacestr) | 将字符串str从第idx位置开始,len个字符长的子串替换为字符串replacestr |
UPPER(s) 或 UCASE(s) | 将字符串s的所有字母转成大写字母 |
LOWER(s) 或LCASE(s) | 将字符串s的所有字母转成小写字母 |
LEFT(str,n) | 返回字符串str最左边的n个字符 |
RIGHT(str,n) | 返回字符串str最右边的n个字符 |
LPAD(str, len, pad) | 用字符串pad对str最左边进行填充,直到str的长度为len个字符 |
RPAD(str ,len, pad) | 用字符串pad对str最右边进行填充,直到str的长度为len个字符 |
LTRIM(s) | 去掉字符串s左侧的空格 |
RTRIM(s) | 去掉字符串s右侧的空格 |
TRIM(s) | 去掉字符串s开始与结尾的空格 |
TRIM(s1 FROM s) | 去掉字符串s开始与结尾的s1 |
TRIM(LEADING s1FROM s) | 去掉字符串s开始处的s1 |
TRIM(TRAILING s1FROM s) | 去掉字符串s结尾处的s1 |
REPEAT(str, n) | 返回str重复n次的结果 |
SPACE(n) | 返回n个空格 |
STRCMP(s1,s2) | 比较字符串s1,s2的ASCII码值的大小,>0表示s1大, <0 表示s2大 |
SUBSTR(s,index,len) | 返回从字符串s的index位置其len个字符,作用与SUBSTRING(s,n,len)、MID(s,n,len)相同 |
LOCATE(substr,str) | 返回字符串substr在字符串str中首次出现的位置,作用于POSITION(substrIN str)、INSTR(str,substr)相同。未找到,返回0 |
ELT(m,s1,s2,…,sn) | 返回指定位置的字符串,如果m=1,则返回s1,如果m=2,则返回s2,如果m=n,则返回sn |
FIELD(s,s1,s2,…,sn) | 返回字符串s在字符串列表中第一次出现的位置 |
FIND_IN_SET(s1,s2) | 返回字符串s1在字符串s2中出现的位置。其中,字符串s2是一个以逗号分隔的字符串 |
REVERSE(s) | 返回s反转后的字符串 |
NULLIF(value1,value2) | 比较两个字符串,如果value1与value2相等,则返回NULL,否则返回value1 |
SELECT ASCII('Abcdfsf'),CHAR_LENGTH('hello'),CHAR_LENGTH('我们'), |
字符串的索引是从1开始的!
SELECT INSERT('helloworld',2,3,'aaaaa'),REPLACE('hello','lol','mmm') |
LPAD:实现右对齐效果
RPAD:实现左对齐效果
SELECT employee_id,last_name,LPAD(salary,10,' ') |
日期和时间函数
获取日期、时间
函数 | 用法 |
---|---|
CURDATE() ,CURRENT_DATE() | 返回当前日期,只包含年、月、日 |
CURTIME() , CURRENT_TIME() | 返回当前时间,只包含时、分、秒 |
NOW() / SYSDATE() / CURRENT_TIMESTAMP() / LOCALTIME() /LOCALTIMESTAMP() | 返回当前系统日期和时间 |
UTC_DATE() | 返回UTC(世界标准时间)日期 |
UTC_TIME() | 返回UTC(世界标准时间)时间 |
SELECT CURDATE(),CURRENT_DATE(),CURTIME(),NOW(),SYSDATE(), |
💖日期与时间戳的转换(把指定的日期转换成毫秒数)
函数 | 用法 |
---|---|
UNIX_TIMESTAMP() | 以UNIX时间戳的形式返回当前时间。SELECT UNIX_TIMESTAMP() ->1634348884 |
UNIX_TIMESTAMP(date) | 将指定时间date以UNIX时间戳的形式返回。 |
FROM_UNIXTIME(timestamp) | 将UNIX时间戳的时间转换为普通格式的时间 |
SELECT UNIX_TIMESTAMP(),UNIX_TIMESTAMP('2021-10-01 12:12:32'), |
获取月份、星期、星期数、天数等函数
函数 | 用法 |
---|---|
YEAR(date) / MONTH(date) / DAY(date) | 返回具体的日期值 |
HOUR(time) / MINUTE(time) /SECOND(time) | 返回具体的时间值 |
MONTHNAME(date) | 返回月份:January,… |
DAYNAME(date) | 返回星期几:MONDAY,TUESDAY…..SUNDAY |
WEEKDAY(date) | 返回周几,注意,周1是0,周2是1,。。。周日是6 |
QUARTER(date) | 返回日期对应的季度,范围为1~4 |
WEEK(date) , WEEKOFYEAR(date) | 返回一年中的第几周 |
DAYOFYEAR(date) | 返回日期是一年中的第几天 |
DAYOFMONTH(date) | 返回日期位于所在月份的第几天 |
DAYOFWEEK(date) | 返回周几,注意:周日是1,周一是2,。。。周六是7 |
SELECT YEAR(CURDATE()),MONTH(CURDATE()),DAY(CURDATE()), |
日期的操作函数
函数 | 用法 |
---|---|
EXTRACT(type FROM date) | 返回指定日期中特定的部分,type指定返回的值 |
SELECT EXTRACT(SECOND FROM NOW()),EXTRACT(DAY FROM NOW()), |
时间和秒钟转换的函数
函数 | 用法 |
---|---|
TIME_TO_SEC(time) | 将 time 转化为秒并返回结果值。转化的公式为: 小时3600+分钟60+秒 |
SEC_TO_TIME(seconds) | 将 seconds 描述转化为包含小时、分钟和秒的时间 |
SELECT TIME_TO_SEC(CURTIME()), |
💖计算日期和时间的函数
函数 | 用法 |
---|---|
DATE_ADD(datetime, INTERVAL expr type),ADDDATE(date,INTERVAL expr type) | 返回与给定日期时间相差INTERVAL时间段的日期时间 |
DATE_SUB(date,INTERVAL expr type),SUBDATE(date,INTERVAL expr type) | 返回与date相差INTERVAL时间间隔的日期 |
函数 | 用法 |
---|---|
ADDTIME(time1,time2) | 返回time1加上time2的时间。当time2为一个数字时,代表的是秒 ,可以为负数 |
SUBTIME(time1,time2) | 返回time1减去time2后的时间。当time2为一个数字时,代表的是 秒 ,可以为负数 |
DATEDIFF(date1,date2) | 返回date1 - date2的日期间隔天数 |
TIMEDIFF(time1, time2) | 返回time1 - time2的时间间隔 |
FROM_DAYS(N) | 返回从0000年1月1日起,N天以后的日期 |
TO_DAYS(date) | 返回日期date距离0000年1月1日的天数 |
LAST_DAY(date) | 返回date所在月份的最后一天的日期 |
MAKEDATE(year,n) | 针对给定年份与所在年份中的天数返回一个日期 |
MAKETIME(hour,minute,second) | 将给定的小时、分钟和秒组合成时间并返回 |
PERIOD_ADD(time,n) | 返回time加上n后的时间 |
SELECT NOW(),DATE_ADD(NOW(),INTERVAL 1 YEAR), |
💖日期的格式化与解析
函数 | 用法 |
---|---|
DATE_FORMAT(date,fmt) | 按照字符串fmt格式化日期date值为字符串 |
TIME_FORMAT(time,fmt) | 按照字符串fmt格式化时间time值 |
GET_FORMAT(date_type,format_type) | 返回日期字符串的显示格式 |
STR_TO_DATE(str, fmt) | 按照字符串fmt对str进行解析,解析为一个日期 |
上述 非GET_FORMAT 函数中fmt参数常用的格式符:
符 | 说明 | 格式符 | 说明 |
---|---|---|---|
%Y | 4位数字表示年份 | %y | 表示两位数字表示年份 |
%M | 月名表示月份(January,….) | %m | 两位数字表示月份(01,02,03。。。) |
%b | 缩写的月名(Jan.,Feb.,….) | %c | 数字表示月份(1,2,3,…) |
%D | 英文后缀表示月中的天数(1st,2nd,3rd,…) | %d | 两位数字表示月中的天数(01,02…) |
%e | 数字形式表示月中的天数(1,2,3,4,5…..) | ||
%H | 两位数字表示小数,24小时制(01,02..) | %h和%I | 两位数字表示小时,12小时制(01,02..) |
%k | 数字形式的小时,24小时制(1,2,3) | %l | 数字形式表示小时,12小时制(1,2,3,4….) |
%i | 两位数字表示分钟(00,01,02) | %S和%s | 两位数字表示秒(00,01,02…) |
%W | 一周中的星期名称(Sunday…) | %a | 一周中的星期缩写(Sun., Mon.,Tues.,..) |
%w | 以数字表示周中的天数(0=Sunday,1=Monday….) | ||
%j | 以3位数字表示年中的天数(001,002…) | %U | 以数字表示年中的第几周, (1,2,3。。)其中Sunday为周中第一天 |
%u | 以数字表示年中的第几周, (1,2,3。。)其中Monday为周中第一天 | ||
%T | 24小时制 | %r | 12小时制 |
%p | AM或PM | %% | 表示% |
格式化:日期 ---> 字符串 |
流程控制函数
函数 | 用法 |
---|---|
IF(value,value1,value2) | 如果value的值为TRUE,返回value1,否则返回value2 |
IFNULL(value1, value2) | 如果value1不为NULL,返回value1,否则返回value2 |
CASE WHEN 条件1 THEN 结果1 WHEN 条件2 THEN 结果2…. [ELSE resultn] END | 相当于Java的if…else if…else… |
CASE expr WHEN 常量值1 THEN 值1 WHEN 常量值1 THEN值1 …. [ELSE 值n] END | 相当于Java的switch…case…,expr是需要判断的字段 |
IF(VALUE,VALUE1,VALUE2)
SELECT last_name,salary,IF(salary >= 6000,'高工资','低工资') "details" |
IFNULL(VALUE1,VALUE2):看做是IF(VALUE,VALUE1,VALUE2)的特殊情况
SELECT last_name,commission_pct,IFNULL(commission_pct,0) "details" |
CASE WHEN … THEN …WHEN … THEN … ELSE … END
类似于java的if ... else if ... else if ... else |
CASE … WHEN … THEN … WHEN … THEN … ELSE … END
类似于java的swich ... case... |
加密与解密的函数
加密与解密函数主要用于对数据库中的数据进行加密和解密处理,以防止数据被他人窃取。这些函数在保证数据库安全时非常有用。
PASSWORD(str) | 返回字符串str的加密版本,41位长的字符串。加密结果 不可逆 ,常用于用户的密码加密 |
---|---|
MD5(str) | 返回字符串str的md5加密后的值,也是一种加密方式。若参数为NULL,则会返回NULL |
SHA(str) | 从原明文密码str计算并返回加密后的密码字符串,当参数为NULL时,返回NULL。 SHA加密算法比MD5更加安全 。 |
ENCODE(value,password_seed) | 返回使用password_seed作为加密密码加密value |
DECODE(value,password_seed) | 返回使用password_seed作为加密密码解密value |
PASSWORD()在mysql8.0中弃用。 |
MySQL信息函数
VERSION() | 返回当前MySQL的版本号 |
---|---|
CONNECTION_ID() | 返回当前MySQL服务器的连接数 |
DATABASE(),SCHEMA() | 返回MySQL命令行当前所在的数据库 |
USER(),CURRENT_USER()、SYSTEM_USER(),SESSION_USER() | 返回当前连接MySQL的用户名,返回结果格式为“主机名@用户名” |
CHARSET(value) | 返回字符串value自变量的字符集 |
COLLATION(value) | 返回字符串value的比较规则 |
SELECT VERSION(),CONNECTION_ID(),DATABASE(),SCHEMA(), |
其他函数
函数 | 用法 |
---|---|
FORMAT(value,n) | 返回对数字value进行格式化后的结果数据。n表示 四舍五入 后保留到小数点后n位 |
CONV(value,from,to) | 将value的值进行不同进制之间的转换 |
INET_ATON(ipvalue) | 将以点分隔的IP地址转化为一个数字 |
INET_NTOA(value) | 将数字形式的IP地址转化为以点分隔的IP地址 |
BENCHMARK(n,expr) | 将表达式expr重复执行n次。用于测试MySQL处理expr表达式所耗费的时间 |
CONVERT(value USINGchar_code) | 将value所使用的字符编码修改为char_code |
#如果n的值小于或者等于0,则只保留整数部分 |
聚合函数
常见的几个聚合函数
1.1 AVG / SUM :只适用于数值类型的字段(或变量)
SELECT AVG(salary),SUM(salary),AVG(salary) * 107 |
1.2 MAX / MIN :适用于数值类型、字符串类型、日期时间类型的字段(或变量)
SELECT MAX(salary),MIN(salary) |
1.3 COUNT:
① 作用:计算指定字段在查询结构中出现的个数(不包含NULL值的)
SELECT COUNT(employee_id),COUNT(salary),COUNT(2 * salary),COUNT(1),COUNT(2),COUNT(*) |
#如果计算表中有多少条记录,如何实现?
#方式1:COUNT(*)
#方式2:COUNT(1)
#方式3:COUNT(具体字段) : 不一定对!
② 注意:计算指定字段出现的个数时,是不计算NULL值的。
SELECT COUNT(commission_pct) |
③ 公式:AVG = SUM / COUNT
SELECT AVG(salary),SUM(salary)/COUNT(salary), |
GROUP BY 的使用
#需求:查询各个部门的平均工资,最高工资 |
HAVING的使用 (作用:用来过滤数据的)
#练习:查询各个部门中最高工资比10000高的部门信息 |
SQL底层执行原理
#4.1 SELECT 语句的完整结构 |
SQL语句的执行过程:
#FROM …,…-> ON -> (LEFT/RIGNT JOIN) -> WHERE -> GROUP BY -> HAVING -> SELECT -> DISTINCT -> ORDER BY -> LIMIT
子查询
由一个具体的需求,引入子查询
#需求:谁的工资比Abel的高? |
称谓的规范:外查询(或主查询)、内查询(或子查询)
/* |
子查询的分类
角度1:从内查询返回的结果的条目数 |
单行子查询
单行操作符: = != > >= < <=
#题目:查询工资大于149号员工工资的员工的信息 |
子查询中的空值问题
SELECT last_name, job_id |
非法使用子查询
#错误:Subquery returns more than 1 row |
多行子查询
多行子查询的操作符: IN ANY ALL SOME(同ANY)
操作符 | 含义 |
---|---|
IN | 等于列表中的任意一个 |
ANY | 需要和单行比较操作符一起使用,和子查询返回的某一/任一个值比较 |
ALL | 需要和单行比较操作符一起使用,和子查询返回的所有值比较 |
SOME | 实际上是ANY的别名,作用相同,一般常使用ANY |
举例:
IN:
SELECT employee_id, last_name |
ANY / ALL:
#题目:返回其它job_id中比job_id为‘IT_PROG’部门任一工资低的员工的员工号、 |
5.3 空值问题
SELECT last_name |
相关子查询
#回顾:查询员工中工资大于公司平均工资的员工的last_name,salary和其department_id
6.1
SELECT last_name,salary,department_id |
6.2 EXISTS 与 NOT EXISTS关键字
#题目:查询公司管理者的employee_id,last_name,job_id,department_id信息 |
创建和管理表
SELECT *
FROM order;
创建和管理数据库
#1.1 如何创建数据库
#方式1: |
#1.2 管理数据库
#查看当前连接中的数据库都有哪些 |
#1.3 修改数据库
#更改数据库字符集 |
#1.4 删除数据库
#方式1:如果要删除的数据库存在,则删除成功。如果不存在,则报错 |
#2. 如何创建数据表
USE atguigudb; |
#方式1:”白手起家”的方式
CREATE TABLE IF NOT EXISTS myemp1( #需要用户具备创建表的权限。 |
#方式2:基于现有的表,同时导入数据
#基于查询语句创建的表 |
#3. 修改表 –> ALTER TABLE
DESC myemp1;
3.1 添加字段(一个)
ALTER TABLE myemp1 |
3.2 修改一个字段:数据类型、长度、默认值(略)
ALTER TABLE myemp1 |
3.3 重命名一个字段
ALTER TABLE myemp1 |
3.4 删除一个字段
ALTER TABLE myemp1
DROP COLUMN my_email;
#4. 重命名表
#方式1: |
#5. 删除表
#不光将表结构删除掉,同时表中的数据也删除掉,释放表空间 |
#6. 清空表
#清空表,表示清空表中的所有数据,但是表结构保留。 |
#7. DCL 中 COMMIT 和 ROLLBACK
COMMIT:提交数据。一旦执行COMMIT,则数据就被永久的保存在了数据库中,意味着数据不可以回滚。 |
#8. 对比 TRUNCATE TABLE 和 DELETE FROM
相同点:都可以实现对表中所有数据的删除,同时保留表结构。 |
#9.测试MySQL8.0的新特性:DDL的原子化
CREATE DATABASE mytest; |
内容拓展
拓展1:阿里巴巴《Java开发手册》之MySQL
字段命名【强制】表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
正例:aliyun_admin,rdc_config,level3_name
反例:AliyunAdmin,rdcConfig,level_3_name
【 强制 】禁用保留字,如 desc、range、match、delayed 等,请参考 MySQL 官方保留字。【 强制 】表必备三字段:id, gmt_create, gmt_modified。
说明:其中 id 必为主键,类型为BIGINT UNSIGNED、单表时自增、步长为 1。gmt_create, gmt_modified 的类型均为 DATETIME
类型,前者现在时表示主动式创建,后者过去分词表示被动式更新
【 推荐 】表的命名最好是遵循 “业务名称_表的作用”。正例:alipay_task 、 force_project、 trade_config
【 推荐 】库名与应用名称尽量一致。
【参考】合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度。
正例:无符号值可以避免误存负数,且扩大了表示范围
表删除
操作将把表的定义和表中的数据一起删除,并且MySQL在执行删除操作时,不会有任何的确认信息提示,因此执行删除操时应当慎重。在删除表前,最好对表中的数据进行 备份
,这样当操作失误时可以对数据进行恢复,以免造成无法挽回的后果。
同样的,在使用 ALTER TABLE
进行表的基本修改操作时,在执行操作过程之前,也应该确保对数据进行完整的 备份 ,因为数据库的改变是 无法撤销 的,如果添加了一个不需要的字段,可以将其删除;相同的,如果删除了一个需要的列,该列下面的所有数据都将会丢失。
拓展3:MySQL8新特性—DDL的原子化
在MySQL 8.0版本中,InnoDB表的DDL支持事务完整性,即 DDL操作要么成功要么回滚 。DDL操作回滚日志写入到data dictionary数据字典表mysql.innodb_ddl_log(该表是隐藏的表,通过show tables无法看到)中,用于回滚操作。通过设置参数,可将DDL操作日志打印输出到MySQL错误日志中。
分别在MySQL 5.7版本和MySQL 8.0版本中创建数据库和数据表,结果如下:
CREATE DATABASE mytest |
1)在MySQL 5.7版本中,测试步骤如下: 删除数据表book1和数据表book2,结果如下:
mysql> DROP TABLE book1,book2; |
再次查询数据库中的数据表名称,结果如下:
mysql> SHOW TABLES; |
从结果可以看出,虽然删除操作时报错了,但是仍然删除了数据表book1。
2)在MySQL 8.0版本中,测试步骤如下: 删除数据表book1和数据表book2
,结果如下:
mysql> DROP TABLE book1,book2; |
再次查询数据库中的数据表名称,结果如下:
mysql> show tables; |
从结果可以看出,数据表book1并没有被删除。
数据处理之增删改
#0. 储备工作
USE atguigudb; |
#1. 添加数据
#方式1:一条一条的添加数据
① 没有指明添加的字段 |
#方式2:将查询结果插入到表中
SELECT * FROM emp1; |
#2. 更新数据 (或修改数据)
UPDATE .... SET .... WHERE ... |
#3. 删除数据 DELETE FROM …. WHERE….
DELETE FROM emp1 |
#4. MySQL8的新特性:计算列
USE atguigudb; |
#5.综合案例
1、创建数据库test01_library |
MySQL数据类型精讲
MySQL中的数据类型
类型 | 类型举例 |
---|---|
整数类型 | TINYINT、SMALLINT、MEDIUMINT、INT(或INTEGER)、BIGINT |
浮点类型 | FLOAT、DOUBLE,会存在因四舍五入造成的精度损失 |
定点数类型 | DECIMAL |
位类型 | BIT |
日期时间类型 | YEAR、TIME、DATE、DATETIME、TIMESTAMP |
文本字符串类型 | CHAR、VARCHAR、TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT(处理文本数据) |
枚举类型 | ENUM |
集合类型 | SET |
二进制字符串类型 | BINARY、VARBINARY、TINYBLOB、BLOB、MEDIUMBLOB、LONGBLOB(处理非文本数据,比如图片、字节流之类) |
JSON类型 | JSON对象、JSON数组 |
空间数据类型 | 单值类型:GEOMETRY、POINT、LINESTRING、POLYGON;集合类型:MULTIPOINT、MULTILINESTRING、MULTIPOLYGON、GEOMETRYCOLLECTION |
常见数据类型的属性,如下:
MySQL关键字 | 含义 |
---|---|
NULL | 数据列可包含NULL值 |
NOT NULL | 数据列不允许包含NULL值 |
DEFAULT | 默认值 |
PRIMARY KEY | 主键 |
AUTO_INCREMENT | 自动递增,适用于整数类型 |
UNSIGNED | 无符号 |
CHARACTER SET name | 指定一个字符集 |
#创建数据库时指名字符集 |
类型介绍
类型介绍
整数类型一共有 5 种,包括 TINYINT、SMALLINT、MEDIUMINT、INT(INTEGER)和 BIGINT
。它们的区别如下表所示:
整数类型 | 字节 | 有符号数取值范围 | 无符号数取值范围 |
---|---|---|---|
TINYINT | 1 | -128~127 | 0~255 |
SMALLINT | 2 | -32768~32767 | 0~65535 |
MEDIUMINT | 3 | -8388608~8388607 | 0~16777215 |
INT、INTEGER | 4 | -2147483648~2147483647 | 0~4294967295 |
BIGINT | 8 | -9223372036854775808~9223372036854775807 | 0~18446744073709551615 |
可选属性
整数类型的可选属性有三个:
2.2.1 M
M : 表示显示宽度,M的取值范围是(0, 255)。例如,int(5):当数据宽度小于5位的时候在数字前面需要用字符填满宽度。该项功能需要配合“ ZEROFILL ”使用,表示用“0”
填满宽度,否则指定显示宽度无效。如果设置了显示宽度,那么插入的数据宽度超过显示宽度限制,会不会截断或插入失败?
答案:不会对插入的数据有任何影响,还是按照类型的实际宽度进行保存,即
显示宽度与类型可以存储的值范围无关 。从MySQL 8.0.17开始,整数数据类型不推荐使用显示宽度属性。整型数据类型可以在定义表结构时指定所需要的显示宽度,如果不指定,则系统为每一种类型指定默认的宽度值。举例:
CREATE TABLE test_int1 ( x TINYINT, y SMALLINT, z MEDIUMINT, m INT, n BIGINT );
查看表结构 (MySQL5.7中显式如下,MySQL8中不再显式范围)
mysql> desc test_int1; |
TINYINT有符号数和无符号数的取值范围分别为-128127和0255,由于负号占了一个数字位,因此TINYINT默认的显示宽度为4。同理,其他整数类型的默认显示宽度与其有符号数的最小值的宽度相同 .
举例:
f2 INT(5), |
mysql> SELECT * FROM test_int2; |
2.2.2 UNSIGNED
UNSIGNED : 无符号类型(非负),所有的整数类型都有一个可选的属性UNSIGNED(无符号属性),无符号整数类型的最小取值为0。所以,如果需要在MySQL数据库中保存非负整数值时,可以将整数类型设置为无符号类型。int类型默认显示宽度为int(11),无符号int类型默认显示宽度为int(10)。
2.2.3 ZEROFILLCREATE TABLE test_int3( |
2.2.3 ZEROFILL
ZEROFILL
: 0填充,(如果某列是ZEROFILL,那么MySQL会自动为当前列添加UNSIGNED属性),如果指定了ZEROFILL只是表示不够M位时,用0在左边填充,如果超过M位,只要不超过数据存储范围即可。
原来,在 int(M) 中,M 的值跟 int(M) 所占多少存储空间并无任何关系。 int(3)、int(4)、int(8) 在磁盘上都是占用 4 bytes 的存储空间。也就是说,int(M),必须和UNSIGNED ZEROFILL一起使用才有意义。如果整数值超过M位,就按照实际位数存储。只是无须再用字符 0 进行填充。
TINYINT
:一般用于枚举数据,比如系统设定取值范围很小且固定的场景。
SMALLINT
:可以用于较小范围的统计数据,比如统计工厂的固定资产库存数量等。
MEDIUMINT
:用于较大整数的计算,比如车站每日的客流量等。
INT、INTEGER
:取值范围足够大,一般情况下不用考虑超限问题, 用得最多。比如商品编号。
BIGINT
:只有当你处理特别巨大的整数时才会用到。比如双十一的交易量、大型门户网站点击量、证券公司衍生产品持仓等。
2.4 如何选择?
在评估用哪种整数类型的时候,你需要考虑 存储空间 和 可靠性 的平衡问题:一方 面,用占用字节数少的整数类型可以节省存储空间;另一方面,要是为了节省存储空间, 使用的整数类型取值范围太小,一旦遇到超出取值范围的情况,就可能引起 系统错误 ,影响可靠性。
举个例子,商品编号采用的数据类型是 INT。原因就在于,客户门店中流通的商品种类较多,而且,每天都有旧商品下架,新商品上架,这样不断迭代,日积月累。
如果使用 SMALLINT 类型,虽然占用字节数比 INT 类型的整数少,但是却不能保证数据不会超出范围65535。相反,使用 INT,就能确保有足够大的取值范围,不用担心数据超出范围影响可靠性的问题。
你要注意的是,在实际工作中,系统故障产生的成本远远超过增加几个字段存储空间所产生的成本。因此,我建议你首先确保数据不会超过取值范围,在这个前提之下,再去考虑如何节省存储空间。
浮点类型
类型介绍
浮点数和定点数类型的特点是可以 处理小数 ,你可以把整数看成小数的一个特例。因此,浮点数和定点数的使用场景,比整数大多了。 MySQL支持的浮点数类型,分别是 FLOAT、DOUBLE、REAL。
FLOAT :表示单精度浮点数;
DOUBLE 表示双精度浮点数;
REAL默认就是 DOUBLE。如果你把 SQL 模式设定为启用“ REAL_AS_FLOAT ”,那 么,MySQL 就认为REAL 是 FLOAT。如果要启用“REAL_AS_FLOAT”,可以通过以下 SQL 语句实现:
`SET sql_mode = “REAL_AS_FLOAT”;
问题1:FLOAT 和 DOUBLE 这两种数据类型的区别是啥呢?
问题2:为什么浮点数类型的无符号数取值范围,只相当于有符号数取值范围的一半,也就是只相当于有符号数取值范围大于等于零的部分呢?
MySQL 存储浮点数的格式为: 符号(S) 、 尾数(M) 和 阶码(E) 。因此,无论有没有符号,MySQL 的浮点数都会存储表示符号的部分。因此, 所谓的无符号数取值范围,其实就是有符号数取值范围大于等于零的部分。
数据精度说明
对于浮点类型,在MySQL中单精度值使用 4 个字节,双精度值使用 8 个字节。
MySQL允许使用
非标准语法
(其他数据库未必支持,因此如果涉及到数据迁移,则最好不要这么用):FLOAT(M,D) 或 DOUBLE(M,D) 。
这里,M称为 精度 ,D称为 标度 。(M,D)中 M=整数位+小数位,D=小数位。 D<=M<=255,0<=D<=30。例如,定义为FLOAT(5,2)的一个列可以显示为-999.99-999.99。如果超过这个范围会报错。
FLOAT和DOUBLE类型在不指定(M,D)时,默认会按照实际的精度(由实际的硬件和操作系统决定)来显示。
说明:浮点类型,也可以加 UNSIGNED ,但是不会改变数据范围,例如:FLOAT(3,2) UNSIGNED仍然只能表示0-9.99的范围。
不管是否显式设置了精度(M,D),这里MySQL的处理方案如下:
- 如果存储时,整数部分超出了范围,MySQL就会报错,不允许存这样的值
- 如果存储时,小数点部分若超出范围,就分以下情况:
- 若四舍五入后,整数部分没有超出范围,则只警告,但能成功操作并四舍五入删除多余的小数位后保存。例如在FLOAT(5,2)列内插入999.009,近似结果是999.01。
- 若四舍五入后,整数部分超出范围,则MySQL报错,并拒绝处理。如FLOAT(5,2)列内插入999.995和-999.995都会报错。
从MySQL 8.0.17开始,FLOAT(M,D) 和DOUBLE(M,D)用法在官方文档中已经明确不推荐使用,将来可能被移除。另外,关于浮点型FLOAT和DOUBLE的UNSIGNED也不推荐使用了,将来也可能被移除。
CREATE TABLE test_double1(
f1 FLOAT,
f2 FLOAT(5,2),
f3 DOUBLE,
f4 DOUBLE(5,2)
);
DESC test_double1;
INSERT INTO test_double1
VALUES(123.456,123.456,123.4567,123.45);
#Out of range value for column 'f2' at row 1
INSERT INTO test_double1
VALUES(123.456,1234.456,123.4567,123.45);
SELECT * FROM test_double1;浮点数类型有个缺陷,就是不精准。下面我来重点解释一下为什么 MySQL 的浮点数不够精准。比如,我们设计一个表,有f1这个字段,插入值分别为0.47,0.44,0.19,我们期待的运行结果是:0.47 + 0.44 + 0.19 = 1.1。而使用sum之后查询:
CREATE TABLE test_double2(
f1 DOUBLE
);
INSERT INTO test_double2
VALUES(0.47),(0.44),(0.19);mysql> SELECT SUM(f1)
-> FROM test_double2;
+--------------------+
| SUM(f1) |
+--------------------+
| 1.0999999999999999 |
+--------------------+
1 row in set (0.00 sec)mysql> SELECT SUM(f1) = 1.1,1.1 = 1.1
-> FROM test_double2;
+---------------+-----------+
| SUM(f1) = 1.1 | 1.1 = 1.1 |
+---------------+-----------+
| 0 | 1 |
+---------------+-----------+
1 row in set (0.00 sec)查询结果是 1.0999999999999999。看到了吗?虽然误差很小,但确实有误差。 你也可以尝试把数据类型改成 FLOAT,然后运行求和查询,得到的是, 1.0999999940395355。显然,误差更大了。
那么,为什么会存在这样的误差呢?问题还是出在 MySQL 对浮点类型数据的存储方式上。
MySQL 用 4 个字节存储 FLOAT 类型数据,用 8 个字节来存储 DOUBLE 类型数据。无论哪个,都是采用二进制的方式来进行存储的。比如 9.625,用二进制来表达,就是 1001.101,或者表达成 1.001101×2^3。如果尾数不是 0 或 5(比如 9.624),你就无法用一个二进制数来精确表达。进而,就只好在取值允许的范围内进行四舍五入。
在编程中,如果用到浮点数,要特别注意误差问题,
因为浮点数是不准确的,所以我们要避免使用“=”来判断两个数是否相等。同时,在一些对精确度要求较高的项目中,千万不要使用浮点数,不然会导致结果错误,甚至是造成不可挽回的损失。那么,MySQL 有没有精准的数据类型呢?当然有,这就是定点数类型:
DECIMAL
。
定点数类型
类型介绍
- **MySQL中的定点数类型只有 DECIMAL 一种类型。 **
DECIMAL(M,D),DEC,NUMERIC | M+2字节 | 有效范围由M和D决定 |
---|
使用 DECIMAL(M,D) 的方式表示高精度小数。其中,M被称为精度,D被称为标度。0<=M<=65, 0<=D<=30,D<M。例如,定义DECIMAL(5,2)的类型,表示该列取值范围是-999.99~999.99。
DECIMAL(M,D)的最大取值范围与DOUBLE类型一样,但是有效的数据范围是由M和D决定的。DECIMAL 的存储空间并不是固定的,由精度值M决定,总共占用的存储空间为M+2个字节。也就是说,在一些对精度要求不高的场景下,比起占用同样字节长度的定点数,浮点数表达的数值范围可以更大一些。
定点数在MySQL内部是以字符串的形式进行存储,这就决定了它一定是精准的。当DECIMAL类型不指定精度和标度时,其默认为DECIMAL(10,0)。当数据的精度超出了定点数类型的精度范围时,则MySQL同样会进行四舍五入处理。
浮点数 vs 定点数
- 浮点数相对于定点数的优点是在长度一定的情况下,浮点类型取值范围大,但是不精准,适用于需要取值范围大,又可以容忍微小误差的科学计算场景(比如计算化学、分子建模、流体动力学等)
- 定点数类型取值范围相对小,但是精准,没有误差,适合于对精度要求极高的场景 (比如涉及金额计算的场景)
CREATE TABLE test_decimal1( |
举例
我们运行下面的语句,把test_double2表中字段“f1”的数据类型修改为 DECIMAL(5,2):
ALTER TABLE test_double2 |
然后,我们再一次运行求和语句:
+---------+ |
位类型:BIT
BIT类型中存储的是二进制值,类似010110。
二进制字符串类型 | 长度 | 长度范围 | 占用空间 |
---|---|---|---|
BIT(M) | M | 1 <= M <= 64 | 约为(M + 7)/8个字节 |
BIT类型,如果没有指定(M),默认是1位。这个1位,表示只能存1位的二进制值。这里(M)是表示二进制的位数,位数最小值为1,最大值为64。
CREATE TABLE test_bit1( |
注意:在向BIT类型的字段中插入数据时,一定要确保插入的数据在BIT类型支持的范围内。
mysql> SELECT * FROM test_bit1; |
可以看到,使用b+0查询数据时,可以直接查询出存储的十进制数据的值。
日期与时间类
日期与时间是重要的信息,在我们的系统中,几乎所有的数据表都用得到。原因是客户需要知道数据的时间标签,从而进行数据查询、统计和处理。
MySQL有多种表示日期和时间的数据类型,不同的版本可能有所差异,MySQL8.0版本支持的日期和时间类型主要有:YEAR类型、TIME类型、DATE类型、DATETIME类型和TIMESTAMP
类型。
YEAR
类型通常用来表示年
DATE
类型通常用来表示年、月、日
TIME
类型通常用来表示时、分、秒
DATETIME
类型通常用来表示年、月、日、时、分、秒
TIMESTAMP
类型通常用来表示带时区的年、月、日、时、分、秒
类型 | 名称 | 字节 | 日期格式 | 最小值 | 最大值 |
---|---|---|---|---|---|
YEAR | 年 | 1 | YYYY或YY | 1901 | 2155 |
TIME | 时间 | 3 | HH:MM:SS | -838:59:59 | 838:59:59 |
DATE | 日期 | 3 | YYYY-MM-DD | 1000-01-01 | 9999-12-03 |
DATETIME | 日期时间 | 8 | YYYY-MM-DDHH:MM:SS | 1000-01-0100:00:00 | 9999-12-3123:59:59 |
TIMESTAMP | 日期时间 | 4 | YYYY-MM-DDHH:MM:SS | 1970-01-0100:00:00 UTC | 2038-01-1903:14:07UTC |
约束
1.1 为什么需要约束
数据完整性(Data Integrity)是指数据的精确性(Accuracy)和可靠性(Reliability)。它是防止数据库中存在不符合语义规定的数据和防止因错误信息的输入输出造成无效操作或错误信息而提出的。
为了保证数据的完整性,SQL规范以约束的方式对表数据进行额外的条件限制。从以下四个方面考虑:
实体完整性(Entity Integrity) :例如,同一个表中,不能存在两条完全相同无法区分的记录
域完整性(Domain Integrity) :例如:年龄范围0-120,性别范围“男/女”
引用完整性(Referential Integrity) :例如:员工所在部门,在部门表中要能找到这个部门
用户自定义完整性(User-defined Integrity) :例如:用户名唯一、密码不能为空等,本部门经理的工资不得高于本部门职工的平均工资的5倍。
1.2 什么是约束
约束是表级的强制规定。可以在创建表时规定约束(通过 CREATE TABLE 语句),或者在表创建之后通过 ALTER TABLE 语句规定约束。
位置 支持的约束类型 是否可以起约束名
列级约束: 列的后面 语法都支持, 但外键没有效果 不可以
表级约束: 所有列的下面 默认和非空不支持,其他支持 可以(主键没有效果)
根据约束起的作用,约束可分为:
NOT NULL 非空约束,规定某个字段不能为空
UNIQUE 唯一约束,规定某个字段在整个表中是唯一的
PRIMARY KEY 主键(非空且唯一)约束
FOREIGN KEY 外键约束
CHECK 检查约束
DEFAULT 默认值约束
查看某个表已有的约束
#information_schema数据库名(系统库) |
非空约束
2.1 作用
限定某个字段/某列的值不允许为空
2.2 关键字
NOT NULL
2.3 特点
1.默认,所有的类型的值都可以是NULL,包括INT、FLOAT等数据类型
2.非空约束只能出现在表对象的列上,只能某个列单独限定非空,不能组合非空
3.一个表可以有很多列都分别限定了非空
4.空字符串’’不等于NULL,0也不等于NULL
…查看文件G:\个人\02转码学习资料\12 SQL\课件pdf版\课件
视图
常见的数据库对象
对象 | 描述 |
---|---|
表(TABLE) | 表是存储数据的逻辑单元,以行和列的形式存在,列就是字段,行就是记录 |
数据字典 | 就是系统表,存放数据库相关信息的表。系统表的数据通常由数据库系统维护,程序员通常不应该修改,只可查看 |
约束(CONSTRAINT) | 执行数据校验的规则,用于保证数据完整性的规则 |
视图(VIEW) | 一个或者多个数据表里的数据的逻辑显示,视图并不存储数据 |
索引(INDEX) | 用于提高查询性能,相当于书的目录 |
存储过程(PROCEDURE) | 用于完成一次完整的业务处理,没有返回值,但可通过传出参数将多个值传给调用环境 |
存储函数(FUNCTION) | 用于完成一次特定的计算,具有一个返回值 |
触发器(TRIGGER) | 相当于一个事件监听器,当数据库发生特定事件后,触发器被触发,完成相应的处理 |
视图概述
为什么使用视图?
视图的理解
视图是一种 虚拟表 ,本身是 不具有数据 的,占用很少的内存空间,它是 SQL 中的一个重要概念。
视图建立在已有表的基础上, 视图赖以建立的这些表称为基表。
- 视图的创建和删除只影响视图本身,不影响对应的基表。但是当对视图中的数据进行增加、删除和修改操作时,数据表中的数据会相应地发生变化,反之亦然。
- 在数据库中,视图不会保存数据,数据真正保存在数据表中。当对视图中的数据进行增加、删除和修改操作时,数据表中的数据会相应地发生变化;反之亦然。
- 视图,是向用户提供基表数据的另一种表现形式。通常情况下,小型项目的数据库可以不使用视图,但是在大型项目中,以及数据表比较复杂的情况下,视图的价值就凸显出来了,它可以帮助我们把经常查询的结果集放到虚拟表中,提升使用效率。理解和使用起来都非常方便。