MySQL学习笔记-基础篇2

目录

  • MySQL学习笔记-基础篇2
  • 8.子查询
    • 8.1 需求分析与问题解决
      • 8.1.1 实际问题
      • 8.1.2 子查询的基本使用
      • 8.1.3 子查询的分类
    • 8.2 单行子查询
      • 8.2.1 单行比较操作符
      • 8.2.2 代码示例
      • 8.2.3 HAVING 中的子查询
      • 8.2.4CASE 中的子查询
      • 8.2.5 子查询中的空值问题
      • 8.2.5 非法使用子查询
      • 8.3 多行子查询
      • 8.3.1 多行比较操作符
      • 8.3.2 代码示例
      • 8.3.3 空值问题
    • 8.4 相关子查询
      • 8.4.1 相关子查询执行流程
      • 8.4.3 EXISTS 与NOT EXISTS 关键字
      • 8.4.4 相关更新
      • 8.4.4 相关删除
    • 8.5 思考
  • 9. 创建和管理表
    • 9.1 基础知识
      • 9.1.1一条数据存储的过程
      • 9.1.2 标识符命名规则
      • 9.1.3 MySQL中的数据类型
    • 9.2 创建和管理数据库
      • 9.2.1 创建数据库
      • 9.2.2 使用数据库
      • 9.2.3 修改数据库
      • 9.2.4 删除数据库
    • 9.3 创建表
      • 9.3.1 创建方式1
      • 9.3.2 创建方式2:
      • 9.3.3 查看数据表结构
    • 9.4 修改表
      • 9.4.1 追加一个列
      • 9.4.2 修改一个列
      • 9.4.3 重命名一个列
      • 9.4.4 删除一个列
    • 9.5 重命名表
    • 9.6 删除表
    • 9.7 清空表
    • 9.8 内容扩展
  • 10. 数据处理之增删改
    • 10.1 插入数据
      • 10.1.1 实际问题
      • 10.1.2 方式1:VALUES的方式添加
      • 10.1.3 方式2:将查询结果插入到表中
    • 10.2 更新数据
    • 10.3 删除数据
    • 10.4 MySQL 8.0新特性:计算列
    • 10.5 综合案例
  • 11. MySQL 数据类型
    • 11.1 MySQL中的数据类型
    • 11.2 整数类型
      • 11.2.1类型介绍
      • 11.2.2 可选属性
      • 11.2.3 适用场景
      • 11.2.4 如何选择?
    • 11.3 浮点类型
      • 11.3.1 类型介绍
      • 11.3.2 数据精度说明
      • 11.3.3 精度误差说明
    • 11.4 定点数类型
      • 11.4.1 类型介绍
      • 11.4.2
    • 11.5 位类型:BIT
    • 11.6 日期与时间类型
      • 11.6.1 YEAR 类型
      • 11.6.2 DATE 类型
      • 11.6.3 TIME 类型
      • 11.6.4 DATETIME 类型
      • 11.6.5 TIMESTAMP类型
      • 11.6.6 开发中的经验
    • 11.7 文本字符串类型
      • 11.7.1 CHAR 与VARCHAR类型
      • 11.7.2 TEXT 类型
    • 11.8 ENUM 类型
    • 11.9 SET 类型
    • 11.10 二进制字符串类型
    • 11.11 JOSN 类型
    • 11.12 空间类型
    • 11.13 小结及选择建议

8.子查询

  • 子查询指一个查询语句嵌套在另一个查询语句内部的查询
  • SQL中子查询的使用大大增强了SELECT查询的能力,因为很多时查询需要从结果集中获取数据,或者需要从同一个表中先计算得出一个数据结果,然后与这个数据结果(可能是某个标量,也可能是某个集合)进行比较。

8.1 需求分析与问题解决

8.1.1 实际问题

  • 现有解决方式:
-- 方式一
SELECT salary
FROM employees
WHERE last_name ='Abel';SELECT last_name, salary
FROM employees
WHERE salary>11000;-- 方式二:自连接
SELECT e2.last_name,e2.salary
FROM employees e1,employees e2
WHERE e1.last_name='Abel'
AND e1.salary <e2.salary;-- 方式三:子查询
SELECT last_name,salary
FROM employees
WHERE salary>(SELECT salaryFROM employeesWHERE last_name='Abel');

8.1.2 子查询的基本使用

  • 子查询的基本语法结构:
  • 子查询(内查询)在主查询之前一次执行完成。
  • 子查询的结果被主查询(外查询)使用。
  • 注意:
    • 子查询需要包含在括号内
    • 将子查询放在比较条件的右侧
    • 单行操作符对应单行子查询,多行操作符对应多行子查询

8.1.3 子查询的分类

分类方式一:

  • 按内查询的的结果返回一条还是多条记录,将子查询分为单行子查询、多行子查询。

  • 单行子查询

  • 多行子查询

    分类方式二:
    按内查询是否被执行多次,将子查询划分为相关(或关联)子查询和不相关(或非关联)子查询

  • 子查询从数据表中查询了数据结果,如果这个数据结果只执行一次,然后这个数据结果作为主查询的条件进行执行,那么这样的子查询叫做不相关子查询。

  • 同样,如果子查询需要执行多次,即采用循环的方式,先从外部查询开始,每次都传入子查询进行查询,然后再将结果反馈给外部,这种嵌套的执行方式称为相关子查询。

8.2 单行子查询

8.2.1 单行比较操作符

8.2.2 代码示例

  • 题目:查询工资大于149号员工工资的员工信息

  • 题目:返回job_id与141号员工相同,salary比143号员工多的员工信息,job_id和工资
SELECT last_name,job_id,salary
FROM employees
WHERE job_id =(SELECT job_idFROM employees WHERE employee_id =141)
AND salary>(SELECT salaryFROM employeesWHERE employee_id =143);
  • 题目:返回公司工资最少的员工的last_name,job_id和salary
SELECT last_name,job_id,salary
FROM employees
WHERE salary = (SELECT MIN(salary)FROM employees);
  • 题目:查询与141号或174号员工的manager_id和department_id 相同的其他员工的employee_id,manager_id,department_id
-- 实现方式1:不成对比较
SELECT employee_id, manager_id,department_id
FROM employees
WHERE manager_id IN (SELECT manager_idFROM employeesWHERE employee_id IN (174,141))
AND department_id IN(SELECT department_idFROM employeesWHERE employees_id IN(141,174))
AND employee_id NOT IN(141,174);-- 实现方式2:成对比较
SELECT employee_id,manager_id,department_id
FROM employees
WHERE (manager_id,department_id)IN(SELECT manager_id,department_idFROM employeesWHERE employee_id IN(141,174))
AND employee_id NOT IN(141,174);

8.2.3 HAVING 中的子查询

  • 首先执行子查询
  • 向主查询中的HAVING子句返回结果
  • 题目:查询最低工资大于50号部门最低工资的部门id和其最低工资
SELECT department_id,MIN(salary)
FROM employees
GROUP BY department_id
HAVING MIN(salary)>(SELECT MIN(salary)FROM employeesWHERE department_id =50);

8.2.4CASE 中的子查询

在CASE表达式中使用单列子查询
题目:显示员工的employee_id,last_name,和location。其中若员工的department_id与location_id为1800的department_id相同,则location为“Canada”,其余为“USA”。

SELECT employees_id,last_name,(CASE department_idWHEN    (SELECT department_id FROM departmentsWHERE location_id =1800)THEN 'Canada' ELSE 'USA' END)location
FROM employees;

8.2.5 子查询中的空值问题

SELECT last_name,job_id
FROM employees
WHERE job_id=(SELECT job_idFROM employeesWHERE last_name="Haas");

  • 子查询不返回任何行

8.2.5 非法使用子查询

SELECT employee_id,last_name
FROM employees
WHERE salary=(SELECT MIN(salary)FROM employeesGROUP BY department_id);

  • 多行子查询使用单行比较符

8.3 多行子查询

  • 也称为集合比较子查询
  • 内查询返回多行
  • 使用多行比较操作符

8.3.1 多行比较操作符

  • 体会ANY和ALL的区别

8.3.2 代码示例

  • 题目:返回其它job_id中比job_id为“IT_PROG”部门任一工资低的员工的员工号、姓名、job_id以及salary
  • 题目:查询平均工资最低的部门id
-- 方式1:
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary)=(SELECT MIN(avg_sal)FROM (SELECT AVG(salary) avg_salFROM employeesGROUP BY department_id) dept_avg_sal)-- 方式2
SELECT department_id
FROM employees
GROUP BY department
HAVING AVG(salary) <= ALL(SELECT AVG(salary) avg_salFROM employeesGROUP BY department_id);

8.3.3 空值问题

SELECT last_name
FROM employees
WHERE employee_id NOT IN(SELECT manager_idFROM employees);

8.4 相关子查询

8.4.1 相关子查询执行流程

  • 如果子查询的执行依赖于外部查询,通常情况下都是因为子查询中的表用到了外部的表,进行了条件关联,因此每执行一次外部查询,子查询都要重新计算一次,这样的子查询就称为关联子查询

  • 相关子查询按照一行接一行的顺序执行,主查询的每一行都执行一次子查询。
    - 说明:子查询中使用主查询中的列

  • 题目:查询员工中工资大于本部门平均工资的员工的last_name,salary和其department_id

  • 方式一:相关子查询

  • 方式二:在FROM中使用子查询

SELECT last_name,salary,e1.department_id
FROM employees e1,(SELECT department_id,AVG(salary) dept_avg_sal FROM employees GROUP BY department_id) e2
WHERE e1.department_id = e2.department_id
AND e2.dept_avg_sal<e1.salary;
  • FROM 型的子查询:子查询是作为FROM的一部分,子查询要用()引起来,并且要给这个子查询取别名,把它当成一张临时的虚拟表来使用

  • 在ORDER BY中使用子查询

  • 题目:查询员工的id,salary,按照department_name排序

SELECT employee_id,salary
FROM employees e
ORDER BY(SELECT department_nameFROM departments dWHERE e.department_id=d.department_id);
  • 题目:若employees表中employee_id与job_history表中employee_id相同的数目不小于2,输出这些相同id的员工的employee_id,last_name和其job_id
SELECT e.employee_id, last_name,e.job_id
FROM employees e
WHERE 2 <= (SELECT COUNT(*) FROM job_history WHERE employee_id = e.employee_id);

8.4.3 EXISTS 与NOT EXISTS 关键字

  • 关联子查询通常也会和EXISTS操作符一起来使用,用来检查字查询中是否存在满足条件的行。

  • 如果在子查询中不存在满足条件的行:

    • 条件返回FALSE
    • 继续在子查询中查询
  • 如果在子查询中存在满足条件的行:

    • 不在子查询中继续查找
    • 条件返回TRUE
  • NOT EXISTS关键字表示如果不存在某种条件,则返回TRUE,否则返回FALSE。

  • 题目:查询公司管理者的employee_id,last_name,job_id,department_id信息

  • 方式一:

SELECT employee_id, last_name, job_id, department_id
FROM employees e1
WHERE EXISTS ( SELECT * FROM employees e2 WHERE e2.manager_id = e1.employee_id);
  • 方式二:自连接
SELECT DISTINCT e1.employee_id, e1.last_name, e1.job_id, e1.department_id
FROM employees e1 JOIN employees e2
WHERE e1.employee_id = e2.manager_id;
  • 方法三:
SELECT employee_id,last_name,job_id,department_id
FROM employees
WHERE employee_id IN ( SELECT DISTINCT manager_id FROM employees );
  • 题目:查询departments表中,不存在于employees表中的部门 department_id和department_name
SELECT department_id, department_name
FROM departments d
WHERE NOT EXISTS (SELECT 'X' FROM employees WHERE department_id = d.department_id);

8.4.4 相关更新

UPDATE table1 alias1
SET column=(SELECT expressionFROM table2 alias2WHERE alias1.column=alias2.column);
  • 使用相关子查询依据一个表中的数据更新另一个表中的数据。
-- 1.
ALTER TABLE employees
ADD(department_name VARCHAR2(14));-- 2.
UPDATE employees e
SET department_name = (SELECT department_nameFROM departments dWHERE e.department_id = d.department);

8.4.4 相关删除

DELETE FROM table1 alias1
WHERE colunm operator(SELECT expressionFROM  table2 alias2WHERE alias1.column = alias2.column);
  • 使用相关子查询依据一个表中的数据删除另一个表的数据

  • 题目:删除表employees 中,与emp_history 表皆有的数据

DELETE FROM employees e
WHERE employee_id in(SELECT employee_idFROM emp_historyWHERE employee_id = e.employee_id);

8.5 思考

问题:谁的工资必Abel的高?
解答:

-- 方式1:自连接
SELECT e2.last_name,e2.salary
FROM employees e1, employees e2
WHERE e1.last_name = 'Abel'
AND e1.salary<e2.sarary-- 方式2:子查询
SELECT last_name, salary
FROM employees
WHERE salary>(SELECT salaryFROM employeeesWHERE last_name='Abel');

问题:以上两种方式有好坏之分吗?
解答:自连接方式是好!

题目中可以使用子查询,也可以使用自连接。一般情况下建议使用自连接,因为在许多DBMS的处理过程中,对于自连接的处理速度要比子查询快很多。

可以这样理解:子查询实际上是通过未知表进行查询后的条件判断,而自连接是通过已知的自身数据表进行条件判断,因为在大部分DBMS中都对自连接处理进行了优化。

9. 创建和管理表

9.1 基础知识

9.1.1一条数据存储的过程

  • 存储数据是处理数据的第一步。只有正确地吧数据存储起来,才能进行有效处理和分析,否则,只能一团乱麻,无从下手。
  • 那么怎样才能把用户各种经经营相关的、纷繁复杂的数据,有序、高效地存储起来呢?在MySQL中,一个完整的数据存储过程中总共有4步,分别是创建数据库,确认字段,创建数据表,插入数据。
    从系统架构层次看,MySQL数据库系统从大到小依次是:数据库服务,数据库,数据表,数据表的列与行。

9.1.2 标识符命名规则

  • 数据库表,表名不得超过30个字符,变量名限制为29个
  • 必须只能包含A-Z,a-z,0-9,_共63个字符
  • 数据库名、表名、字段名等对象名中间不要包含空格
  • 同一个MySQL软件中,数据库不能同名;同一个库中,表不能重名;同一个表中,字段不能重名
  • 必须保证字段没有和保留字,数据库系统或常用方法冲突。如果坚持使用,在SQL语句汇总使用`(着重号)引起来
  • 保持字段名和类型的一致性:在命名字段并为其制定数据类型的时候一定要保证一致性,假如数据类型在一个表里是整数,那在另一个表里可就变成字符类型了。

9.1.3 MySQL中的数据类型

  • 其中,常用的几类类型介绍

9.2 创建和管理数据库

9.2.1 创建数据库

  • 方式一:创建数据库
CREATE DATABASE 数据库名;
  • 方式二:创建数据库并指定字符集
CREATE DATABASE 数据库名 CHARACTER SET 字符集;
  • 方式三:判断数据库是否存在,不存在则创建数据库(推荐)
CREATE DATABASE IF NOT EXISTS 数据库名;

如果MySQL中已经存在相应的数据库,则忽略创建语句,不再创建数据库。

  • 注意:DATABASE 不能改名,一些可视化工具可以改名,它是重新建立库,把所有表复制到新库,再删除旧库完成的。

9.2.2 使用数据库

  • 查看当前所有的数据库
SHOW DATABASES;# 有一个S,代表多个数据库
  • 查看当前正在使用的数据库
SELECT DATABAE(); # 使用一个MySQL中的全局函数。
  • 查看指定库下所有的表
SHOW TABLES FROM 数据库名;
  • 查询数控的创建信息
SHOW CREATE  DATABASE 数据库名;
或者
SHOW CREATE DATABASE 数据库名\G
  • 使用、切换数据库
USE 数据库名;

注意:要操作表格和数据之前必须先说明是对哪个数据库进行操作,否则就要对所有对象加上数据库名

9.2.3 修改数据库

  • 更新数据库字符属性
ALTER DATABASE 数据库名 CHARACTER SET 字符集; # 比如gbk,utf8等

9.2.4 删除数据库

  • 方式一:删除指定库
DROP DATABASE  数据库名;
  • 方式二:删除指定库(推荐)
DROP DATABASE IF EXISTS 数据库名;

9.3 创建表

9.3.1 创建方式1

  • 必备条件:

    • CREATE TABLE权限
    • 存储空间
  • 语法格式:
CREATE TABLE [IF NOT EXISTS]表名(字段1,数据类型[约束条件][默认值],字段2,数据类型[约束条件][默认值],字段3,数据类型[约束条件][默认值],……[表约束条件]
);

如果加了IF NOT EXISTS 关键字,则表示:如果当前数据库中不存在要创建的数据包,则创建数据表;如果当前数据库中已经存在要创建的数据表,则忽略建表语句,不再创建数据表。

  • 必须指定:

    • 列名
    • 列名(或字段名) ,数据类型,长度
  • 可选指定列:
    • 约束条件
    • 默认值
  • 创建表举例:
-- 创建表
CREATE TABLE emp(-- int 类型emp_id INT,-- 最多保存20个中英文字符emp_name VARCHAR(20),-- 总位数不超过15位salary DOUBLE,-- 日期类birthday DATE
);
DESC emp;

MySQL 在执行建表语句时,将id字段的类型设置为INT(11),这里的11实际上是int类型指定的显示宽度,默认的显示宽度为11,也可以在创建表的时候指定数据的显示宽度。

  • 创建表举例:
CREATE TABLE dept(-- int类型,自增deptno INT(2) AUTO_INCREMENT,dname VARCHAR(14),loc VARCHAR(13),-- 主键PRIMARY KEY(deptno)
);
DESCRIBE dept;

9.3.2 创建方式2:

  • 使用AS subquery选项,将创建表和插入数据联合起来
    - 指定的列和子查询中的列要一一对应
  • 通过列名和默认值定义列
CREATE TABLE emp1 AS SELECT * FROM employees;
CREATE TABLE emp2 AS SELECT * FROM employees;CREATE TABLE dept80
AS
SELECT employee_id,last_name,salary*12 ANNSAL,hire_date
FROM employes
WHERE department_id =80;
DESCRIBE dept80;

9.3.3 查看数据表结构

在MySQL中创建好数据表之后,可以查看数据表的结构。MySQL支持使用 DESCRIBE/ DESC 语句查看数据表结构,也支持使用SHOW CREATE TABLE 语句查看数据表结构。

  • 语法格式如下:
SHOW CREATE TABLE 表名\G
  • 使用SHOW CREATE TABLE 语句不仅可以查看创建时的详细语句,还可以查看存储引擎和字符编码。

9.4 修改表

修改表指的是修改数据库中已经存在的数据表的结构。

  • 使用ALTER TABLE 语句可实现:

    • 向已有的表中添加列
    • 修改现有表中的列
    • 删除现有表的列
    • 重命名现有表的列

9.4.1 追加一个列

  • 语法格式如下:
ALTER TABLE 表名 ADD [COLUMN] 字段名 字段类型[FIRST|AFTER 字段名];
  • 举例:
ALTER TABLE dept80
ADD job_id varchar(15);

9.4.2 修改一个列

  • 可以修改列的数据类型,长度,默认值和位置
  • 修改字段数据类型、长度、默认值、位置的语法格式如下:
ALTER TABLE 表名 MODIFY [COLUMN] 字段名1 字段类型[DEFAULT 默认值] [FIRDT|AFTER 字段名2];
  • 举例:
ALTER TABLE dept80
MODIFY last_name VARCHAR(30);ALTER TABLE dept80
MODIFY salary double(9,2) default 1000;
  • 对默认值的修改只影响今后对表的修改
  • 此外,还可以通过这种方式修改列的约束。

9.4.3 重命名一个列

使用CHANGE old_column new_column dataType 子句重命名列。语法格式如下;

ALTER TABLE 表名 CHANGE 【column】 列名 新列名 新数据类型;
  • 举例:
ALTER TABLE dept80
CHANGE department_name dept_name varchar(15);

9.4.4 删除一个列

  • 删除表中某个字段的语法格式如下:
ALTER TABLE 表名 DROP 【COLUMN】字段名
  • 举例:
ALTER TABLE dept80 DROP COLUMN job_id;

9.5 重命名表

  • 方式一:使用RENAME
RENAME TABLE emp
TO myemp
  • 方式二:
ALTER TABLE dept
RENAME[TO] detail_dept;-- [To] 可以省略
  • 必须是对象的拥有者

9.6 删除表

  • 在MySQL中,当一张数据表没有与其他任何数据表形成关联关系时,可以将当前数据表直接删除。
  • 数据和结构都被删除
  • 所有正在运行的相关事务被提交
  • 所有相关索引被删除
  • 语法格式:
DROP TABLE [IF EXISTS] 数据表1 [, 数据表2, …, 数据表n];

IF EXISTS:如果当前数据库中存在相应的数据表,则删除数据表;如果当前数据库中不存在相应的数据表,则忽略删除语句,不再执行删除数据表的操作。

  • 举例:
DROP TABLE dept80;
  • DROP TABLE 语句不能回滚

9.7 清空表

  • TRUNCATE TABLE 语句:

    • 删除表中所有的数据
    • 释放表的存储空间
  • 举例:
TRUNCATE TABLE detail_dept;

TRUNCATE 语句不能回滚,而是用DELETE语句删除数据,可以回滚
对比:

SET autocommit = FALSE;DELECT FROM emp2;
# TRUNCATE TABLE emp2;SELECT * FROM emp2;ROLLBACK;
SELECT * FROM emp2;
  • 阿里开发规范:
  • 【参考】TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但 TRUNCATE 无事务且不触发 TRIGGER,有可能造成事故,故不建议在开发代码中使用此语句。
  • 说明:TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。

9.8 内容扩展

  • 扩展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
  • 【 推荐 】库名与应用名称尽量一致。
  • 【参考】合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速
    度。

    • 正例:无符号值可以避免误存负数,且扩大了表示范围。

  • 拓展2:如何理解清空表,删除表等操作需谨慎!

  • 表删除操作将把表的定义和表中的数据一起删除,并且MySQL在执行删除操作时,不会有任何的确认信息提示,因此执行删除操作时应当慎重。在删除表前,最好对表中的数据进行备份,这样当操作失误时可以对数据进行恢复,以避免造成无法挽回的结果。

  • 同样的,在使用ALTER TABLE 进行表的基本修改操作是,在执行操作过程之前,也应该确保对数据进行完整的备份,因为数据库的改变时无法撤销的,如果添加了一个不需要的字段,可以将其删除;相同的,如果删除了一个需要的列,该列下的所有数据都将会丢失。

  • 拓展3:MySQL8 新特性-DDL的原子化

  • 在MySQL8.0 版中,InNoDB表的DDL支持事务完整性,即DDL操作要么成功要么回滚。DDL操作回滚日志写入data dictionary数据字典表mysql.innodb_ddl_log(该表是隐藏的表,通过show tables无法看到)中,用于回滚操作。通过设置参数,可将DDL操作日志打印输出到MySQL错误日志中。

  • 分别在MySQL 5.7 版本和MySQL 8.0版本中创建数据库和数据表,结果如下:

CREATE DATABASE mytest;
USE mytest;
CREATE TABLE book1( book_id INT , book_name VARCHAR(255) );SHOW TABLES;
  • 在MySQL 5.7版本中,测试步骤如下:删除数据表book1和数据表book2,结果如下:
mysql> DROP TABLE book1,book2;
ERROR
1051 (42S02): Unknown table 'mytest.book2'

再次查询数据库中的数据表名称,虽然删除时报错了,但是仍然删除了数据表book1。

在MySQL 8.0 版本中,测试步骤如下:删除数据表book1和数据表book2,结果如下:

mysql> DROP TABLE book1,book2;
ERROR 1051 (42S02): Unknown table 'mytest.book2'

再次查看数据库中的数据表名称,结果如下:

  • 从结果可以看出,数据表book1并没有被删除。

10. 数据处理之增删改

10.1 插入数据

10.1.1 实际问题

10.1.2 方式1:VALUES的方式添加

使用这种语法一次只能向表中插入一条数据

  • 情况1:为表的所有字段按默认顺序插入数据
INSERT INTO 表名
VALUES(value1,value2,...);
  • 值列表中需要为表的每一个字段指定值,并且值的顺序必须和数据表中字段定义时的顺序相同。

  • 举例:

INSERT INTO departments
VALUES (70,'Pub',100,1700);INSERT INTO departments
VALUES(100,'Finance',NULL,NULL);
  • 情况2:为表的指定字段插入数据
INSERT INTO 表名(column1 [,column2,...,columnn])
VALUES (value1 [,value2,...,valuen]);

为表的指定字段插入数据,就是在INSERT语句中只向部分字段中插入值,而其他字段的值为定义时的默认值。

在INSET字句中随意列出列名,但是一旦列出,VALUES中要插入的要与一一对应。如果类型不同,将无法插入,并且MySQL会产生误会。

  • 举例:
INSERT INTO departments(department_id,department_name)
VALUES(80,'IT');
  • 情况3:同时插入多条记录
  • INSERT 语句可以同时向数据表中插入多条记录,插入时指定多个值列表,每个值列表之间用逗号分隔,基本语法如下:
INSERT INTO table_name
VALUES
(value1 [,value2,...,valuen]),
(value1 [,value2,...,valuen]),
.....
(value1 [,value2,...,valuen]);-- 或者
INSERT INTO table_name(column1 [,column2,...,columnn])
VALUES
(value1 [,value2,...,valuen]),
(value1 [,value2,...,valuen]),
.....
(value1 [,value2,...,valuen]);
-- 举例
INSERT INTO emp(emp_id,emp_name)
VALUES(1001,'sahif'),(1002,'falfdf'),(1003,'dfajdg');
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
  • 使用INSERT 同时插入多条记录时,MySQL会返回一些在执行单行插入时没有的额外信息:Records:表明插入的记录条数。Duplicates:表明插入时被忽略的记录,原因可能是这些记录包含了重复的主键值。Warnings:表明有问题的数据值,例如发生数据类型转换。

  • 一个同时插入多行记录的INSERT语句等同于多个单行插入的INSERT语句,但是多行的INSERT语句在处理过程中 效率更高 。因为MySQL执行单条INSERT语句插入多行数据比使用多条INSERT语句快,所以在插入多条记录时最好选择使用单条INSERT语句的方式插入。

  • 小结

  • VALUES 也可以写成VALUE,但是VALUES是标准写法

  • 字符和日期型数据应包含在单引号中。

10.1.3 方式2:将查询结果插入到表中

  • INSERT还可以将SELECT语句查询的结果插入到表中,此时不需要把每一条记录的值一个一个输入,只需要使用一条INSERT语句和一条SELECT语句组成的组合语句即可快速地从一个或多个表中向一个表中插入多行。
  • 基本语法格式如下:
INSERT INTO 目标表名 (tar_column1 [, tar_column2, …, tar_columnn])
SELECT
(src_column1 [, src_column2, …, src_columnn])
FROM 源表名
[WHERE condition]
  • 在INSERT语句中加入子查询
  • 不写VALUES子句
  • 子查询中的值列表应与INSERT子句中的列名对应。

举例:

INSERT INTO emp2
SELECT *
FROM employees
WHERE department_id = 90;INSERT INTO sales_reps(id, name, salary, commission_pct)
SELECT employee_id, last_name, salary, commission_pct
FROM employees
WHERE job_id LIKE '%REP%';

10.2 更新数据

  • 使用UPDATE语句更新数据。语法如下:
UPDATE table_name
SET column1=value1, column2=value2, … , column=valuen
[WHERE condition]
  • 可以一次更新多条数据
  • 如果需要回滚数据,需要保证在DML前,进行设置:SET AUTOCOMMIT=FALSE;
  • 使用WHERE子句指定需要更新的数据。
UPDATE employees
SET department_id = 70
WHERE employee_id = 113;
  • 如果省略WHERE子句,则表中的所有数据都将被更新。
UPDATE copy_emp
SET department_id = 110;
  • 更新中的数据完整性错误
UPDATE employees
SET department_id = 55
WHERE department_id = 110;

10.3 删除数据

  • 使用DELECT语句从表中删除数据

DELETE FROM table_name [WHERE <condition>];

table_name 指定要执行删除操作的表,“[WHERE ]”为可选参数,指定删除的条件,如果没有WHERE子句,DELETE语句将删除表中的所有记录。

  • 使用WHERE子句删除指定的记录。
DELETE FROM departments
WHERE department_name ='Finance';
  • 如果省略WHERE子句,则表中的全部数据被删除
DELECT FROM copy_emp;
  • 删除中的数据完整性错误
DELECT  FROM departments
WHERE department_id =60;

10.4 MySQL 8.0新特性:计算列

什么叫计算列?简单来说就是某一列的值是通过别的列计算得来的,例如a列为1,b列为2,c列不需要手动插入,定义为a+b的结果为c,那么c就是计算列,是通过别的列计算来的。
在MySQL 8.0中, CREATE TABLE 和ALTER TABLE都支持增加计算列。下面以CREATE TABLE为例进行说明

  • 举例:定义数据表tb1,然后定义字段id,字段a,字段b和字段c,其中字段c为计算列,用于计算a+b的值,首先创建测试表tb1。语句如下:
CREATE TABLE tb1(id INT,a INT,b INT,c INT GENRATED ALWAYS AS (a+b) VIRTUSL);

演示结果:

10.5 综合案例

  • books 表结构
  • 表内容
# 1、创建数据库test01_library
CREATE TABLE IF NOT EXISTS test01_library CHARACTER SET 'utf8';USE test01_library;# 2、创建表 books
CREATE TABLE books(id INT,name VARCHAR(50),authors VARCHAR(100),price FLOAT,pubdate YEAR,note VARCHAR(100),num INT);# 3、向books表中插入记录
# 1)不指定字段名称,插入第一条记录
INSERT INTO books
VALUES(1,'Tal of AAA','Dickes',23,1995,'novel',111);# 2)指定所有字段名称,插入第二记录
INSERT INTO books (id,name,`authors`,price,pubdate,note,num) VALUES(2,'EmmaT','Jane lura',35,1993,'Joke',22);# 3)同时插入多条记录(剩下的所有记录)
INSERT INTO books (id,name,`authors`,price,pubdate,note,num)
VALUES (3,'Story of Jane','Jane Tim',40,2001,'novel',0),
(4,'Lovey Day','George Byron',20,2005,'novel',30),
(5,'Old land','Honore Blade',30,2010,'Law',0),
(6,'The Battle','Upton Sara',30,1999,'medicine',40),
(7,'Rose Hood','Richard haggard',28,2008,'cartoon',28);# 4、将小说类型(novel)的书的价格都增加5。
UPDATE books SET price= price+5 WHERE note 'novel';# 5、将名称为EmmaT的书的价格改为40,并将说明改为drama。
UPDATE nooks SET price=40, note='drame' WHERE name='EmmaT';# 6、删除库存为0的记录
DELETE FROM books WHERE num=0;# 7、统计书名中包含a字母的书
SELECT *FROM books WHERE name LIKE '%a%';# 8、统计书名中包含a字母的书的数量和库存总量
SELECT COUNT(*),SUM(num) FROM books WHERE name LIKE '%a%';# 9、找出“novel”类型的书,按照价格降序排列
SELECT * FROM books WHERE note = 'novel' ORDER BY price DESC;# 10、查询图书信息,按照库存量降序排列,如果库存量相同的按照note升序排列
SELECT * FROM books ORDER BY num DESC,note ASC;# 11、按照note分类统计书的数量
SELECT note,COUNT(*) FROM books GROUP BY note;# 12、按照note分类统计书的库存量,显示库存量超过30本的
SELECT note,SUM(num) FROM books GROUP BY note HAVING SUM(num)>30;# 13、查询所有图书,每页显示5本,显示第二页
SELECT * FROM books LIMIT 5,5;# 14、按照note分类统计书的库存量,显示库存量最多的
SELECT note,SUM(num) sum_num
FROM books
GROUP BY note
ORDER BY sum_num DESC
LIMIT 0,1;# 15、查询书名达到10个字符的书,不包括里面的空格
SELECT * FROM books WHERE CHAR_LENGTH(REPLACE(name,' ',''))>=10;# 16、查询书名和类型,其中note值为novel显示小说,law显示法律,medicine显示医药,cartoon显示卡通, joke显示笑话
SELECT name AS "书名" ,note, CASE note WHEN 'novel' THEN '小说' WHEN 'law' THEN '法律' WHEN 'medicine' THEN '医药'WHEN 'cartoon' THEN '卡通' WHEN 'joke' THEN '笑话' END AS "类型"
FROM books;
# 17、查询书名、库存,其中num值超过30本的,显示滞销,大于0并低于10的,显示畅销,为0的显示需要无货
SELECT name,num,CASE WHEN num>30 THEN '滞销' WHEN num>0 AND num<10 THEN '畅销' WHEN num=0 THEN '无货' ELSE '正常' END AS "库存状态"
FROM books;# 18、统计每一种note的库存量,并合计总量
SELECT IFNULL(note,'合计总库存量') AS note,SUM(num) FROM books GROUP BY note WITH ROLLUP;# 19、统计每一种note的数量,并合计总量
SELECT IFNULL(note,'合计总数') AS note,COUNT(*)
FROM books
GROUP BY note
WITH ROLLUP;# 20、统计库存量前三名的图书
SELECT * FROM books ORDER BY num DESC LIMIT 0,3;# 21、找出最早出版的一本书
SELECT * FROM books ORDER BY pubdate ASC LIMIT 0,1;# 22、找出novel中价格最高的一本书
SELECT * FROM books WHERE note = 'novel' ORDER BY price DESC LIMIT 0,1;# 23、找出书名中字数最多的一本书,不含空格
SELECT * FROM books ORDER BY CHAR_LENGTH(REPLACE(name,' ','')) DESC LIMIT 0,1;

11. MySQL 数据类型

11.1 MySQL中的数据类型

  • 常见数据类型数据

11.2 整数类型

11.2.1类型介绍

整数类型一共有 5 种,包括 TINYINT、SMALLINT、MEDIUMINT、INT(INTEGER)和 BIGINT。
它们的区别如下表所示

11.2.2 可选属性

  • 整数类型的可选属性有三个:
  • M
  • M:表示显示宽度,M的取值范围是(0,255)。例如:int(5):当数据宽度小于5位的时候在数字前面需要用字符填满宽度。该项功能需要配合“ZEROFILL”使用,表示用“0”填满,否则指定显示宽度无效。
  • 如果设置了显示宽度,那么插入的数据宽度超过显示宽度限制,会不会截断或插入失败?
  • 不会对插入的数据有任何影响,还是按照类型的试卷宽度进行保存,即显示宽度与类型可存储的值范围无关。从MySQL 8.0开始,整数数据类型不推荐使用显示宽度属性。
  • 整型数据类型可以在定义表结构时指定所需要的显示宽度,如果不指定,则系统为每一种类型指定默认的宽度值。
  • 举例:
CREATE TABLE test_int1 (x TINYINT, y SMALLINT, z MEDIUMINT, m INT, n BIGINT );
  • 查看表结构 (MySQL5.7中显式如下,MySQL8中不再显式范围)
  • TINYINT有符号数和无符号数的取值范围分别为-128~ 127和0~ 255,由于负号占了一个数字位,因此TINYINT默认的显示宽度为4。同理,其他整数类型的默认显示宽度与其有符号数的最小值的宽度相同。
  • 举例:
    - UNSIGNED

UNSIGNED:无符号类型(非负),所有的整数类型都有一个可选的UNSIGNED(无符号属性),无符号整数类型的最小取值为0。所以,如果需要在MySQL数据库中保存非负整数值时,可将整数类型设置为无符号类型。

int类型默认显示宽度为int(11),无符号int类型默认显示宽度为int(10)。

  • ZEROFILL
    ZEROFILL :0 填充,(如果某列是ZEROFILL,那么MySQL会自动为当前列添加UNSIGNED属性),如果指定了ZEROFILL只是表示不够M位时,用0 在左边填充,如果超过M为,只要不超过数据存储的范围即可。

原来,在int(M)中,M的值根int(M)所占多少存储空间并无任何关系。int(3),int(4),int(8)在磁盘上都是占用4bytes的存储空间。也就是说,int(M),必须和UNSIGNED ZERROFILL 一起使用才有意义。如果整数超过M位,就按照实际位数存储。只是无需再用字符0进行填充。

11.2.3 适用场景

  • TINYINT :一般用于枚举数据,比如系统设定取值范围很小且固定的场景。
  • SMALLINT :可以用于较小范围的统计数据,比如统计工厂的固定资产库存数量等。
  • MEDIUMINT :用于较大整数的计算,比如车站每日的客流量等。
  • INT、INTEGER :取值范围足够大,一般情况下不用考虑超限问题,用得最多。比如商品编号。
  • BIGINT :只有当你处理特别巨大的整数时才会用到。比如双十一的交易量、大型门户网站点击量、证券公司衍生产品持仓等。

11.2.4 如何选择?

  • 在评估用哪种整数类型的时候,你需要考虑 存储空间可靠性 的平衡问题:一方 面,用占用字节数少的整数类型可以节省存储空间;另一方面,要是为了节省存储空间, 使用的整数类型取值范围太小,一旦遇到超出取值范围的情况,就可能引起 系统错误 ,影响可靠性。
  • 举个例子,商品编号采用的数据类型是 INT。原因就在于,客户门店中流通的商品种类较多,而且,每天都有旧商品下架,新商品上架,这样不断迭代,日积月累。如果使用 SMALLINT 类型,虽然占用字节数比 INT 类型的整数少,但是却不能保证数据不会超出范围65535。相反,使用 INT,就能确保有足够大的取值范围,不用担心数据超出范围影响可靠性的问题。
  • 要注意的是,在实际工作中,系统故障产生的成本远远超过增加几个字段存储空间所产生的成本。因此,我建议你首先确保数据不会超过取值范围,在这个前提之下,再去考虑如何节省存储空间。

11.3 浮点类型

11.3.1 类型介绍

浮点数和定点数类型的特点是可以处理小数,可以把整数看成小数的一个特例。因此,浮点数和定点数的使用场景,比正数大多了。MySQL 支持的浮点数类型,分别为FLOAT和DOUBLE,FEAL。

  • FLOAT表示单精度浮点数
  • DOUBLE表示双进度浮点数

  • REAL 默认是DOUBLE。如果你把SQL模式设定为启用“REAL_AS_FLOAT”,那么MySQL就默认REAL是FLOAT。要启用“REAL_AS_FLOAT”,可以通过下面SQL方式:
SET sql_mode="REAL_AS_FLOAT";
  • 问题1: FLOAT和DOUBLE这两种类型的区别是啥?
    FLOAT 占用字节数少,取值范围小;DOUBLE 占用字节数多,取值范围也大。

  • 问题2: 为什么浮点数类型的无符号数取值范围,只相当于有符号数取值范围的一半,也就是只相当于有符号数取值范围大于等于零的部分呢?
    MySQL 存储浮点数的格式为: 符号(S) 、 尾数(M) 和 阶码(E) 。因此,无论有没有符号,MySQL 的浮点数都会存储表示符号的部分。因此, 所谓的无符号数取值范围,其实就是有符号数取值范围大于等于零的部分。

11.3.2 数据精度说明

对于浮点类型,在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也不推荐使用了,将来也可能被移除。

  • 举例

11.3.3 精度误差说明

浮点数类型有个缺陷,就是不精准。下面重点解释一下为什么 MySQL 的浮点数不够精准。比如,设计一个表,有f1这个字段,插入值分别为0.47,0.44,0.19,期待的运行结果是:0.47 + 0.44 + 0.19 = 1.1。而使用sum之后查询:

  • 查询结果是 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

11.4 定点数类型

11.4.1 类型介绍

  • MySQL 中的定点数类型只有DECIMAL一种类型

    使用 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 定点数

    • 浮点数相对于定点数的优点是在长度一定的情况下,浮点类型取值范围大,但是不精准,适用于需要取值范围大,又可以容忍微小误差的科学计算场景(比如计算化学、分子建模、流体动力学等)
    • 定点数类型取值范围相对小,但是精准,没有误差,适合于对精度要求极高的场景 (比如涉及金额计算的场景)
  • 举例:
    - 举例:
    运行下面的语句,把test_double2表中字段“f1”的数据类型修改为 DECIMAL(5,2):

ALTER TABLE test_double2 MODIFY f1 DECIMAL(5,2);

然后,再一次运行求和语句:

11.4.2

  • “由于 DECIMAL 数据类型的精准性,在我们的项目中,除了极少数(比如商品编号)用到整数类型外,其他的数值都用的是 DECIMAL,原因就是这个项目所处的零售行业,要求精准,一分钱也不能差。 ” ——来自某项目经理

11.5 位类型:BIT

BIT类型中存储的是二进制值,类似于010101。
BIT类型,如果没有指定(M),默认是1位。这个1位,表示只能存1位的二进制值。这里(M)是表示二进制的位数,位数最小值为1,最大值为64。
注意:在向BIT类型的字段中插入数据时,一定要确保插入的数据在BIT类型支持的范围内。
使用SELECT命令查询位字段时,可以用 BIN() 或 HEX() 函数进行读取。
可以看到,使用b+0查询数据时,可以直接查询出存储的十进制数据的值。

11.6 日期与时间类型

  • 日期与时间是重要的信息,在我们的系统中,几乎所有的数据表都用得到。原因是客户需要知道数据的时间标签,从而进行数据查询、统计和处理。

  • MySQL有多种表示日期和时间的数据类型,不同的版本可能有所差异,MySQL8.0版本支持的日期和时间类型主要有:YEAR类型、TIME类型、DATE类型、DATETIME类型和TIMESTAMP类型。

    • YEAR类型通常用来表示年
    • DATE类型通常表示年、月、日
    • TIME类型通常用来表示时、分、秒
    • DATETIME类型通常表示年、月、日、时、分、秒
    • TIMESTAMP 类型通常用来表示带时区的年、月、日、时、分、秒

  • 可以看到,不同数据类型表示的时间内容不同、取值范围不同,而且占用的字节数也不一样,你要根据实际需要灵活选取。
  • 为什么时间类型 TIME 的取值范围不是 -23:59:59~23:59:59 呢?原因是 MySQL 设计的 TIME 类型,不光表示一天之内的时间,而且可以用来表示一个时间间隔,这个时间间隔可以超过 24 小时。

11.6.1 YEAR 类型

YEAR类型用来表示年份,在所有的日期时间类型中所占用的存储空间最小,只需要 1个字节 的存储空间。

  • 以4位字符串或数字格式表示YEAR类型,其格式为YYYY,最小值为1901,最大值为2155。
  • 以2位字符串格式表示YEAR类型,最小值为00,最大值为99。
    • 当取值为01到69时,表示2001到2069;
    • 当取值为70到99时,表示1970到1999;
    • 当取值整数的0或00添加的话,那么是0000年;
    • 当取值是日期/字符串的’0’添加的话,是2000年

从MySQL5.5.27开始,2位格式的YEAR已经不推荐使用。YEAR默认格式就是“YYYY”,没必要写成YEAR(4), 从MySQL 8.0.19开始,不推荐使用指定显示宽度的YEAR(4)数据类型。

11.6.2 DATE 类型

DATE类型表示日期,没有时间部分,格式为 YYYY-MM-DD ,其中,YYYY表示年份,MM表示月份,DD表示日期。需要 3个字节 的存储空间。在向DATE类型的字段插入数据时,同样需要满足一定的格式条件。

  • 以 YYYY-MM-DD 格式或者 YYYYMMDD 格式表示的字符串日期,其最小取值为1000-01-01,最大取值为9999-12-03。
  • YYYYMMDD格式会被转化为YYYY-MM-DD格式。以 YY-MM-DD 格式或YYMMDD 格式表示的字符串日期,此格式中,年份为两位数值或字符串满足YEAR类型的格式条件为:当年份取值为00到69时,会被转化为2000到2069;当年份取值为70到99时,会被转化为1970到1999。
  • 使用 CURRENT_DATE() 或者 NOW() 函数,会插入当前系统的日期。
    举例:

创建数据表,表中只包含一个DATE类型的字段f1。

CREATE TABLE test_date1(f1 DATE);

插入数据

11.6.3 TIME 类型

  • TIME类型用来表示时间,不包含日期部分。在MySQL中,需要 3个字节 的存储空间来存储TIME类型的数据,可以使用“HH:MM:SS”格式来表示TIME类型,其中,HH表示小时,MM表示分钟,SS表示秒。

  • 在MySQL中,向TIME类型的字段插入数据时,也可以使用几种不同的格式。

  • (1)可以使用带有冒号的字符串,比如’ D HH:MM:SS’ 、’ HH:MM:SS ‘、’ HH:MM ‘、’ D HH:MM ‘、’ D HH ‘或’ SS '格式,都能被正确地插入TIME类型的字段中。其中D表示天,其最小值为0,最大值为34。如果使用带有D格式的字符串插入TIME类型的字段时,D会被转化为小时,计算格式为D*24+HH。当使用带有冒号并且不带D的字符串表示时间时,表示当天的时间,比如12:10表示12:10:00,而不是00:12:10。

  • (2)可以使用不带有冒号的字符串或者数字,格式为’ HHMMSS '或者 HHMMSS 。如果插入一个不合法的字符串或者数字,MySQL在存储数据时,会将其自动转化为00:00:00进行存储。比如1210,MySQL会将最右边的两位解析成秒,表示00:12:10,而不是12:10:00。

  • (3)使用 CURRENT_TIME() 或者 NOW() ,会插入当前系统的时间。

  • 举例:

  • 创建数据表,表中包含一个TIME类型的字段f1。

CREATE TABLE test_time1(f1 TIME
);
INSERT INTO test_time1
VALUES('2 12:30:29'), ('12:35:29'), ('12:40'), ('2 12:40'),('1 05'), ('45');
INSERT INTO test_time1
VALUES ('123520'), (124011),(1210);
INSERT INTO test_time1
VALUES (NOW()), (CURRENT_TIME());SELECT * FROM test_time1;

11.6.4 DATETIME 类型

DATETIME类型在所有的日期时间类型中占用的存储空间最大,总共需要 8 个字节的存储空间。在格式上为DATE类型和TIME类型的组合,可以表示为 YYYY-MM-DD HH:MM:SS ,其中YYYY表示年份,MM表示月份,DD表示日期,HH表示时,MM表示分钟,SS表示秒。

  • 在向DATETIME类型的字段插入数据时,同样需要满足一定的格式条件

  • 以 YYYY-MM-DD HH:MM:SS 格式或者 YYYYMMDDHHMMSS 格式的字符串插入DATETIME类型的字段时,最小值为1000-01-01 00:00:00,最大值为9999-12-03 23:59:59。

    • YYYYMMDDHHMMSS格式的数字插入DATETIME类型的字段时,会被转化为YYYY-MM-DD HH:MM:SS格式。
    • 以 YY-MM-DD HH:MM:SS 格式或者 YYMMDDHHMMSS 格式的字符串插入DATETIME类型的字段时,两位数的年份规则符合YEAR类型的规则,00到69表示2000到2069;70到99表示1970到1999。
    • 使用函数 CURRENT_TIMESTAMP() 和 NOW() ,可以向DATETIME类型的字段插入系统的当前日期和时间。
  • 举例:

  • 创建数据表,表中包含一个DATETIME类型的字段dt。

CREATE TABLE test_datetime1( dt DATETIME );
Query OK, 0 rows affected (0.02 sec)

插入数据

**INSERT INTO test_datetime1
VALUES ('2021-01-01 06:50:30'), ('20210101065030'); INSERT INTO test_datetime1
VALUES ('99-01-01 00:00:00'), ('990101000000'), ('20-01-01 00:00:00'), ('200101000000'); INSERT INTO test_datetime1
VALUES (20200101000000), (200101000000), (19990101000000), (990101000000); INSERT INTO test_datetime1
VALUES (CURRENT_TIMESTAMP()), (NOW());**

11.6.5 TIMESTAMP类型

TIMESTAMP类型也可以表示日期时间,其显示格式与DATETIME类型相同,都是 YYYY-MM-DD HH:MM:SS ,需要4个字节的存储空间。但是TIMESTAMP存储的时间范围比DATETIME要小很多,只能存储“1970-01-01 00:00:01 UTC”到“2038-01-19 03:14:07 UTC”之间的时间。其中,UTC表示世界统一时间,也叫作世界标准时间。

  • 存储数据的时候需要对当前时间所在的时区进行转换,查询书籍的时候再将时间转换回当前的时区,因此,使用TIMESTAMP存储的同一个时间值,在不同的时区查询会显示不同的时间。

向TIMESTAMP类型的字段插入数据时,当插入的数据格式满足YY-MM-DD HH:MM:SS和YYMMDDHHMMSS时,两位数值的年份同样符合YEAR类型的规则条件,只不过表示的时间范围要小很多。

如果向TIMESTAMP类型的字段插入的时间超出了TIMESTAMP类型的范围,则MySQL会抛出错误信息。

  • 举例:
  • 创建数据表,表中包含一个TIMESTAMP类型的字段ts。
CREATE TABLE test_timestamp1(ts TIMESTAMP);

插入数据:

INSERT INTO test_timestamp1
VALUES ('1999-01-01 03:04:50'), ('19990101030405'), ('99-01-01 03:04:05'), ('990101030405'); INSERT INTO test_timestamp1
VALUES ('2020@01@01@00@00@00'), ('20@01@01@00@00@00'); INSERT INTO test_timestamp1
VALUES (CURRENT_TIMESTAMP()), (NOW()); #Incorrect datetime value
INSERT INTO test_timestamp1
VALUES ('2038-01-20 03:14:07');
  • 加粗样式
  • TIMESTAMP 存储空间比较小,表示的日期时间范围也比较小
  • 底层存储方式不同,TIMESTAMP底层存储的是毫秒值,距离1970-1-1 0:0:0毫秒的毫秒值。
  • 两个日期比较大小或日期计算时,TIMESTAMP更方便更快。
  • TIMESTAMP和时区有关。TIMESTAMP会根据用户的时区不同,显示不同的结果。而DATETIME则只能反映出插入时当地的时区,其他时区的人查看数据必然会有误差的。

11.6.6 开发中的经验

用的最多的日期类型是DATETIME。虽然 MySQL 也支持 YEAR(年)、 TIME(时间)、DATE(日期),以及 TIMESTAMP 类型,但是在实际项目中,尽量用 DATETIME 类型。因为这个数据类型包括了完整的日期和时间信息,取值范围也最大,使用起来比较方便。毕竟,如果日期时间信息分散在好几个字段,很不容易记,而且查询的时候,SQL 语句也会更加复杂。

此外,一般存注册时间、商品发布时间等,不建议使用DATETIME存储,而是使用 时间戳 ,因为DATETIME虽然直观,但不便于计算。

11.7 文本字符串类型

在实际的项目中,我们还经常遇到一种数据,就是字符串数据。

  • MySQL中,文本字符串总体上分为 CHAR 、 VARCHAR 、 TINYTEXT 、 TEXT 、 MEDIUMTEXT 、 LONGTEXT 、 ENUM 、 SET 等类型。

11.7.1 CHAR 与VARCHAR类型

CHAR和VARCHAR类型都可以存储比较短的字符串。
- CHAR 类型
- CHAR(M)类型一般需要预先定义字符串长度。如果不指定M,则表示默认1个字符。
- 如果保存时,数据的实际长度比CHAR类型声明的长度小,则会在右侧填充空格以达到指定的长度。当MYSQL检索CHAR类型的数据是,CHAR类型的字段会去除尾部的空格。
- 定义CHAR类型字段时,声明的字段长度即为CHAR乐西字段所占的存储空间的字节数。

  • VARCHAR类型

    • VARCHAR(M) 定义时, 必须指定 长度M,否则报错。
    • MySQL4.0版本以下,varchar(20):指的是20字节,如果存放UTF8汉字时,只能存6个(每个汉字3字节) ;MySQL5.0版本以上,varchar(20):指的是20字符。
    • 检索VARCHAR类型的字段数据时,会保留数据尾部的空格。VARCHAR类型的字段所占用的存储空间为字符串实际长度加1个字节。
CREATE TABLE test_varchar1(NAME VARCHAR # 错误
)# Column length too big for column ‘NAME’ (max =21845)
CREATE TABLE test_varchar2(NAME VARCHAR(65535) #错误
);

**哪些情况下使用CHAR或VARCHAR更好 **

  • 情况1:存储很短的信息。比如门牌号码101,201……这样很短的信息应该用char,因为varchar还要占个byte用于存储信息长度,本来打算节约存储的,结果得不偿失。
  • 情况2:固定长度的。比如使用uuid作为主键,那用char应该更合适。因为他固定长度,varchar动态根据长度的特性就消失了,而且还要占个长度信息。
  • 情况3:十分频繁改变的column。因为varchar每次存储都要有额外的计算,得到长度等工作,如果一个非常频繁改变的,那就要有很多的精力用于计算,而这些对于char来说是不需要的。
  • 情况4:具体存储引擎中的情况:
    • MyISAM 数据存储引擎和数据列:MyISAM数据表,最好使用固定长度(CHAR)的数据列代替可变长度(VARCHAR)的数据列。这样使得整个表静态化,从而使 数据检索更快 ,用空间换时间。
    • MEMORY 存储引擎和数据列:MEMORY数据表目前都使用固定长度的数据行存储,因此无论使用CHAR或VARCHAR列都没有关系,两者都是作为CHAR类型处理的。
    • InnoDB 存储引擎,建议使用VARCHAR类型。因为对于InnoDB数据表,内部的行存储格式并没有区分固定长度和可变长度列(所有数据行都使用指向数据列值的头指针),而且主要影响性能的因素是数据行使用的存储总量,由于char平均占用的空间多于varchar,所以除了简短并且固定长度的,其他考虑varchar。这样节省空间,对磁盘I/O和数据存储总量比较好。

11.7.2 TEXT 类型

在MySQL中,TEXT用来保存文本类型的字符串,总共包含4种类型,分别为TINYTEXT、TEXT、 MEDIUMTEXT 和 LONGTEXT 类型。

在向TEXT类型的字段保存和查询数据时,系统自动按照实际长度存储,不需要预先定义长度。这一点和
VARCHAR类型相同。

每种TEXT类型保存的数据长度和所占用的存储空间不同,如下:
由于实际存储的长度不确定,MySQL 不允许 TEXT 类型的字段做主键。遇到这种情况,你只能采用
CHAR(M),或者 VARCHAR(M)。

举例:
说明在保存和查询数据时,并没有删除TEXT类型的数据尾部的空格。
开发中经验:

  • TEXT文本类型,可以存比较大的文本段,搜索速度稍慢,因此如果不是特别大的内容,建议使用CHAR, VARCHAR来代替。还有TEXT类型不用加默认值,加了也没用。而且text和blob类型的数据删除后容易导致“空洞”,使得文件碎片比较多,所以频繁使用的表不建议包含TEXT类型字段,建议单独分出去,单独用一个表。

11.8 ENUM 类型

ENUM类型也叫作枚举类型,ENUM类型的取值范围需要在定义字段时进行指定。设置字段值时,ENUM
类型只允许从成员中选取单个值,不能一次选取多个值。

其所需要的存储空间由定义ENUM类型时指定的成员个数决定。

  • 当ENUM类型包含1~255个成员时,需要1个字节的存储空间;
  • 当ENUM类型包含256~65535个成员时,需要2个字节的存储空间。
  • ENUM类型的成员个数的上限为65535个。

举例:

11.9 SET 类型

SET表示一个字符串对象,可以包含0个或多个成员,但成员个数的上限为 64 。设置字段值时,可以取
取值范围内的 0 个或多个值。

当SET类型包含的成员个数不同时,其所占用的存储空间也是不同的,具体如下

SET类型在存储数据时成员个数越多,其占用的存储空间越大。注意:SET类型在选取成员时,可以一次
选择多个成员,这一点与ENUM类型不同。

11.10 二进制字符串类型

MySQL中的二进制字符串类型主要存储一些二进制数据,比如可以存储图片、音频和视频等二进制数
据。

MySQL中支持的二进制字符串类型主要包括BINARY、VARBINARY、TINYBLOB、BLOB、MEDIUMBLOB 和 LONGBLOB类型。

BINARY与VARBINARY类型
BINARY和VARBINARY类似于CHAR和VARCHAR,只是它们存储的是二进制字符串。

BINARY (M)为固定长度的二进制字符串,M表示最多能存储的字节数,取值范围是0~255个字符。如果未指定(M),表示只能存储 1个字节 。例如BINARY (8),表示最多能存储8个字节,如果字段值不足(M)个字节,将在右边填充’\0’以补齐指定长度。

VARBINARY (M)为可变长度的二进制字符串,M表示最多能存储的字节数,总字节数不能超过行的字节长度限制65535,另外还要考虑额外字节开销,VARBINARY类型的数据除了存储数据本身外,还需要1或2个字节来存储数据的字节数。VARBINARY类型 必须指定(M) ,否则报错

  • BLOB类型
    BLOB是一个 二进制大对象 ,可以容纳可变数量的数据。

MySQL中的BLOB类型包括TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB 4种类型,它们可容纳值的最大长度不同。可以存储一个二进制的大对象,比如 图片 、 音频 和 视频 等。

需要注意的是,在实际工作中,往往不会在MySQL数据库中使用BLOB类型存储大对象数据,通常会将图
片、音频和视频文件存储到 服务器的磁盘上 ,并将图片、音频和视频的访问路径存储到MySQL中。

TEXT和BLOB的使用注意事项:

  • 在使用text和blob字段类型时要注意以下几点,以便更好的发挥数据库的性能。
  • ① BLOB和TEXT值也会引起自己的一些问题,特别是执行了大量的删除或更新操作的时候。删除这种值会在数据表中留下很大的" 空洞 ",以后填入这些"空洞"的记录可能长度不同。为了提高性能,建议定期使用 OPTIMIZE TABLE 功能对这类表进行 碎片整理
  • ② 如果需要对大文本字段进行模糊查询,MySQL 提供了 前缀索引 。但是仍然要在不必要的时候避免检索大型的BLOB或TEXT值。例如,SELECT * 查询就不是很好的想法,除非你能够确定作为约束条件的WHERE子句只会找到所需要的数据行。否则,你可能毫无目的地在网络上传输大量的值。
  • ③ 把BLOB或TEXT列 分离到单独的表 中。在某些环境中,如果把这些数据列移动到第二张数据表中,可以让你把原数据表中的数据列转换为固定长度的数据行格式,那么它就是有意义的。这会 减少主表中的 碎片 ,使你得到固定长度数据行的性能优势。它还使你在主数据表上运行 SELECT * 查询的时候不会通过网络传输大量的BLOB或TEXT值。

11.11 JOSN 类型

JSON(JavaScript Object Notation)是一种轻量级的 数据交换格式 。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。它易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效
率。JSON 可以将 JavaScript 对象中表示的一组数据转换为字符串,然后就可以在网络或者程序之间轻
松地传递这个字符串,并在需要的时候将它还原为各编程语言所支持的数据格式。

在MySQL 5.7中,就已经支持JSON数据类型。在MySQL 8.x版本中,JSON类型提供了可以进行自动验证的JSON文档和优化的存储结构,使得在MySQL中存储和读取JSON类型的数据更加方便和高效。 创建数据表,表中包含一个JSON类型的字段 js 。

11.12 空间类型

MySQL 空间类型扩展支持地理特征的生成、存储和分析。这里的地理特征表示世界上具有位置的任何东
西,可以是一个实体,例如一座山;可以是空间,例如一座办公楼;也可以是一个可定义的位置,例如
一个十字路口等等。MySQL中使用 Geometry(几何) 来表示所有地理特征。Geometry指一个点或点的
集合,代表世界上任何具有位置的事物。

MySQL的空间数据类型(Spatial Data Type)对应于OpenGIS类,包括单值类型:GEOMETRY、POINT、 LINESTRING、POLYGON以及集合类型:MULTIPOINT、MULTILINESTRING、MULTIPOLYGON、 GEOMETRYCOLLECTION 。

  • Geometry是所有空间集合类型的基类,其他类型如POINT、LINESTRING、POLYGON都是Geometry的子类。

    • Point,顾名思义就是点,有一个坐标值。例如POINT(121.213342 31.234532),POINT(30 10),坐标值支持DECIMAL类型,经度(longitude)在前,维度(latitude)在后,用空格分隔。
    • LineString,线,由一系列点连接而成。如果线从头至尾没有交叉,那就是简单的(simple);如果起点和终点重叠,那就是封闭的(closed)。例如LINESTRING(30 10,10 30,40 40),点与点之间用逗号分隔,一个点中的经纬度用空格分隔,与POINT格式一致。
    • Polygon,多边形。可以是一个实心平面形,即没有内部边界,也可以有空洞,类似纽扣。最简单的就是只有一个外边界的情况,例如POLYGON((0 0,10 0,10 10, 0 10))。

11.13 小结及选择建议

在定义数据类型时,如果确定是 整数 ,就用 INT ; 如果是 小数 ,一定用定点数类型DECIMAL(M,D) ; 如果是日期与时间,就用DATETIME 。

这样做的好处是,首先确保你的系统不会因为数据类型定义出错。不过,凡事都是有两面的,可靠性好,并不意味着高效。比如,TEXT 虽然使用方便,但是效率不如 CHAR(M) 和 VARCHAR(M)。
关于字符串的选择,建议参考如下阿里巴巴的《Java开发手册》规范:

  • 阿里巴巴《Java开发手册》之MySQL数据库:

    • 任何字段如果为非负数,必须是 UNSIGNED
    • 【 强制 】小数类型为 DECIMAL,禁止使用 FLOAT 和 DOUBLE。
      • 说明:在存储的时候,FLOAT 和 DOUBLE 都存在精度损失的问题,很可能在比较值的时候,得到不正确的结果。如果存储的数据范围超过 DECIMAL 的范围,建议将数据拆成整数和小数并分开存储。
  • 【 强制 】如果存储的字符串长度几乎相等,使用 CHAR 定长字符串类型。

  • 【 强制 】VARCHAR 是可变长字符串,不预先分配存储空间,长度不要超过 5000。如果存储长度大于此值,定义字段类型为 TEXT,独立出来一张表,用主键来对应,避免影响其它字段索引效率。

MySQL学习笔记-基础篇2相关推荐

  1. MySQL学习笔记-基础篇1

    MySQL 学习笔记–基础篇1 目录 MySQL 学习笔记--基础篇1 1. 数据库概述与MySQL安装 1.1 数据库概述 1.1.1 为什么要使用数据库 1.2 数据库与数据库管理系统 1.2.1 ...

  2. Redis学习笔记①基础篇_Redis快速入门

    若文章内容或图片失效,请留言反馈.部分素材来自网络,若不小心影响到您的利益,请联系博主删除. 资料链接:https://pan.baidu.com/s/1189u6u4icQYHg_9_7ovWmA( ...

  3. 数据库MySQL学习笔记高级篇(周阳)

    数据库MySQL学习笔记高级篇 1. mysql的架构介绍 mysql简介 高级Mysql mysqlLinux版的安装 mysql配置文件 mysql逻辑架构介绍 mysql存储引擎 2. 索引优化 ...

  4. extlink.php,ExtJs 学习笔记基础篇 Ext组件的使用_extjs

    昨天刚接触到Extjs,简单写了篇学习笔记,今天继续. 天介绍一下Ext中组件举几个简单的例子做说明.注意:文章内容有些摘自本人学习过程中看到的资料. Ext2.0对框架进行了非常大的重构,其中最重要 ...

  5. MySQL数据库笔记-基础篇

    文章目录 MySQL概述 数据库相关概念 小总结 MySQL数据库 版本: 启动与停止 客户端连接 关系型数据库 数据模型 小总结 SQL SQL通用语法 SQL分类 DDL 数据库操作 查询 创建 ...

  6. python3多线程编程_Python 3多线程编程学习笔记-基础篇

    本文是学习<Python核心编程>的学习笔记,介绍了Python中的全局解释器锁和常用的两个线程模块:thread, threading,并对比他们的优缺点和给出简单的列子. 全局解释器锁 ...

  7. C++ 学习笔记----基础篇

    (一)类和抽象数据类型   1.通过类实现的接口(公有和私有)可实现信息隐藏.实现对数据的封装等: 2.抽象数据类型(ADT:Abstract Data Type):当一个数据类型仅暴露公有接口,而将 ...

  8. java学习笔记-基础篇

    Java基础篇 1-12 常识 13 this关键字 14参数传递 16 继承 17 访问权限 28-31异常 1-12 常识 1.文件夹以列表展示,显示扩展名,在地址栏显示全路径 2.javac编译 ...

  9. 易语言学习笔记——基础篇

    易语言学习笔记20180710 一. 易语言的数据类型可以分为基本数据类型和特殊数据类型 1.     其中基本数据类型分为: ①   数值型 ②   逻辑型 ③   日期时间型 ④   文本型 ⑤  ...

最新文章

  1. 监控平台zabbix高级配置
  2. linux数组shell数组添加内容,shell数组的定义与应用
  3. 别忽视新冠轻症,它会损害你的记忆力
  4. wxWidgets:获取主机的IP地址
  5. 揭秘 | 小米最新款12PRO智能动态刷新率技术原理
  6. 低代码发展专访系列之六:低代码平台能解决业务重构的问题么?
  7. 先查询后修改并发的时候sql_如何解决并发场景下扣款的数据一致性问题?
  8. 第一个SSCLI范例:echo的调试问题
  9. ajax beforeSend中无效果
  10. php如何查询本周的数据,php、mysql查询当天,查询本周,查询本月的数据实例
  11. unity有用资源的导出未package便于在其他工程用的问题解决
  12. table切换数据 vue_Vue实现表格中对数据进行转换、处理的方法
  13. mysql 主从服务-主从复制数据一致性校验出现的问题
  14. 互联网晚报 | 05月31日 星期二 | 教育部将彻查教材插图问题;​6月1日上海有序恢复相关秩序;近半数职场人年终奖少于1个月...
  15. python内置模块re_python内置模块[re]
  16. 阿里性能专家全方位对比Jmeter和Locust,到底谁更香?
  17. 同时查询京东多个快递物流,并分析中转延误
  18. mmap(内存映射)、sendfile() 与零拷贝技术
  19. 解决微信电脑版备份,手机端出现目前网络状况复杂的问题:当前网络状况复杂,请尝试使用其它网络
  20. e3服务器性能,揭Xeon E3-1200 V3变化:性能测试见分晓

热门文章

  1. ECl@SS学习笔记(2)采纳的国际标准与信息模型
  2. 新东方在厦门大学演讲--笔摘
  3. java中用户登陆密码加密方法
  4. 基于cw32f030c8t6的多功能语音播报提醒装置装置
  5. 一文理解Linux的基本指令(下)(三分钟学会Linux基本指令)
  6. 423 从英文中重建数字(找规律)
  7. 《当代教育科学(原:山东教育科研)》投稿经验分享
  8. IDEA注释设置:单行或多行注释规则
  9. html缩小页面div换行,当浏览器窗口缩小时,CSS水平菜单换行
  10. 计算机学院 铁道交通运营管理,成都动车学校铁道交通运营管理专业