01mysql

--------笔记1---------

一. MySQL基础篇

(一) Linux(CentOS 7)安装MySQL 5.6

卸载

1 查看MySQL软件
rpm -qa|grep mysql 
yum repolist all | grep mysql 
2 卸载
yum remove -y mysql mysql-libs mysql-common #卸载mysql 
rm -rf /var/lib/mysql #删除mysql下的数据文件 
rm /etc/my.cnf #删除mysql配置文件 
yum remove -y mysql-community-release-el6-5.noarch #删除组件

安装

1 下载MySQL
#下载rpm文件
wget http://repo.mysql.com/mysql-community-release-el6-5.noarch.rpm
#执行rpm源文件
rpm -ivh mysql-community-release-el6-5.noarch.rpm
2 执行、启动MySQL
#执行安装文件
yum install mysql-community-server
#启动MySQL
systemctl start mysqld
3 设置密码(将 root 账号设置密码为 root )
#没有原密码
/usr/bin/mysqladmin -u root password 'root'
#有原来的密码,则加(一般)
/usr/bin/mysqladmin -u root -p '123' password 'root'
4 登录MySQL
mysql -uroot -proot
#-u:指定数据库用户名
#-p:指定数据库密码,记住-u和登录密码之间没有空格
5 修改配置
vim /etc/my.cnf

注:配置文件

[mysqld]
# MySQL设置大小写不敏感:默认:区分表名的大小写,不区分列名的大小写
# 0:大小写敏感 1:大小写不敏感
lower_case_table_names=1
# 默认字符集
character-set-server=utf8
6 MySQL远程连接授权(grant 权限 on 数据库对象 to 用户)
mysql>GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION; 
#赋予root用户对所有数据库对象的全部操作权限:
FLUSH PRIVILEGES;   
#刷新权限

命令说明:

	ALL PRIVILEGES :表示授予所有的权限,此处可以指定具体的授权权限。
	*.* :表示所有库中的所有表
	'root'@'%' : myuser是数据库的用户名,%表示是任意ip地址,可以指定具体ip地址。
	IDENTIFIED BY 'mypassword' :mypassword是数据库的密码。
7 关闭linux的防火墙
systemctl stop firewalld          		(默认)
systemctl disable firewalld.service   	(设置开启不启动)
8 客户端远程访问
利用navicat可以远程访问MySQL

注:如果连接不上,可以按以下步骤排错
1、MySQL是否正常启动
[root@localhost ~]# ps -ef | grep mysql
root 1114 1 0 10:21 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe -
-datadir=/var/lib/mysql --socket=/var/lib/mysql/mysql.sock --pidfile=/var/run/mysqld/mysqld.pid --basedir=/usr --user=mysql
mysql 1698 1114 0 10:21 ? 00:00:03 /usr/sbin/mysqld

2、查看防火墙是否关闭
[root@localhost ~]# systemctl status firewalld
firewalld.service - firewalld - dynamic firewall daemon
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled)
Active: inactive (dead)

3、查看root权限为所有ip都可以访问
mysql> show grants for root;
+-------------------------------------------------------------------------------
-------------------------------------------------+
| Grants for root@%
| GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY PASSWORD
'*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B' WITH GRANT OPTION |
+-----------------------------------------------------------------

4、服务器与客户端是否可以ping通
ping 192.168.239.129
正在 Ping 192.168.239.129 具有 32 字节的数据:
来自 192.168.239.129 的回复: 字节=32 时间<1ms TTL=64

5、客户端是否可以telnet到服务器端
telnet 192.168.239.129 3306

6、Navicat是否正确安装

(二) SQL语言分类

- 数据定义语言:简称【DDL】(Data Definition Language),用来定义数据库对象:数据库,表,列 等。关键字:create,alter,drop等 

- 数据操作语言:简称【DML】(Data Manipulation Language),用来对数据库中表的记录进行更 新。关键字:insert,delete,update等 

- 数据控制语言:简称【DCL】(Data Control Language),用来定义数据库的访问权限和安全级别, 及创建用户;关键字:grant等 

- 数据查询语言:简称【DQL】(Data Query Language),用来查询数据库中表的记录。关键字: select,from,where等

DDL语句

1.数据库操作:database
#创建数据库
create database 数据库名; 
create database 数据库名 character set 字符集

#查看所有的数据库
show databases;
#查看指定的数据库
show create database 数据库名;

#删除数据库
drop database 数据库名称;

#选择(切换)要使用的数据库
use 数据库名;

#查看当前使用的数据库
select database();
2.表操作:table
#创建表
create table 表名( 
    字段名 类型(长度) 约束, 
    字段名 类型(长度) 约束 
);

#约束条件(主键约束 = 唯一约束 + 非空约束)
- 主键约束:primary key 
- 唯一约束:unique 
- 非空约束:not null

#查看所有的表
show tables; 
#查看表结构
desc 表名;

#删除表
drop table 表名;

#修改表
alter table 表名 add 列名 类型(长度) 约束; 				   --修改表添加列. 
alter table 表名 modify 列名 类型(长度) 约束; 			   --修改表修改列的类型长度及约束. 
alter table 表名 change 旧列名 新列名 类型(长度) 约束; 		 --修改表修改列名. 
alter table 表名 drop 列名; 								--修改表删除列. 
rename table 表名 to 新表名; 								--修改表名 
alter table 表名 character set 字符集; 						--修改表的字符集 

DML语句

1.插入记录:insert
insert into 表 (列名1,列名2,列名3..) values (值1,值2,值3..); 		-- 向表中插入某些列 
insert into 表 values (值1,值2,值3..); 							--向表中插入所有列 

insert into 表 (列名1,列名2,列名3..) values select (列名1,列名2,列名3..) from 表 
insert into 表 values select * from 表

#例子
	INSERT INTO sort(sid,sname) VALUES('s001', '电器'); 
	INSERT INTO sort VALUES('s004','书籍');

注:

1. 列名数与values后面的值的个数相等
2. 列的顺序与插入的值得顺序一致
3. 列名的类型与插入的值要一致. 
4. 插入值得时候不能超过最大长度. 
5. 值如果是字符串或者日期需要加引号’’ (一般是单引号)
2. 更新记录:update
update 表名 set 字段名=值,字段名=值; 
update 表名 set 字段名=值,字段名=值 where 条件;        			#常用

注:

1. 列名的类型与修改的值要一致. 
2. 修改值得时候不能超过最大长度. 
3. 值如果是字符串或者日期需要加’’.
3.删除记录:delete
delete from 表名 [where 条件];

注:

删除表中所有记录使用【delete from 表名】,还是用【truncate table 表名】? 

删除方式: - delete :一条一条删除,不清空auto_increment记录数。 
		- truncate :直接将表删除,重新建表,auto_increment将置为零,从新开始。

DQL语句

提前创建表

#商品表 
CREATE TABLE product ( 
	pid INT PRIMARY KEY AUTO_INCREMENT, # 自增加 AUTO_INCREMENT 
	pname VARCHAR(20),#商品名称 
	price DOUBLE, #商品价格 
	pdate DATE, # 日期 
	cid int #分类ID 
);

#目录表 
create table category( 
    id INT PRIMARY KEY , 
    cname varchar(100) 
);

INSERT INTO product VALUES(NULL,'泰国大榴莲', 98, NULL, 1); 
INSERT INTO product VALUES(NULL,'泰国大枣', 38, NULL, 1); 
INSERT INTO product VALUES(NULL,'新疆切糕', 68, NULL, 2); 
INSERT INTO product VALUES(NULL,'十三香', 10, NULL, 2); 
INSERT INTO product VALUES(NULL,'泰国大枣', 20, NULL, 2);
insert into product values(null,'泰国大枣',98,null,20); #没有对应 

insert into product values(null,'iPhone手机',800,null,30);#没有对应 
INSERT INTO category VALUES(1,'国外食品'); 
INSERT INTO category VALUES(2,'国内食品'); 
INSERT INTO category VALUES(3,'国内服装'); #没有对应
简单查询
select * from product;		--查询所有的商品.
select pname,price from product;  --查询商品名和商品价格.
select * from product as p;		--别名查询,使用的as关键字,as可以省略的.表别名:
select pname as pn from product; 	--列别名
select distinct price from product; 	--去重
select pname,price+10 from product;			--查询结果是表达式(运算查询):将所有商品的价格+10元进行显示.
条件查询(> ,<,=,>=,<=,<> like )
select * from product where pname = '十三香';	--查询商品名称为十三香的商品所有信息:
select * from product where price > 60;			--查询商品价格>60元的所有的商品信息: 
select * from product where pname like '%新%'; --使用占位符 _ 和 % _代表一个字符 %代表任意个字符.
select * from product where pid in (2,5,8);		--in在某个范围中获得值(exists). 
排序(asc-升序,desc-降序)
select * from product order by price;	--查询所有的商品,按价格进行排序
select * from product where pname like '%新%' order by price desc;
									--查询名称有新的商品的信息并且按价格降序排序.
聚合函数,分组函数

注:

sum():求某一列的和 
avg():求某一列的平均值 
max():求某一列的最大值 
min():求某一列的最小值 
count():求某一列的元素个数
select sum(price) from product; 	--获得所有商品的价格的总和:
select avg(price) from product; 	--获得所有商品的价格的平均值:
select count(*) from product;		--获得所有商品的个数:

select cid,count(*) from product group by cid; 	--根据cno字段分组,分组后统计商品的个数
select cid,avg(price) from product group by cid having avg(price)>60;
                       --根据cno分组,分组统计每组商品的平均价格,并且平均价格> 60;
#注意事项          
1. select语句中的列(非聚合函数列),必须出现在group by子句中 
2. group by子句中的列,不一定要出现在select语句中 
3. 聚合函数只能出现select语句中或者having语句中,一定不能出现在where语句中。					
分页查询
#格式
SELECT * FROM table LIMIT [offset,] rows
						--offset :偏移量	rows :每页多少行记录。
#注意
	逻辑分页:将数据库中的数据查询到内存之后再进行分页。 
	物理分页:通过LIMIT关键字,直接在数据库中进行分页,最终返回的数据,只是分页后的数据。(limit)
子查询
#定义
	子查询允许把一个查询嵌套在另一个查询当中。 
	子查询,又叫内部查询,相对于内部查询,包含内部查询的就称为外部查询。 
	子查询可以包含普通select可以包括的任何子句,比如:distinct、 group by、order by、limit、 join和union等; 
	但是对应的外部查询必须是以下语句之一:select、insert、update、delete。

#位置
	select中、from 后、where 中. 
	group by 和order by 中无实用意义。
其他查询语句(union)
union 集合的并集(不包含重复记录)
union all 集合的并集(包含重复记录)

(三) SQL解析顺序

1. FROM(将最近的两张表,进行笛卡尔积)---VT1 
2. ON(将VT1按照它的条件进行过滤)---VT2 
3. LEFT JOIN(保留左表的记录)---VT3 
4. WHERE(过滤VT3中的记录)--VT4…VTn 
5. GROUP BY(对VT4的记录进行分组)---VT5 
6. HAVING(对VT5中的记录进行过滤)---VT6 
7. SELECT(对VT6中的记录,选取指定的列)--VT7 
8. ORDER BY(对VT7的记录进行排序)--VT8 
9. LIMIT(对排序之后的值进行分页)--MySQL特有的语法

(四) 表与表之间的关系

  • 一对一关系

    常见实例:一夫一妻

  • 一对多关系

    常见实例:会员和订单

  • 多对多关系(需要中间表实现)

    常见实例:商品和订单

(五) 多表关联查询

JOIN 按照功能大致分为如下三类:

#CROSS JOIN (交叉连接)							
	**交叉连接也叫笛卡尔积连接。****笛卡尔积**是指在数学中,两个集合 X 和 Y 的笛卡尓积( Cartesian 
    product ),又称直积,表示为 X*Y ,第一个对象是 X 的成员而第二个对象是 Y 的所有可能有序对的其
    中一个成员。

#INNER JOIN (内连接或等值连接)。
	内连接也叫**等值连接**,内联接使用比较运算符根据每个表共有的列的值匹配两个表中的行。
	
#OUTER JOIN (外连接)  
	左外连接left join、右外连接right join、全外连接join

(补) MySQL补充知识点

基本数据类型

数值类型
类型大小范围(有符号)范围(无符号)用途
TINYINT1 Bytes(-128,127)(0,255)小整数值
SMALLINT2 Bytes(-32 768,32 767)(0,65 535)大整数值
MEDIUMINT3 Bytes(-8 388 608,8 388 607)(0,16 777 215)大整数值
INT或INTEGER4 Bytes(-2 147 483 648,2 147 483 647)(0,4 294 967 295)大整数值
BIGINT8 Bytes(-9,223,372,036,854,775,808,9 223 372 036 854 775 807)(0,18 446 744 073 709 551 615)极大整数值
FLOAT4 Bytes(-3.402 823 466 E+38,-1.175 494 351 E-38),0,(1.175 494 351 E-38,3.402 823 466 351 E+38)0,(1.175 494 351 E-38,3.402 823 466 E+38)单精度 浮点数值
DOUBLE8 Bytes(-1.797 693 134 862 315 7 E+308,-2.225 073 858 507 201 4 E-308),0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308)0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308)双精度 浮点数值
DECIMAL对DECIMAL(M,D) ,如果M>D,为M+2否则为D+2依赖于M和D的值依赖于M和D的值小数值

日期和时间类型
类型大小 ( bytes)范围格式用途
DATE31000-01-01/9999-12-31YYYY-MM-DD日期值
TIME3‘-838:59:59’/‘838:59:59’HH:MM:SS时间值或持续时间
YEAR11901/2155YYYY年份值
DATETIME81000-01-01 00:00:00/9999-12-31 23:59:59YYYY-MM-DD HH:MM:SS混合日期和时间值
TIMESTAMP41970-01-01 00:00:00/2038结束时间是第 2147483647 秒,北京时间 2038-1-19 11:14:07,格林尼治时间 2038年1月19日 凌晨 03:14:07YYYYMMDD HHMMSS混合日期和时间值,时间戳

字符串类型
类型大小用途
CHAR0-255 bytes定长字符串
VARCHAR0-65535 bytes变长字符串
TINYBLOB0-255 bytes不超过 255 个字符的二进制字符串
TINYTEXT0-255 bytes短文本字符串
BLOB0-65 535 bytes二进制形式的长文本数据
TEXT0-65 535 bytes长文本数据
MEDIUMBLOB0-16 777 215 bytes二进制形式的中等长度文本数据
MEDIUMTEXT0-16 777 215 bytes中等长度文本数据
LONGBLOB0-4 294 967 295 bytes二进制形式的极大文本数据
LONGTEXT0-4 294 967 295 bytes极大文本数据
数字型:int 
浮点型:double 
字符型:varchar(可变长字符串) 
日期类型:date(只有年月日,没有时分秒) 
		datetime(年月日,时分秒) 
		timestamp
boolean类型:不支持,一般使用tinyint替代(值为0和1)

比较运算符

符号描述备注
=等于
<>, !=不等于
>大于
<小于
<=小于等于
>=大于等于
BETWEEN在两值之间>=min&&<=max
NOT BETWEEN不在两值之间
IN在集合中
NOT IN不在集合中
<=>严格比较两个NULL值是否相等两个操作码均为NULL时,其所得值为1;而当一个操作码为NULL时,其所得值为0
LIKE模糊匹配
REGEXP 或 RLIKE正则式匹配
IS NULL为空
IS NOT NULL不为空

常见函数

MySQL 字符串函数
函数描述实例
CHAR_LENGTH(s)返回字符串 s 的字符数返回字符串 RUNOOB 的字符数SELECT CHAR_LENGTH("RUNOOB") AS LengthOfString;
CONCAT(s1,s2…sn)字符串 s1,s2 等多个字符串合并为一个字符串合并多个字符串SELECT CONCAT("SQL ", "Runoob ", "Gooogle ", "Facebook") AS ConcatenatedString;
FORMAT(x,n)函数可以将数字 x 进行格式化 “#,###.##”, 将 x 保留到小数点后 n 位,最后一位四舍五入。格式化数字 “#,###.##” 形式:SELECT FORMAT(250500.5634, 2); -- 输出 250,500.56
LOWER(s)将字符串 s 的所有字母变成小写字母字符串 RUNOOB 转换为小写:SELECT LOWER('RUNOOB') -- runoob
REVERSE(s)将字符串s的顺序反过来将字符串 abc 的顺序反过来:SELECT REVERSE('abc') -- cba
STRCMP(s1,s2)比较字符串 s1 和 s2,如果 s1 与 s2 相等返回 0 ,如果 s1>s2 返回 1,如果 s1<s2 返回 -1比较字符串:SELECT STRCMP("runoob", "runoob"); -- 0
TRIM(s)去掉字符串 s 开始和结尾处的空格去掉字符串 RUNOOB 的首尾空格:SELECT TRIM(' RUNOOB ') AS TrimmedString;
UPPER(s)将字符串转换为大写将字符串 runoob 转换为大写:SELECT UPPER("runoob"); -- RUNOOB

MySQL 数字函数
函数名描述实例
ABS(x)返回 x 的绝对值返回 -1 的绝对值:SELECT ABS(-1) -- 返回1
FLOOR(x)返回小于或等于 x 的最大整数小于或等于 1.5 的整数:SELECT FLOOR(1.5) -- 返回1
GREATEST(expr1, expr2, expr3, …)返回列表中的最大值返回以下数字列表中的最大值:SELECT GREATEST(3, 12, 34, 8, 25); -- 34返回以下字符串列表中的最大值:SELECT GREATEST("Google", "Runoob", "Apple"); -- Runoob
LEAST(expr1, expr2, expr3, …)返回列表中的最小值返回以下数字列表中的最小值:SELECT LEAST(3, 12, 34, 8, 25); -- 3返回以下字符串列表中的最小值:SELECT LEAST("Google", "Runoob", "Apple"); -- Apple
MAX(expression)返回字段 expression 中的最大值返回数据表 Products 中字段 Price 的最大值:SELECT MAX(Price) AS LargestPrice FROM Products;
MIN(expression)返回字段 expression 中的最小值返回数据表 Products 中字段 Price 的最小值:SELECT MIN(Price) AS MinPrice FROM Products;
MOD(x,y)返回 x 除以 y 以后的余数5 除于 2 的余数:SELECT MOD(5,2) -- 1
POW(x,y)返回 x 的 y 次方2 的 3 次方:SELECT POW(2,3) -- 8
POWER(x,y)返回 x 的 y 次方2 的 3 次方:SELECT POWER(2,3) -- 8
RAND()返回 0 到 1 的随机数SELECT RAND() --0.93099315644334
ROUND(x)返回离 x 最近的整数SELECT ROUND(1.23456) --1
SIGN(x)返回 x 的符号,x 是负数、0、正数分别返回 -1、0 和 1SELECT SIGN(-10) -- (-1)
SIN(x)求正弦值(参数是弧度)SELECT SIN(RADIANS(30)) -- 0.5
SQRT(x)返回x的平方根25 的平方根:SELECT SQRT(25) -- 5
SUM(expression)返回指定字段的总和计算 OrderDetails 表中字段 Quantity 的总和:SELECT SUM(Quantity) AS TotalItemsOrdered FROM OrderDetails;
TRUNCATE(x,y)返回数值 x 保留到小数点后 y 位的值(与 ROUND 最大的区别是不会进行四舍五入)SELECT TRUNCATE(1.23456,3) -- 1.234

MySQL 日期函数
函数名实例描述
ADDDATE(d,n)SELECT ADDDATE("2017-06-15", INTERVAL 10 DAY); ->2017-06-25计算起始日期 d 加上 n 天的日期
ADDTIME(t,n)加 5 秒:SELECT ADDTIME('2011-11-11 11:11:11', 5); ->2011-11-11 11:11:16 (秒)添加 2 小时, 10 分钟, 5 秒:SELECT ADDTIME("2020-06-15 09:34:21", "2:10:5"); -> 2020-06-15 11:44:26n 是一个时间表达式,时间 t 加上时间表达式 n
CURRENT_DATE()SELECT CURRENT_DATE(); -> 2018-09-19返回当前日期
CURRENT_TIMESELECT CURRENT_TIME(); -> 19:59:02返回当前时间
CURRENT_TIMESTAMP()SELECT CURRENT_TIMESTAMP() -> 2018-09-19 20:57:43返回当前日期和时间
DATE()SELECT DATE("2017-06-15"); -> 2017-06-15从日期或日期时间表达式中提取日期值
DATEDIFF(d1,d2)SELECT DATEDIFF('2001-01-01','2001-02-02') -> -32计算日期 d1->d2 之间相隔的天数
DATE_FORMAT(d,f)SELECT DATE_FORMAT('2011-11-11 11:11:11','%Y-%m-%d %r') -> 2011-11-11 11:11:11 AM按表达式 f的要求显示日期 d
DATE_SUB(date,INTERVAL expr type)Orders 表中 OrderDate 字段减去 2 天:SELECT OrderId,DATE_SUB(OrderDate,INTERVAL 2 DAY) AS OrderPayDate FROM Orders函数从日期减去指定的时间间隔。
DAY(d)SELECT DAY("2017-06-15"); -> 15返回日期值 d 的日期部分
DAYNAME(d)SELECT DAYNAME('2011-11-11 11:11:11') ->Friday返回日期 d 是星期几,如 Monday,Tuesday
DAYOFMONTH(d)SELECT DAYOFMONTH('2011-11-11 11:11:11') ->11计算日期 d 是本月的第几天
DAYOFWEEK(d)SELECT DAYOFWEEK('2011-11-11 11:11:11') ->6日期 d 今天是星期几,1 星期日,2 星期一,以此类推
DAYOFYEAR(d)SELECT DAYOFYEAR('2011-11-11 11:11:11') ->315计算日期 d 是本年的第几天
EXTRACT(type FROM d)SELECT EXTRACT(MINUTE FROM '2011-11-11 11:11:11') -> 11从日期 d 中获取指定的值,type 指定返回的值。 type可取值为: MICROSECONDSECONDMINUTEHOURDAYWEEKMONTHQUARTERYEARSECOND_MICROSECONDMINUTE_MICROSECONDMINUTE_SECONDHOUR_MICROSECONDHOUR_SECONDHOUR_MINUTEDAY_MICROSECONDDAY_SECONDDAY_MINUTEDAY_HOURYEAR_MONTH
FROM_DAYS(n)SELECT FROM_DAYS(1111) -> 0003-01-16计算从 0000 年 1 月 1 日开始 n 天后的日期
HOUR(t)SELECT HOUR('1:2:3') -> 1返回 t 中的小时值
LAST_DAY(d)SELECT LAST_DAY("2017-06-20"); -> 2017-06-30返回给给定日期的那一月份的最后一天
MINUTE(t)SELECT MINUTE('1:2:3') -> 2返回 t 中的分钟值
MONTHNAME(d)SELECT MONTHNAME('2011-11-11 11:11:11') -> November返回日期当中的月份名称,如 November
MONTH(d)SELECT MONTH('2011-11-11 11:11:11') ->11返回日期d中的月份值,1 到 12
NOW()SELECT NOW() -> 2018-09-19 20:57:43返回当前日期和时间
PERIOD_ADD(period, number)SELECT PERIOD_ADD(201703, 5); -> 201708为 年-月 组合日期添加一个时段
PERIOD_DIFF(period1, period2)SELECT PERIOD_DIFF(201710, 201703); -> 7返回两个时段之间的月份差值
QUARTER(d)SELECT QUARTER('2011-11-11 11:11:11') -> 4返回日期d是第几季节,返回 1 到 4
SECOND(t)SELECT SECOND('1:2:3') -> 3返回 t 中的秒钟值
SEC_TO_TIME(s)SELECT SEC_TO_TIME(4320) -> 01:12:00将以秒为单位的时间 s 转换为时分秒的格式
STR_TO_DATE(string, format_mask)SELECT STR_TO_DATE("August 10 2017", "%M %d %Y"); -> 2017-08-10将字符串转变为日期

MySQL 高级函数
函数名描述实例
BIN(x)返回 x 的二进制编码15 的 2 进制编码:SELECT BIN(15); -- 1111
BINARY(s)将字符串 s 转换为二进制字符串SELECT BINARY "RUNOOB"; -> RUNOOB
CAST(x AS type)转换数据类型字符串日期转换为日期:SELECT CAST("2017-08-29" AS DATE); -> 2017-08-29
COALESCE(expr1, expr2, …, expr_n)返回参数中的第一个非空表达式(从左向右)SELECT COALESCE(NULL, NULL, NULL, 'runoob.com', NULL, 'google.com'); -> runoob.com
CONV(x,f1,f2)返回 f1 进制数变成 f2 进制数SELECT CONV(15, 10, 2); -> 1111
CONVERT(s USING cs)函数将字符串 s 的字符集变成 csSELECT CHARSET('ABC') ->utf-8 SELECT CHARSET(CONVERT('ABC' USING gbk)) ->gbk
IF(expr,v1,v2)如果表达式 expr 成立,返回结果 v1;否则,返回结果 v2。SELECT IF(1 > 0,'正确','错误') ->正确
IFNULL(v1,v2)如果 v1 的值不为 NULL,则返回 v1,否则返回 v2。SELECT IFNULL(null,'Hello Word') ->Hello Word
ISNULL(expression)判断表达式是否为 NULLSELECT ISNULL(NULL); ->1

二. MySQL架构篇

(一) MySQL文件结构

日志文件(顺序IO)

查看当前数据库中的日志使用信息:

mysql> show variables like 'log_%';
1)错误日志(errorlog)
2)二进制日志(bin log)
3)通用查询日志(general query log)
4)慢查询日志(slow query log)

数据文件(随机IO)

查看MySQL数据文件:

SHOW VARIABLES LIKE '%datadir%';
1)InnoDB数据文件
.frm文件:主要存放与表相关的数据信息,主要包括表结构的定义信息 (表结构)
.ibd:使用独享表空间存储表数据和索引信息,一张表对应一个ibd文件。 (表数据和索引)
.ibdata文件:使用共享表空间存储表数据和索引信息,所有表共同使用一个或者多个ibdata文件。(共享)
2)MyIsam数据文件
.frm文件:主要存放与表相关的数据信息,主要包括表结构的定义信息 (表结构)
.myd文件:主要用来存储表数据信息。 (表数据)
.myi文件:主要用来存储表数据文件中任何索引的数据树。(索引)

(二) 逻辑架构图

mysql server层InnoDB层
连接器 --> 分析器(查询缓存) --> 优化器 --> 执行器磁盘结构、内存结构、数据落盘

(三) MySqlServer 层对象

Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能, 以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现, 比如存储过程、触发器、视图等。

执行流程结果详情
连接器连接管理、权限验证先连接到这个数据库上,这时候接待你的就是连接器。连接器负责跟客户端建立连接、获 取权限、维持和管理连接。
分析器(查询缓存)词法分析、语法分析(命中查询、返回结果)(大多数情况下我会建议你不要使用查询缓存,因为查询缓存往往弊大于利)
优化器生成计划、选择索引1)当有多个索引可用的时候,决定使用哪个索引; 2)在一个语句有多表关联(join)的时候,决定各个表的连接顺序,以哪个表为基准表。
执行器调用接口、返回结果1)要先判断一下你对这个表customer有没有执行查询的权限; 2)执行器会根据表的引擎定义,去使用这 个引擎提供的查询接口,提取数据。

(四) InnoDB 存储引擎层

​ 存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等 多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。

存储引擎说明
MyISAM高速引擎,拥有较高的插入,查询速度,但不支持事务
InnoDB5.5版本后MySQL的默认数据库,支持事务和行级锁定,比 MyISAM处理速度稍慢
ISAMMyISAM的前身,MySQL5.0以后不再默认安装
MRG_MyISAM(MERGE)将多个表联合成一个表使用,在超大规模数据存储时很有用
Memory内存存储引擎,拥有极高的插入,更新和查询效率。但是会占用和 数据量成正比的内存空间。只在内存上保存数据,意味着数据可能 会丢失
Falcon一种新的存储引擎,支持事物处理,传言可能是InnoDB的替代者
Archive数据压缩后进行存储,非常适合存储大量的独立的,作为历史记 录的数据,但是只能进行插入和查询操作
CSVCSV 存储引擎是基于 CSV 格式文件存储数据(应用于跨平台的数据交换)

InnoDB的磁盘结构

1)系统表空间
  • 系统表空间包含InnoDB数据字典(元数据以及相关对象)、double write buffer、change buffer、undo logs的存储区域
2)用户表空间
  • 用户表空间只存储该表的数据、索引信息,其余信息还是存放在默认的系统表空间中。
3)rodo log 文件组
  • 当InnoDB的数据存储文件发生错误时,重做日志文件就能派上用场。InnoDB存储引擎可以使用重 做日志文件将数据恢复为正确状态,以此来保证数据的正确性和完整性
  • 为了得到更高的可靠性,用户可以设置多个镜像日志组,将不同的文件组放在不同的磁盘上,以此 来提高重做日志的高可用性。
4)磁盘文件逻辑结构
  • 文件 -> 段 -> 区 -> 页 -> 行

​ InnoDB对数据的存取是以页为单位的,一个数据页默认是16k

InnoDB的内存结构

1)buffer pool
1.数据页
2.索引页

​ InnoDB存储引擎工作时,需要以Page页为最小单位去将磁盘中的数据加载到内存中,与数据库相关的所有内容都存储在Page结构里。
​ Page分为几种类型,数据页和索引页就是其中最为重要的两种类型。

3.ChangeBuffer修改缓冲区(InsertBuffer插入缓冲区)

​ 为了提高辅助索引(非聚集索引,除主键之外的索引)更新性能,暂时先把辅助索引的更新内容写到 ChangeBuffer中。后台有线程定时更新。

4.自适应hash索引

​ 为了提高查询性能,由InnoDB自己维护了一个hash索引。用户无法干预。
​ 参考:https://juejin.im/post/6847902217270198286

5.锁信息和数据字典

​ InnoDB有自己的表缓存,可以称为表定义缓存或者数据字典(Data Dictionary)。当InnoDB打开一张
表,就增加一个对应的对象到数据字典。
​ 数据字典是对数据库中的数据、库对象、表对象等的元信息的集合。在MySQL中,数据字典信息内容就
包括表结构、数据库名或表名、字段的数据类型、视图、索引、表字段信息、存储过程、触发器等内
**容。**MySQL INFORMATION_SCHEMA库提供了对数据局元数据、统计信息、以及有关MySQL server的
访问信息(例如:数据库名或表名,字段的数据类型和访问权限等)。该库中保存的信息也可以称为
MySQL的数据字典。

2)redo log buffer重做日志缓冲区
1.redolog是一个顺序写的日志文件。wal(write ahead log)模式。顺序写比随机写效率要高。使用redo 		log暂存提交成功的数据。如果一旦系统崩溃,可以使用redo log恢复数据。
2.redo log buffer就是一个缓冲区,定时将缓冲区的数据保存到磁盘。
	默认1s保存一次。默认在执行commit操作之前刷新redo log buffer。
	参数:innodb_flush_log_at_trx_commit控制redo log的落盘时机

	当属性值为0时,事务提交时,不会对重做日志进行写入操作,而是等待主线程按时写入每秒写入一次;
	当属性值为1时,事务提交时,会将重做日志写入文件系统缓存,并且调用文件系统的fsync,将文件系统缓冲中的数据真正写入磁盘存储,确保不会出现数据丢失;(一般)
	当属性值为2时,事务提交时,也会将日志文件写入文件系统缓存,但是不会调用fsync,而是让文件系统自己去判断何时将缓存写入磁盘。
3)双写缓冲区
  • 数据落盘需要执行双写操作,需要使用到双写缓冲区。稍后详解。
4)数据落盘

  • 脏页
    当更新数据commit之后,数据不是马上更新到磁盘。把数据更新到缓冲池中对应的数据页和索引页中。此时就会造成内存中的数据页和索引页与磁盘上的数据页不一致,就形成了脏页。
  • CheckPoint 执行脏页落盘操作的。
    sharp checkpoint:在关闭数据库的时候,将buffer pool中的脏页全部刷新到磁盘中
    fuzzy checkpoint:模糊落盘
    1、Master Thread Checkpoint;
    会以每秒或者每10秒一次的频率,将部分脏页从内存中刷新到磁盘,参数可调。
    2、FLUSH_LRU_LIST Checkpoint;
    使用LRU算法需要把一些数据页移除,但是数据页是脏页,需要执行checkpoint。
    3、Async/Sync Flush Checkpoint;
    redo log文件快写满时。
    Async:当需要落盘的日志超过日志文件的75%并且小于90%时。
    sync:当需要落盘的日志超过日志文件超过90%时。
    4、Dirty Page too much Checkpoint
    bufferPool缓冲池脏页太多
    Dirty Page 由[innodb_max_dirty_pages_pct]配置,innodb_max_dirty_pages_pct的默认值在innodb 1.0之前是90%,之后是75%
  • 双写落盘double write
    ​ 当脏页落盘过程中,是使用双写操作执行落盘的。
    ​ 1.把要落盘的数据写先写入双写缓冲区2m大小的双写缓冲区。
    ​ 2.把双写缓冲区的数据写入系统表空间中的双写区。
    ​ 3.把双写缓冲区的数据写入用户表空间中。

​ redolog中记录的是数据页中修改了哪些内容,并不是完整的数据页。

场景1:系统表空间中的数据页写坏了,可以通过用户表空间的数据页+redolog恢复。
场景2:系统表空间中的数据页写完了,用户表空间写坏了。可以通过系统表空间的数据页恢复。

​ 通过双写机制保证数据落盘过程万无一失。

三. MySQL事务篇

(一) 事务介绍

​ MySQL 是一个服务器/客户端架构的软件,对于同一个服务器来说,可以有若干个客户端与之连接,每 个客户端与服务器连接上之后,就可以称之为一个会话( Session )。我们可以同时在不同的会话里输 入各种语句,这些语句可以作为事务的一部分进行处理。不同的会话可以同时发送请求,也就是说服务 器可能同时在处理多个事务,这样子就会导致不同的事务可能同时访问到相同的记录。我们前边说过事 务有一个特性称之为 隔离性 ,理论上在某个事务对某个数据进行访问时,其他事务应该进行排队,当该 事务提交之后,其他事务才可以继续访问这个数据。但是这样子的话对性能影响太大,所以设计数据库 的大叔提出了各种 隔离级别 ,来最大限度的提升系统并发处理事务的能力,但是这也是以牺牲一定的 隔 离性 来达到的。

事务特征(ACID)

  • 原子性(atomicity) :事务最小工作单元,要么全成功,要么全失败 。
  • 一致性(consistency): 事务开始和结束后,数据库的完整性不会被破坏 。
  • 隔离性(isolation) :不同事务之间互不影响,四种隔离级别为RU(读未提交)、RC(读已提 交)、RR(可重复读)、SERIALIZABLE (串行化)。
  • 持久性(durability) :事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失 。

事务的隔离级别

隔离级别标识影响详情
未提交读RU(READ UNCOMMITTED)脏读一个事务读取到另一个事务未提交的数据
已提交读RC(READ COMMITTED)不可重复读一个事务因读取到另一个事务已提交的update
可重复读RR(REPEATABLE READ)幻读一个事务因读取到另一个事务已提交的insert数据或者delete数据
串行化SERIALIZABLE不允许 读-写写-读 的并发操作

(二) 事务和MVCC底层原理详解

1、解决数据更新丢失的问题

1)LBCC
基于锁的并发控制。让操作串行化执行。效率低。
2)MVCC
基于版本的并发控制。使用快照形式。效率高。读写不冲突。主流数据库都是使用的MVCC。

2、InnoDB中MVCC的实现

特点:读不加锁,读写不冲突
实现方案:基于undolog+readview实现的。

1)undolog
	回滚日志。需要在undolog中记录未提交操作的原始状态。
	在undolog中会记录版本信息。在每一行记录上都有2-3个隐藏列。如果表没有主键时就是3个,如果有主键就是两个。

隐藏列:

  • rowid:如果没有主键,会自动生成一个隐藏列。(非必须)

  • 回滚指针:指向记录的上一个版本。

  • 事务id:记录了操作这条记录的事务id。

    事务id在mysql中每个事务都有一个唯一的id,并且是自增的。

​ 每次更新时,都是生成一个新的版本,并且由回滚指针指向旧版本。就会形成一个版本链。后台有个purge线程执行清理操作。删除记录时,是在记录上打上删除标记,并不直接删除。

2)readview

​ 生成一个readview相当于生成了一个快照。只要是ReadView不发生变化读到的结果就是相同的
​ readview是一个数组,生成的时机是执行select操作时生成。数组m_ids,其中记录的当前时刻,数据库中活跃的事务id列表。
​ 例如:
​ m_ids:[105,110,111,120]

​ 可见性判断条件:

支持两种事务隔离级别:RC、RR。
读未提交和串行化和MVCC没有关系。

一个select对应一个ReadView(m_ids),select语句执行完毕后ReadView就失效。
如果当前数据库中没有活跃事务,那么ReadView(m_ids)中就包含将要生成的事务id。

3)RC事务隔离级别的实现

读已提交,当前事务中可以读取到其他事务提交的结果。
实现方案:在当前事务中执行select查询,就会生成一个ReadView。如果同一个select再次执行,会再次生成ReadView

4)RR事务隔离级别

可重复读,当前事务中select语句多次执行得到的结果是相同的,无论其他事务是否已经提交。
实现方案:在当前事务中执行select查询,就会生成一个ReadView,之后同一个select使用同一个ReadView

5)小结:RC和RR的区别
	`READ COMMITTD`、`REPEATABLE READ`这两个隔离级别的一个很大不同就是生成`ReadView`的时机不同,`READ COMMITTD`在每一次进行普通`SELECT`操作前都会生成一个`ReadView`,而`REPEATABLE READ`只在第一次进行普通`SELECT`操作前生成一个`ReadView`,之后的查询操作都重复这个`ReadView`就好了。
6)redo log 与 undo log
	redo log顾名思义,就是重做日志,每次数据库的SQL操作导致的数据变化它都会记录一下,具体来说,redo log是物理日志,记录的是数据库页的物理修改操作。如果数据发生了丢失,数据库可以根据redo log进行数据恢复。
	undo log记录了数据在每个操作前的状态,如果事务执行过程中需要回滚,就可以根据undo log进行回滚操作。

四. MySQL索引篇

(一) 索引介绍

1、索引是什么

  • 官方介绍索引是帮助MySQL高效获取数据数据结构。更通俗的说,数据库索引好比是一本书前

面的目录,能加快数据库的查询速度

  • 一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往是存储在磁盘上的文件中的

(可能存储在单独的索引文件中,也可能和数据一起存储在数据文件中)。

  • 我们通常所说的索引,包括聚集索引、覆盖索引、组合索引、前缀索引、唯一索引等,没有特别说

明,默认都是使用B+树结构组织(多路搜索树,并不一定是二叉的)的索引。

2、索引的优势和劣势

优势:

1.可以提高数据检索的效率,降低数据库的IO成本,类似于书的目录。

2.通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗。

被索引的列会自动进行排序,包括【单列索引】和【组合索引】,只是组合索引的排序要复

杂一些。

如果按照索引列的顺序进行排序,对应order by语句来说,效率就会提高很多。

劣势:

1.索引会占据磁盘空间

2.索引虽然会提高查询效率,但是会降低更新表的效率。比如每次对表进行增删改操作,MySQL不

仅要保存数据,还有保存或者更新对应的索引文件。

(二) 索引的使用

1、索引的类型

  • 主键索引:索引列中的值必须是唯一的,不允许有空值。
ALTER TABLE table_name ADD PRIMARY KEY (column_name);
  • 普通索引:MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值。
ALTER TABLE table_name ADD INDEX index_name (column_name) ;
CREATE INDEX index_name ON table_name(column_name) ;
  • 唯一索引:索引列中的值必须是唯一的,但是允许为空值。
ALTER TABLE table_name ADD UNIQUE INDEX index_name (column_name) ;
CREATE UNIQUE INDEX index_name ON table_name(column_name) ;
  • 全文索引:只能在文本类型CHAR,VARCHAR,TEXT类型字段上创建全文索引。字段长度比较大时,

    如果创建普通索引,在进行like模糊查询时效率比较低,这时可以创建全文索引。

#创建表时,创建全文索引 
CREATE TABLE `t_fulltext` ( 
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `content` varchar(100) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    FULLTEXT KEY `idx_content` (`content`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

#创建全文索引 
ALTER TABLE `t_fulltext` ADD FULLTEXT INDEX `idx_content`(`content`);
CREATE FULLTEXT INDEX index_name ON table_name(column_name) ;

可以使用MATCH() … AGAINST语法执行全文搜索。

SELECT * FROM t_fulltext WHERE MATCH(content) AGAINST('开课吧');
  • 空间索引:MySQL在5.7之后的版本支持了空间索引,而且支持OpenGIS几何数据模型。MySQL在空间索引这方面遵循OpenGIS几何数据模型规则。(少用

  • 前缀索引少用

    在文本类型如CHAR,VARCHAR,TEXT类列上创建索引时,可以指定索引列的长度,但是数值类型不

    能指定。

    ALTER TABLE table_name ADD INDEX index_name (column1(length));
    
  • 按照索引列的数量

    单列索引:索引中只有一个列。

    组合索引:使用2个以上的字段创建的索引。

    1.组合索引的使用,需要遵循最左前缀原则
    2.一般情况下,建议使用组合索引代替单列索引(主键索引除外)。
    
    #添加方式
    ALTER TABLE table_name ADD INDEX index_name (column1,column2);
    

2、删除索引

ALTER TABLE table_name DROP INDEX index_name;
DROP INDEX index_name ON table_name

3、查看索引

SHOW INDEX FROM table_name \G

(三) 索引的数据结构

1、索引分类
1)聚簇索引

基于主键创建的索引。或者叫做聚集索引,因为索引和数据是保存在一起的。

2)非聚簇索引

除了主键索引之外创建的索引都是非聚簇索引。辅助索引

2、索引的数据结构
1)hash

key-value形式的数据结构。
查询速度非常快。直接通过可以找到value。
优点:速度快。时间复杂度O(1)
缺点:不适合范围查询。不适合做索引。

2)二叉查找树

特点:左子树一定是小于根节点,右子树一定是大于根节点。时间复杂度O(log2n)
问题:根节点的选取最好是所有数据中中间数值。否则左子树和右子树高度可能不一致。最差情况下退化成链表。

3)二叉平衡树查找树

特点:向树中增加节点,动态调整树的平衡状态,要求左右子树的高度相差最大不能超过1.
问题:不断的添加数据,二叉树会不断的调整状态。调整的过程也是非常耗时的。数据量大时,树的高度也会非常高,每查找一个节点就会产生一个磁盘的IO。总体上性能也不太好。
要求树高度要低,推荐使用多叉树。

4)B树

每个节点中可以有多个分支,分支的数量叫做度。分支越多效果越好。每个数据的左右两个分支,左子树小于当前数值,右子树大于当前数值。B树是一种多叉平衡查找树
前提:
每个节点中保存的数据 8byte+4byte+4byte,每个数据节点是16k时,b树的度应该是1000

高度为2的b树可以存储数据量:
1000*1000=10W

特点:
1、每个节点中都保存数据
2、数据出现在中间节点中,就不会出现在叶子节点中。

问题:
选择索引数据结构时,需要考虑两点,一个就是等值查询,一个是范围查询。
如果使用b树做范围查询效率不高

5)B+树(常用)

对b树的改进:
1、b+树中除叶子节点外,中间节点不保存数据,中间节点保存的是主键
2、b+树中所有的数据都存放在叶子节点中
3、b+树中叶子节点之间有双向指针,形成一个双向链表

b+树适合于等值查询也适合于范围查询。在mysql中所有的索引都是b+tree结构。

3、MyIsam引擎的索引结构
1)主键索引
 b+tree结构。在MyIsam引擎中索引和数据是分在不同的文件中存储的。
 中间节点保存都是主键的值,叶子节点中保存的是对应数据行的地址。
2)非主键索引,辅助索引
在普通字段上创建的索引。
对应MyIsam引擎来说**辅助索引和主键索引是相同的**。
4、InnoDB引擎的索引结构
1)主键索引
  聚集索引或者聚簇索引。如果使用的是InnoDB引擎,没有设置主键系统会自动创建一个主键,rowid。
  InnoDB中主键索引和数据是存储在一起的,所以叫做聚簇索引,聚集索引。
  通过主键查询数据效率是最高的,找到叶子节点就找到数据行。

InnoDB引擎必有主键

1. 在表上定义主键PRIMARY KEY,InnoDB将主键索引用作聚簇索引。
2. 如果表没有定义主键,InnoDB会选择第一个不为NULL的唯一索引列用作聚簇索引。
3. 如果以上两个都没有,InnoDB 会使用一个6 字节长整型的隐式字段 ROWID字段构建聚簇索
引。该ROWID字段会在插入新行时自动递增。 
2)非主键索引,辅助索引
非主键索引也是b+tree结构,叶子节点中保存的就是主键的值。 如果根据辅助索引查询,只能得到主键的值,然后根据主键查询聚集索引找到对应的数据,这个过程就叫做回表。
如果使用辅助索引,效率是不如MyIsam引擎的辅助索引效率高。 
3)组合索引(重点)

1、什么是组合索引

组合索引就是辅助索引的一种形式。基于多个字段创建的一个辅助索引。
 创建辅助索引的两种形式:
  1、在每个字段上分别创建一个索引。
  2、创建一个辅助索引,里面包含三个字段。(推荐使用形式,组合索引)
 SQL语句中如果使用到一个表的索引,那么只能使用一个。
#示例
在组合索引中,如果有三个字段a、b、c
  先按照a排序,
  如果a相同,按照b排序
  如果b相同,按照c排序

2、组合索引的使用方法

1)要求查询条件中必须包含组合索引中的第一个字段。
2)可以满足根据a、ab、abc查询都可以。
3)如果条件中只有b、c是否能使用索引?否。
4)如果使用组合索引时,必须有索引中第一个字段,而且后面的条件必须按照创建索引的顺序出现。
  如果条件中a、c,那么只能a条件使用索引,c无法使用索引。

索引使用口诀

 全值匹配我最爱,最左前缀要遵守;
 带头大哥不能死,中间兄弟不能断;
 索引列上不计算,范围之后全失效;
 Like百分写最右,覆盖索引不写星;
 不等空值还有OR,索引失效要少用。
in是可以使用到索引的。如果表中数据量小并且,in集合中数据较多的情况下可能不使用索引。如果数据量大in一定会使用索引。

3、创建组合索引的原则

 1)经常出现在where条件中字段
 2)经常出现order by、group by字段中的
 3)经常出现在select字段列表中的字段
 select id,a,b,c from user wehre xxxx

组合索引的字段顺序如何设计呢?
出现在where条件中频率最高的字段应该排第一位,按照使用频率进行排序

order by使用索引情况的分析:
https://juejin.im/post/6854573217978253320

4)索引覆盖
 查询的字段内容可以直接从索引中获得,无需再回表,这样可以提高查询效率。
 在select语句中不要使用“*”,应该使用字段列表。
5)索引条件下推ICP
索引条件下推在mysql5.7以后才有的一个优化手段。默认开启的。
 作用就是,即便是组合索引条件中断或者范围查询,后面的条件依然可以使用索引。
 #optimizer_switch优化相关参数开关
 mysql> show VARIABLES like 'optimizer_switch'\G;
 #关闭ICP
 SET optimizer_switch = 'index_condition_pushdown=off';
 #开启ICP
 SET optimizer_switch = 'index_condition_pushdown=on';

(四) explain是否使用索引

1、Using index(覆盖索引)

#Extra中为Using index
1 查询的列被索引覆盖
2 并且where筛选条件是索引的是前导列

2、Using where ;Using index

#Extra中为Using where; Using index,
1 查询的列被索引覆盖
2 where筛选条件是索引列之一,但是不是索引的不是前导列,
  或者where筛选条件是索引列前导列的一个范围

3、NULL

#Extra中为NULL(没有信息)
1 查询的列未被索引覆盖,并且where筛选条件是索引的前导列,

意味着用到了索引,但是部分字段未被索引覆盖,必须通过“回表”来实现,不是纯粹地用到了索引,也不是完全没用到索引,

4、Using where

#Extra中为Using where
1 查询的列未被索引覆盖
2 where筛选条件非索引的前导列或者非索引列

5、Using index condition

1 查询的列不全在索引中,where条件中是一个前导列的范围
2 查询列不完全被索引覆盖,查询条件完全可以使用到索引(进行索引查找)

五.MySQL锁篇

(一) 锁介绍

1. 按照锁的粒度来说

全局锁:锁的是整个database。由MySQL的SQL layer层实现的
表级锁:锁的是某个table。由MySQL的SQL layer层实现的
行级锁:锁的是某行数据,也可能锁定行之间的间隙。由某些存储引擎实现,比如InnoDB。

2. 按照锁的功能来说

共享锁Shared Locks(S锁): 读锁

1、兼容性:加了S锁的记录,允许其他事务再加S锁,不允许其他事务再加X锁 
2、加锁方式:select…lock in share mode 

排他锁Exclusive Locks(X锁): 写锁

1、兼容性:加了X锁的记录,不允许其他事务再加S锁或者X锁 
2、加锁方式:select…for update

(二) 全局锁

​ 全局锁就对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的MDL的写语句,DDL语句,

已经更新操作的事务提交语句都将被阻塞。其典型的使用场景是做全库的逻辑备份,对所有的表进行锁

定,从而获取一致性视图,保证数据的完整性。

1. 对于innodb这种支持事务的引擎,使用mysqldump备份时可以使用--single-transaction参数,利用
mvcc提供一致性视图,而不使用全局锁,不会影响业务的正常运行。
2.而对于有MyISAM这种不支持事务的表,就只能通过全局锁获得一致性视图,对应的mysqldump参数为--lock-all-tables。

加锁

	mysql> flush tables with read lock;

解锁

	mysql> unlock tables;

​ 或者会话关闭,锁自动解除。
注意:全局锁一般很少使用,会影响业务处理。

(三) 表级锁

1、表级锁介绍

1、表读、写锁。 
2、元数据锁(meta data lock,MDL)。 
3、意向锁 Intention Locks(InnoDB) 
4、自增锁(AUTO-INC Locks)

2、读锁S、写锁X

1) 表锁相关命令
  • MySQL 实现的表级锁定的争用状态变量

    mysql> show status like 'table%';
    
    - table_locks_immediate:产生表级锁定的次数; 
    - table_locks_waited:出现表级锁定争用而发生等待的次数;
    
  • 表锁有两种表现形式

    表共享读锁(Table Read Lock) 
    表独占写锁(Table Write Lock)
    
  • 手动增加表锁:

    lock table 表名称1 read(write),表名称2 read(write),其他;
    
  • 查看表锁情况:

    show open tables;
    
  • 删除表锁

    unlock tables;
    
2) 表锁说明
  • 读锁(当前session加读锁)

    #当前session
    1. 当前session可以查询该表记录,但是不能查询其他没有加锁的表
    2. 当前session进行插入,更新,删除表会报错
    
    #其他session
    1. 其他session可以查询该表记录,也可以查询其他未加锁的表
    2. 其他session对该表进行插入,更新,删除会一直等待获得锁
    
  • 写锁(当前session加写锁)

    #当前session
    1. 当前session可以进行增,删,改,查操作
    
    #其他session
    1. 其他session进行增,删,改,查操作被阻塞,要等待锁被释放
    
  • 注意

    1.S锁的作用,在读取过程中防止其他会话对表进行修改操作,如果一个表上有任何锁,包括S、X锁,都无法再加X锁。
    	一个表上可以加多个S锁,数据可以共享读。
    2.X锁,排它锁,在修改过程中,对表处于一个独享状态。如果一个表上有X锁,那么此时表上不允许加任何锁。
    
    3.锁的兼容性:
    	S、S可以
    	S、X不可以
    	X、X不可以
    表锁一般也不经常使用,粒度比较粗。
    

3、元数据锁

1)元数据锁介绍

当事务未结束时,在事务中执行了select操作,不允许对表结构进行修改的。

MDL不需要显式使用,在访问一个表的时候会被自动加上。在 MySQL 5.5 版本中引入了 MDL

1.当对一个表做增删改查操作的时候,加 MDL 读锁;
2.当要对表做结构变更操作的时候,加 MDL 写锁。
  • 读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。

  • 读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性。因此,如果有两个线程要同

    时给一个表加字段,其中一个要等另一个执行完才能开始执行。

2)元数据锁演示
1、session1: begin;--开启事务 
   select * from mylock;--加MDL读锁 
2、session2: alter table mylock add f int; -- 修改阻塞 
3、session1:commit; --提交事务 或者 rollback 释放读锁 
4、session2:Query OK, 0 rows affected (38.67 sec) --修改完成 
Records: 0 Duplicates: 0 Warnings: 0

4、自增锁(AUTO-INC Locks)

​ AUTO-INC锁是一种特殊的表级锁,发生涉及AUTO_INCREMENT列的事务性插入操作时产生。

(四) 行级锁(InnoDB独有)

  • MySQL的行级锁,是由存储引擎来实现的,这里我们主要讲解InnoDB的行级锁

  • InnoDB行锁是通过给索引上的索引项加锁来实现的,因此InnoDB这种行锁实现特点意味着:只有通过 索引条件检索的数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

    1 记录锁(Record Locks):锁定索引中一条记录。 
    2 间隙锁(Gap Locks):要么锁住索引记录中间的值,要么锁住第一个索引记录前面的值或者最后一个索引记录后面的值。(范围查询,解决MVCC的幻读问题)
    3 临键锁(Next-Key Locks):是索引记录上的记录锁和在索引记录之前的间隙锁的组合(间隙锁+记录锁)。
    4 插入意向锁(Insert Intention Locks):做insert操作时添加的对记录id的锁。
    

MySQL的行级锁,是由存储引擎来实现的,这里我们主要讲解InnoDB的行级锁。 InnoDB行锁是通过给索引上的索引项加锁来实现的,因此InnoDB这种行锁实现特点意味着:只有通过 索引条件检索的数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

1)记录锁

​ 只有InnoDB引擎支持行锁。也就是说行锁是在InnoDB中实现。
​ 行锁也有共享锁和排他锁。
加共享锁:

select * from t where id=1 lock in share mode;

加排它锁:

select * from t where id=1 for update;

2)意向锁

  • ​ 意向锁共享锁IS:表中某个记录上有共享锁是添加。
  • ​ 意向排他锁IX:表中某个记录有排他锁是添加
    就是一个标志位,为了快速判断表上是否有行锁

​ 意向锁是表级锁。是InnoDB实现的,是行锁的一个副产品。
​ 不需要人为干预,InnoDB自动处理。在对记录加行锁时由InnoDB自动加意向锁。

是否兼容ISIXSX
IS
IX
S
X

3)间隙锁

  • ​ 区间锁, 仅仅锁住一个索引区间(开区间,不包括双端端点)
  • ​ 间隙锁可用于防止幻读,保证索引间的不会被插入数据
    ​ 间隙锁就是MVCC防止幻读的解决方案。
    ​ MVCC实现的是快照读,加锁不影响读操作。

    ​ 只有RR事务隔离级别才用到间隙锁,RC是不需要的。

4)临键锁

​ 记录锁+间隙锁
​ 默认情况下,innodb使用next-key locks来锁定记录。

  • ​ 记录锁:条件中根据主键等值查询,并且值存在时,只有记录锁。
  • ​ 间隙锁:条件中根据主键等值查询,并且值不存在时,只有间隙锁,锁等值所在的那个区间。
  • ​ 临键锁:条件中根据索引做范围查询,使用临键锁。

如果根据辅助索引更新:

	1)因为辅助索引不唯一,所以更新时,在辅助索引上加临键锁,
	2)并且在主索引上增加记录锁。

5)插入意向锁(Insert Intention Locks)

​ 做insert操作时,会锁定未提交记录的id的值。

6)行锁加锁规则

  • 主键索引
  1. 等值查询

    1)命中记录,加记录锁。
    2)未命中记录,加间隙锁。
    
  2. 范围查询

1)没有命中任何一条记录时,加间隙锁。
2)命中1条或者多条,包含where条件的临键区间,加临键锁
  • 辅助索引
  1. 等值查询

    1)命中记录,命中记录的辅助索引项+主键索引项加记录锁,辅助索引项两侧加间隙锁。
    2)未命中记录,加间隙锁
    
  2. 范围查询

    1)没有命中任何一条记录时,加间隙锁。
    2)命中1条或者多条,包含where条件的临键区间加临键锁。命中记录的id索引项加记录锁。
    

(五) 死锁

有两个或两个以上的资源的情况下,有两个事物互相持有了对方期待的资源,导致互相等待对方释放资源时,产生死锁。

  • 如何避免死锁呢?

MySQL默认会主动探知死锁,并回滚某一个影响最小的事务。等另一事务执行完成之后,再重新执行该事务。

如何避免死锁

1、注意程序的逻辑

根本的原因是程序逻辑的顺序,最常见的是交差更新
Transaction 1: 更新表A -> 更新表B
Transaction 2: 更新表B -> 更新表A
Transaction获得两个资源

2、保持事务的轻量

越是轻量的事务,占有越少的锁资源,这样发生死锁的几率就越小

3、提高运行的速度

避免使用子查询,尽量使用主键等等

4、尽量快提交事务,减少持有锁的时间

越早提交事务,锁就越早释放

六.MySQL性能优化篇

(一)性能优化的思路

1. 首先需要使用【慢查询日志】功能,去获取所有查询时间比较长的SQL语句
2. 查看执行计划,查看有问题的SQL的执行计划
3. 针对查询慢的SQL语句进行优化
4. 使用【show profile[s]】 查看有问题的SQL的性能使用情况
5. 调整操作系统参数优化
6. 升级服务器硬件

(二)慢查询日志

1、慢查询日志介绍

​ 数据库查询快慢是影响项目性能的一大因素,对于数据库,我们除了要优化 SQL,更重要的是得先找到需要优化的SQL。

MySQL数据库有一个“慢查询日志”功能,用来记录查询时间超过某个设定值的SQL语句,这将极大程度帮助我们快速定位到症结所在,以便对症下药。

​ 至于查询时间的多少才算慢,每个项目、业务都有不同的要求。

  • MySQL的慢查询日志功能默认是关闭的,需要手动开启

2、开启慢查询功能

  • 查看是否开启慢查询功能

    mysql> show variables like '%slow_query%';				#查看是否开启慢查询
    mysql> show variables like '%long_query_time%';			#慢查询阈值,当查询时间多于设定的阈值时,记录日志,
    
  • 临时开启慢查询功能

    #在 MySQL 执行 SQL 语句设置,但是如果重启 MySQL 的话将失效
    set global slow_query_log = ON; 
    set global long_query_time = 1;
    
  • 永久开启慢查询功能

    #修改/etc/my.cnf配置文件,重启 MySQL, 这种永久生效.
    [mysqld] 
    slow_query_log=ON 
    long_query_time=1
    

3、慢查询日志格式

mysql> select sleep(3);

查看日志信息:

tail -100f xxx.log;

格式说明:

  • 第一行,SQL查询执行的具体时间

  • 第二行,执行SQL查询的连接信息,用户和连接IP

  • 第三行,记录了一些我们比较有用的信息,如下解析

    Query_time,这条SQL执行的时间,越长则越慢 
    Lock_time,在MySQL服务器阶段(不是在存储引擎阶段)等待表锁时间 
    Rows_sent,查询返回的行数 
    Rows_examined,查询检查的行数,越长就当然越费时间
    
  • 第四行,设置时间戳,没有实际意义,只是和第一行对应执行时间。

  • 第五行及后面所有行(第二个# Time:之前),执行的sql语句记录信息,因为sql可能会很长。

4、分析慢查询日志的工具

使用mysqldumpslow工具,mysqldumpslow是MySQL自带的慢查询日志工具。可以使用mysqldumpslow工具搜索慢查询日志中的SQL语句。

[root@localhost mysql]# mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/slow.log

说明:

  • -s:是表示按照何种方式排序

    al 平均锁定时间
    ar 平均返回记录时间
    at 平均查询时间(默认)
    c 计数
    l 锁定时间
    r 返回记录
    t 查询时间
    
  • -t:是top n的意思,即为返回前面多少条的数据

  • -g:后边可以写一个正则匹配模式,大小写不敏感的

[root@mysql132 mysql]# mysqldumpslow -s t /var/lib/mysql/mysql132-slow.log 

Reading mysql slow query log from /var/lib/mysql/mysql132-slow.log 

Count: 1 Time=143.16s (143s) Lock=0.00s (0s) Rows=27907961.0 (27907961), 
root[root]@localhost select * from t_slow a left join t_slow b on a.name=b.name
    
Count: 5 Time=5.80s (28s) Lock=0.00s (0s) Rows=0.0 (0), 
root[root]@localhost insert into t_slow(name,address) select name,address from t_slow 

Count: 1 Time=3.01s (3s) Lock=0.00s (0s) Rows=1.0 (1), root[root]@localhost select sleep(N)

(三)查看执行计划explain

1.参数说明

mysql> explain select * from tuser where id = 2 \G 
*************************** 1. row ***************************
	id: 1 
	select_type: SIMPLE 
	table: tuser 
	partitions: NULL 
	type: const 
	possible_keys: PRIMARY 
	key: PRIMARY 
	key_len: 4 
	ref: const 
	rows: 1 
	filtered: 100.00 
	Extra: NULL 
1 row in set, 1 warning (0.01 sec)

说明:

  • id: SELECT 查询的标识符. 每个 SELECT 都会自动分配一个唯一的标识符.

    id相同:执行顺序由上到下			#多表同级查询
    id不同:如果是子查询,id号会自增,id越大,优先级越高。		#多表存在子查询
    
  • select_type: SELECT 查询的类型.

    1.simple
    	表示不需要union操作或者不包含子查询的简单select查询。有连接查询时,外层的查询为simple。
    2.primary
    	一个需要union操作或者含有子查询的select,位于最外层的单位查询的select_type即为primary。
    3.union
    	union连接的两个select查询,第一个查询是dervied派生表,除了第一个表外,第二个以后的表select_type都是union
    4.dependent union
    	与union一样,出现在union 或union all语句中,但是这个查询要受到外部查询的影响
    5.union result
    	包含union的结果集,在union和union all语句中,因为它不需要参与查询,所以id字段为null
    6.subquery
    	除了from字句中包含的子查询外,其他地方出现的子查询都可能是subquery
    7.dependent subquery
    	与dependent union类似,表示这个subquery的查询要受到外部表查询的影响
    8.derived
    	from字句中出现的子查询,也叫做派生表,其他数据库中可能叫做内联视图或嵌套select
    
    
    #注意;
    ·DERIVED 在FROM列表中包含的子查询被标记为DERIVED(衍生),MySQL会递归执行这些子查询,把结果放在临时表中
    ·UNION 若第二个SELECT出现在UNION之后,则被标记为UNION:若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
    ·UNION RESULT 从UNION表获取结果的SELECT
    
  • table: 查询的是哪个表

    1 如果查询使用了别名,那么这里显示的是别名
    2 如果不涉及对数据表的操作,那么这显示为null
    3 如果显示为尖括号括起来的就表示这个是临时表,后边的N就是执行计划中的id,表示结果来自于这个查询产生。
    4 如果是尖括号括起来的<union M,N>,与类似,也是一个临时表,表示这个结果来自于union查询的id为M,N的结果集。
    
  • partitions: 匹配的分区

    #使用的哪些分区(对于非分区表值为null)。
    什么是分区表?
    	mysql内部实现的表的水平拆分,所有数据还在一个表中,但物理存储根据一定的规则放在不同的文件中。这个是mysql支持的功能,业务代码无需改动。
    技术现状:
    	业内进行一些技术交流的时候也更多的是自己分库分表,而不是使用分区表。
    	1)分区表,分区键设计不太灵活,如果不走分区键,很容易出现全表锁
    	2)一旦数据量并发量上来,如果在分区表实施关联,就是一个灾难
    	3)自己分库分表,自己掌控业务场景与访问模式,可控。分区表,研发写了一个sql,都不确定mysql是怎么玩的,不太可控
    	4)运维的坑
    
  • type: join 类型

    #显示的是单位查询的连接类型或者理解为访问类型,访问性能依次从好到差:
    system 
    const 
    eq_ref 
    ref 
    fulltext 
    ref_or_null 
    unique_subquery 
    index_subquery 
    range 
    index_merge 
    index ALL
    
    #注意
    - 除了all之外,其他的type都可以使用到索引 
    - 除了index_merge之外,其他的type只可以用到一个索引 
    - 最少要使用到range级别
    
  • possible_keys: 此次查询中可能选用的索引

    此次查询中可能选用的索引,一个或多个
    
  • key: 此次查询中确切使用到的索引.

    查询真正使用到的索引,select_type为index_merge时,这里可能出现两个以上的索引,其他的select_type这里只会出现一个。
    
  • key_len:

    key_len越小 索引效果越好
    
  • ref: 哪个字段或常数与 key 一起被使用

    1 如果是使用的常数等值查询,这里会显示const
    2 如果是连接查询,被驱动表的执行计划这里会显示驱动表的关联字段
    3 如果是条件使用了表达式或者函数,或者条件列发生了内部隐式转换,这里可能显示为func
    
  • rows: 显示此查询一共扫描了多少行. 这个是一个估计值.

    这里是执行计划中估算的扫描行数,不是精确值(InnoDB不是精确的值,MyISAM是精确的值,主要原
    因是InnoDB里面使用了MVCC并发机制)
    
  • filtered: 表示此查询条件所过滤的数据的百分比

    filtered列指示将由mysql server层需要对存储引擎层返回的记录进行筛选的估计百分比,也就是说存储引擎层返回的结果中包含有效记录数的百分比。最大值为100,这意味着没有对行进行筛选。值从100减小表示过滤量增加。rows显示检查的估计行数,rows×filtered显示将与下表联接的行数。例如,如果rows为1000,filtered为50.00(50%),则要与下表联接的行数为1000×50%=500。
    
  • extra: 额外的信息

(四)SQL语句优化(开发人员)

1、索引优化

  • 为搜索字段(where中的条件)、排序字段、select查询列创建合适的索引,不过要考虑数据的

    业务场景:查询多还是增删多?

  • 尽量建立组合索引并注意组合索引的创建顺序,按照顺序组织查询条件、尽量将筛选粒度大的查询条件放到最左边。

  • 尽量使用覆盖索引,SELECT语句中尽量不要使用*。

  • order by、group by语句要尽量使用到索引

  • 索引长度尽量短,短索引可以节省索引空间,使查找的速度得到提升,同时内存中也可以装载更多的索引键值。太长的列,可以选择建立前缀索引

  • 索引更新不能频繁,更新非常频繁的数据不适宜建索引,因为维护索引的成本。

  • order by的索引生效,order by排序应该遵循最佳左前缀查询,如果是使用多个索引字段进行排序,那么排序的规则必须相同(同是升序或者降序),否则索引同样会失效。

2、LIMIT优化

  • 如果预计SELECT语句的查询结果是一条,最好使用 LIMIT 1,可以停止全表扫描。

  • 处理分页会使用到 LIMIT ,当翻页到非常靠后的页面的时候,偏移量会非常大,这时LIMIT的效率会非常差。 LIMIT OFFSET , SIZE;

    LIMIT的优化问题,其实是 OFFSET 的问题,它会导致MySql扫描大量不需要的行然后再抛弃掉。

    解决方案:单表分页时,使用自增主键排序之后,先使用where条件 id > offset值,limit后面只写rows

3、其他查询优化

  • 小表驱动大表,建议使用left join时,以小表关联大表,因为使用join的话,第一张表是必须全扫描的,以少关联多就可以减少这个扫描次数。

  • 避免全表扫描,mysql在使用不等于(!=或者<>)的时候无法使用索引导致全表扫描。在查询的时候,如果对索引使用不等于的操作将会导致索引失效,进行全表扫描

  • 避免mysql放弃索引查询,如果mysql估计使用全表扫描要比使用索引快,则不使用索引。(最典型的场景就是数据量少的时候)

  • 尽量不使用count(*)、尽量使用count(主键)

  • JOIN两张表的关联字段最好都建立索引,而且最好字段类型是一样的。

  • WHERE条件中尽量不要使用not in语句(建议使用not exists)

  • 合理利用慢查询日志、explain执行计划查询、show profile查看SQL执行时的资源使用情况

(五)profile分析语句

1、介绍

Query Profiler是MySQL自带的一种query诊断分析工具**,通过它可以分析出一条SQL语句的**硬件性能

瓶颈在什么地方。通常我们是使用的explain,以及slow query log都无法做到精确分析

工具只有在MySQL 5.0.37以及以上版本中才有实现。

**默认的情况下,**MYSQL的该功能没有打开,需要自己手动启动。

2、开启Profile功能

  • Profile 功能由MySQL会话变量 : profiling控制,默认是OFF关闭状态。

  • 查看是否开启了Profile功能:

    select @@profiling; 
    -- 或者 
    show variables like '%profil%';
    
  • 开启profile功能

    set profiling=1; --1是开启、0是关闭
    

3、语句使用

  • show profileshow profiles 语句可以展示当前会话(退出session后,profiling重置为0) 中执行语句的资源使用情况.
  • show profiles :以列表形式显示最近发送到服务器上执行的语句的资源使用情况.显示的记录数由变量:profiling_history_size 控制,默认15条
  • show profile: 展示最近一条语句执行的详细资源占用信息,默认显示 Status和Duration两列
  • show profile 还可根据 show profiles 列表中的 Query_ID ,选择显示某条记录的性能分析信息

七.MySQL集群篇

(一)集群搭建之主从复制

(二)集群搭建之读写分离

八.MySQL分库分表篇

九.补充篇

--------笔记2---------

第01章_数据库概述

讲师:尚硅谷-宋红康(江湖人称:康师傅)

官网:http://www.atguigu.com


1. 为什么要使用数据库

  • 持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用。大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,而持久化的实现过程大多通过各种关系数据库来完成。
  • 持久化的主要作用是将内存中的数据存储在关系型数据库中,当然也可以存储在磁盘文件、XML数据文件中。
image-20211020202152071

生活中的例子:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LtHKRNMx-1640752865981)(D:/BaiduNetdiskDownload/01_课件/images/image_20211020132711.jpg)]

2. 数据库与数据库管理系统

2.1 数据库的相关概念

DB:数据库(Database)
即存储数据的“仓库”,其本质是一个文件系统。它保存了一系列有组织的数据。
DBMS:数据库管理系统(Database Management System)
是一种操纵和管理数据库的大型软件,用于建立、使用和维护数据库,对数据库进行统一管理和控制。用户通过数据库管理系统访问数据库中表内的数据。
SQL:结构化查询语言(Structured Query Language)
专门用来与数据库通信的语言。

2.2 数据库与数据库管理系统的关系

数据库管理系统(DBMS)可以管理多个数据库,一般开发人员会针对每一个应用创建一个数据库。为保存应用中实体的数据,一般会在数据库创建多个表,以保存程序中实体用户的数据。

数据库管理系统、数据库和表的关系如图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bxGMrksG-1640752865983)(D:/BaiduNetdiskDownload/01_课件/images/image-20211013202511233.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RENwc32o-1640752865983)(D:/BaiduNetdiskDownload/01_课件/images/image-20210915112546261.png)]

2.3 常见的数据库管理系统排名(DBMS)

目前互联网上常见的数据库管理软件有Oracle、MySQL、MS SQL Server、DB2、PostgreSQL、Access、Sybase、Informix这几种。以下是2021年DB-Engines Ranking 对各数据库受欢迎程度进行调查后的统计结果:(查看数据库最新排名:https://db-engines.com/en/ranking)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UvcAe49x-1640752865984)(D:/BaiduNetdiskDownload/01_课件/images/image-20211013202815851.png)]

。。。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l6K2XkbQ-1640752865985)(D:/BaiduNetdiskDownload/01_课件/images/image-20211013202940798.png)]

对应的走势图:(https://db-engines.com/en/ranking_trend)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fCn4qbec-1640752865985)(D:/BaiduNetdiskDownload/01_课件/images/image-20211013203029492.png)]

2.4 常见的数据库介绍

Oracle

1979 年,Oracle 2 诞生,它是第一个商用的 RDBMS(关系型数据库管理系统)。随着 Oracle 软件的名气越来越大,公司也改名叫 Oracle 公司。

2007年,总计85亿美金收购BEA Systems。

2009年,总计74亿美金收购SUN。此前的2008年,SUN以10亿美金收购MySQL。意味着Oracle 同时拥有了 MySQL 的管理权,至此 Oracle 在数据库领域中成为绝对的领导者。

2013年,甲骨文超越IBM,成为继Microsoft后全球第二大软件公司。

如今 Oracle 的年收入达到了 400 亿美金,足以证明商用(收费)数据库软件的价值。

SQL Server

SQL Server 是微软开发的大型商业数据库,诞生于 1989 年。C#、.net等语言常使用,与WinNT完全集成,也可以很好地与Microsoft BackOffice产品集成。

DB2

IBM公司的数据库产品,收费的。常应用在银行系统中。

PostgreSQL

PostgreSQL 的稳定性极强,最符合SQL标准,开放源码,具备商业级DBMS质量。PG对数据量大的文本以及SQL处理较快。

SyBase

已经淡出历史舞台。提供了一个非常专业数据建模的工具PowerDesigner。

SQLite

嵌入式的小型数据库,应用在手机端。 零配置,SQlite3不用安装,不用配置,不用启动,关闭或者配置数据库实例。当系统崩溃后不用做任何恢复操作,再下次使用数据库的时候自动恢复。

informix

IBM公司出品,取自Information 和Unix的结合,它是第一个被移植到Linux上的商业数据库产品。仅运行于unix/linux平台,命令行操作。 性能较高,支持集群,适应于安全性要求极高的系统,尤其是银行,证券系统的应用。

3. MySQL介绍

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7KoW7AlV-1640752865986)(D:/BaiduNetdiskDownload/01_课件/images/image-20210829230656830.png)]

3.1 概述

  • MySQL是一个开放源代码的关系型数据库管理系统,由瑞典MySQL AB(创始人Michael Widenius)公司1995年开发,迅速成为开源数据库的 No.1。
  • 2008被Sun收购(10亿美金),2009年Sun被Oracle收购。MariaDB应运而生。(MySQL 的创造者担心 MySQL 有闭源的风险,因此创建了 MySQL 的分支项目 MariaDB)
  • MySQL6.x 版本之后分为社区版商业版
  • MySQL是一种关联数据库管理系统,将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。
  • MySQL是开源的,所以你不需要支付额外的费用。
  • MySQL是可以定制的,采用了GPL(GNU General Public License)协议,你可以修改源码来开发自己的MySQL系统。
  • MySQL支持大型的数据库。可以处理拥有上千万条记录的大型数据库。
  • MySQL支持大型数据库,支持5000万条记录的数据仓库,32位系统表文件最大可支持4GB,64位系统支持最大的表文件为8TB
  • MySQL使用标准的SQL数据语言形式。
  • MySQL可以允许运行于多个系统上,并且支持多种语言。这些编程语言包括C、C++、Python、Java、Perl、PHP和Ruby等。

3.2 MySQL发展史重大事件

MySQL的历史就是整个互联网的发展史。互联网业务从社交领域、电商领域到金融领域的发展,推动着应用对数据库的需求提升,对传统的数据库服务能力提出了挑战。高并发、高性能、高可用、轻资源、易维护、易扩展的需求,促进了MySQL的长足发展。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IXc5vgLl-1640752865986)(D:/BaiduNetdiskDownload/01_课件/images/image-20210730161043856.png)]

1.4 关于MySQL 8.0

MySQL从5.7版本直接跳跃发布了8.0版本,可见这是一个令人兴奋的里程碑版本。MySQL 8版本在功能上做了显著的改进与增强,开发者对MySQL的源代码进行了重构,最突出的一点是多MySQL Optimizer优化器进行了改进。不仅在速度上得到了改善,还为用户带来了更好的性能和更棒的体验。

1.5 Why choose MySQL?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JAoM4Q7W-1640752865986)(D:/BaiduNetdiskDownload/01_课件/images/image-20211013210429011.png)]

为什么如此多的厂商要选用MySQL?大概总结的原因主要有以下几点:

  1. 开放源代码,使用成本低。

  2. 性能卓越,服务稳定。

  3. 软件体积小,使用简单,并且易于维护。

  4. 历史悠久,社区用户非常活跃,遇到问题可以寻求帮助。

  5. 许多互联网公司在用,经过了时间的验证。

1.6 Oracle vs MySQL

Oracle 更适合大型跨国企业的使用,因为他们对费用不敏感,但是对性能要求以及安全性有更高的要求。

MySQL 由于其体积小、速度快、总体拥有成本低,可处理上千万条记录的大型数据库,尤其是开放源码这一特点,使得很多互联网公司、中小型网站选择了MySQL作为网站数据库(Facebook,Twitter,YouTube,阿里巴巴/蚂蚁金服,去哪儿,美团外卖,腾讯)。

4. RDBMS 与 非RDBMS

从排名中我们能看出来,关系型数据库绝对是 DBMS 的主流,其中使用最多的 DBMS 分别是 Oracle、MySQL 和 SQL Server。这些都是关系型数据库(RDBMS)。

4.1 关系型数据库(RDBMS)

4.1.1 实质
  • 这种类型的数据库是最古老的数据库类型,关系型数据库模型是把复杂的数据结构归结为简单的二元关系(即二维表格形式)。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JxZzeBpZ-1640752865987)(D:/BaiduNetdiskDownload/01_课件/images/image-20211020145811031.png)]

  • 关系型数据库以行(row)列(column)的形式存储数据,以便于用户理解。这一系列的行和列被称为表(table),一组表组成了一个库(database)。

  • 表与表之间的数据记录有关系(relationship)。现实世界中的各种实体以及实体之间的各种联系均用关系模型来表示。关系型数据库,就是建立在关系模型基础上的数据库。

  • SQL 就是关系型数据库的查询语言。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4ncfhN4a-1640752865987)(D:/BaiduNetdiskDownload/01_课件/images/image-20210914235413708.png)]

4.1.2 优势
  • 复杂查询
    可以用SQL语句方便的在一个表以及多个表之间做非常复杂的数据查询。
  • 事务支持
    使得对于安全性能很高的数据访问要求得以实现。

4.2 非关系型数据库(非RDBMS)

4.2.1 介绍

非关系型数据库,可看成传统关系型数据库的功能阉割版本,基于键值对存储数据,不需要经过SQL层的解析,性能非常高。同时,通过减少不常用的功能,进一步提高性能。

目前基本上大部分主流的非关系型数据库都是免费的。

4.2.2 有哪些非关系型数据库

相比于 SQL,NoSQL 泛指非关系型数据库,包括了榜单上的键值型数据库、文档型数据库、搜索引擎和列存储等,除此以外还包括图形数据库。也只有用 NoSQL 一词才能将这些技术囊括进来。

键值型数据库

键值型数据库通过 Key-Value 键值的方式来存储数据,其中 Key 和 Value 可以是简单的对象,也可以是复杂的对象。Key 作为唯一的标识符,优点是查找速度快,在这方面明显优于关系型数据库,缺点是无法像关系型数据库一样使用条件过滤(比如 WHERE),如果你不知道去哪里找数据,就要遍历所有的键,这就会消耗大量的计算。

键值型数据库典型的使用场景是作为内存缓存Redis是最流行的键值型数据库。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E6pDAsWW-1640752865988)(D:/BaiduNetdiskDownload/01_课件/images/image-20211020172958427.png)]

文档型数据库

此类数据库可存放并获取文档,可以是XML、JSON等格式。在数据库中文档作为处理信息的基本单位,一个文档就相当于一条记录。文档数据库所存放的文档,就相当于键值数据库所存放的“值”。MongoDB 是最流行的文档型数据库。此外,还有CouchDB等。

搜索引擎数据库

虽然关系型数据库采用了索引提升检索效率,但是针对全文索引效率却较低。搜索引擎数据库是应用在搜索引擎领域的数据存储形式,由于搜索引擎会爬取大量的数据,并以特定的格式进行存储,这样在检索的时候才能保证性能最优。核心原理是“倒排索引”。

典型产品:Solr、Elasticsearch、Splunk 等。

列式数据库

列式数据库是相对于行式存储的数据库,Oracle、MySQL、SQL Server 等数据库都是采用的行式存储(Row-based),而列式数据库是将数据按照列存储到数据库中,这样做的好处是可以大量降低系统的 I/O,适合于分布式文件系统,不足在于功能相对有限。典型产品:HBase等。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RWi1DIvg-1640752865988)(D:/BaiduNetdiskDownload/01_课件/images/image-20211020173921726.png)]

图形数据库

图形数据库,利用了图这种数据结构存储了实体(对象)之间的关系。图形数据库最典型的例子就是社交网络中人与人的关系,数据模型主要是以节点和边(关系)来实现,特点在于能高效地解决复杂的关系问题。

图形数据库顾名思义,就是一种存储图形关系的数据库。它利用了图这种数据结构存储了实体(对象)之间的关系。关系型数据用于存储明确关系的数据,但对于复杂关系的数据存储却有些力不从心。如社交网络中人物之间的关系,如果用关系型数据库则非常复杂,用图形数据库将非常简单。典型产品:Neo4J、InfoGrid等。

image-20211020180934455
4.2.3 NoSQL的演变

由于 SQL 一直称霸 DBMS,因此许多人在思考是否有一种数据库技术能远离 SQL,于是 NoSQL 诞生了,但是随着发展却发现越来越离不开 SQL。到目前为止 NoSQL 阵营中的 DBMS 都会有实现类似 SQL 的功能。下面是“NoSQL”这个名词在不同时期的诠释,从这些释义的变化中可以看出 NoSQL 功能的演变

1970:NoSQL = We have no SQL

1980:NoSQL = Know SQL

2000:NoSQL = No SQL!

2005:NoSQL = Not only SQL

2013:NoSQL = No, SQL!

NoSQL 对 SQL 做出了很好的补充,比如实际开发中,有很多业务需求,其实并不需要完整的关系型数据库功能,非关系型数据库的功能就足够使用了。这种情况下,使用性能更高成本更低的非关系型数据库当然是更明智的选择。比如:日志收集、排行榜、定时器等。

4.3 小结

NoSQL 的分类很多,即便如此,在 DBMS 排名中,还是 SQL 阵营的比重更大,影响力前 5 的 DBMS 中有 4 个是关系型数据库,而排名前 20 的 DBMS 中也有 12 个是关系型数据库。所以说,掌握 SQL 是非常有必要的。整套课程将围绕 SQL 展开。

5. 关系型数据库设计规则

  • 关系型数据库的典型数据结构就是数据表,这些数据表的组成都是结构化的(Structured)。

  • 将数据放到表中,表再放到库中。

  • 一个数据库中可以有多个表,每个表都有一个名字,用来标识自己。表名具有唯一性。

  • 表具有一些特性,这些特性定义了数据在表中如何存储,类似Java和Python中 “类”的设计。

5.1 表、记录、字段

  • E-R(entity-relationship,实体-联系)模型中有三个主要概念是:实体集属性联系集

  • 一个实体集(class)对应于数据库中的一个表(table),一个实体(instance)则对应于数据库表中的一行(row),也称为一条记录(record)。一个属性(attribute)对应于数据库表中的一列(column),也称为一个字段(field)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pziodAxf-1640752865989)(D:/BaiduNetdiskDownload/01_课件/images/image-20210914235450032-1634141235163.png)]

ORM思想 (Object Relational Mapping)体现:
数据库中的一个表  <---> Java或Python中的一个类
表中的一条数据  <---> 类中的一个对象(或实体)
表中的一个列  <----> 类中的一个字段、属性(field)

5.2 表的关联关系

  • 表与表之间的数据记录有关系(relationship)。现实世界中的各种实体以及实体之间的各种联系均用关系模型来表示。

  • 四种:一对一关联、一对多关联、多对多关联、自我引用

5.2.1 一对一关联(one-to-one)
  • 在实际的开发中应用不多,因为一对一可以创建成一张表。
  • 举例:设计学生表:学号、姓名、手机号码、班级、系别、身份证号码、家庭住址、籍贯、紧急联系人、…
    • 拆为两个表:两个表的记录是一一对应关系。

    • 基础信息表(常用信息):学号、姓名、手机号码、班级、系别

    • 档案信息表(不常用信息):学号、身份证号码、家庭住址、籍贯、紧急联系人、…

  • 两种建表原则:
    • 外键唯一:主表的主键和从表的外键(唯一),形成主外键关系,外键唯一。
    • 外键是主键:主表的主键和从表的主键,形成主外键关系。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z200hOLt-1640752865989)(D:/BaiduNetdiskDownload/01_课件/images/image-20210914235534452.png)]

5.2.2 一对多关系(one-to-many)
  • 常见实例场景:客户表和订单表分类表和商品表部门表和员工表
  • 举例:
    • 员工表:编号、姓名、…、所属部门

    • 部门表:编号、名称、简介

  • 一对多建表原则:在从表(多方)创建一个字段,字段作为外键指向主表(一方)的主键

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bFwPBzHB-1640752865990)(D:/BaiduNetdiskDownload/01_课件/images/image-20210915001013524.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yiZ0g8N1-1640752865990)(D:/BaiduNetdiskDownload/01_课件/images/image-20210914235610597.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zeeqK5nz-1640752865990)(D:/BaiduNetdiskDownload/01_课件/images/image-20210915084623432.png)]

5.2.3 多对多(many-to-many)

要表示多对多关系,必须创建第三个表,该表通常称为联接表,它将多对多关系划分为两个一对多关系。将这两个表的主键都插入到第三个表中。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ICarBD2y-1640752865991)(D:/BaiduNetdiskDownload/01_课件/images/image-20210915001048215.png)]

  • 举例1:学生-课程

    • 学生信息表:一行代表一个学生的信息(学号、姓名、手机号码、班级、系别…)

    • 课程信息表:一行代表一个课程的信息(课程编号、授课老师、简介…)

    • 选课信息表:一个学生可以选多门课,一门课可以被多个学生选择

      学号     课程编号  
      1        1001
      2        1001
      1        1002
      
  • 举例2:产品-订单

    “订单”表和“产品”表有一种多对多的关系,这种关系是通过与“订单明细”表建立两个一对多关系来定义的。一个订单可以有多个产品,每个产品可以出现在多个订单中。

    • 产品表:“产品”表中的每条记录表示一个产品。
    • 订单表:“订单”表中的每条记录表示一个订单。
    • 订单明细表:每个产品可以与“订单”表中的多条记录对应,即出现在多个订单中。一个订单可以与“产品”表中的多条记录对应,即包含多个产品。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gIBiNcOs-1640752865991)(D:/BaiduNetdiskDownload/01_课件/images/image-20210914235637068.png)]

  • 举例3:用户-角色
  • 多对多关系建表原则:需要创建第三张表,中间表中至少两个字段,这两个字段分别作为外键指向各自一方的主键。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZSug2aHo-1640752865991)(D:/BaiduNetdiskDownload/01_课件/images/image-20210915084707586.png)]

5.3.4 自我引用(Self reference)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pSJqKR0X-1640752865992)(images/image-20210914235651997.png)]

第02章_MySQL环境搭建

讲师:尚硅谷 宋红康(江湖人称:康师傅)

官网:http://www.atguigu.com


1. MySQL的卸载

步骤1:停止MySQL服务

在卸载之前,先停止MySQL8.0的服务。按键盘上的“Ctrl + Alt + Delete”组合键,打开“任务管理器”对话框,可以在“服务”列表找到“MySQL8.0”的服务,如果现在“正在运行”状态,可以右键单击服务,选择“停止”选项停止MySQL8.0的服务,如图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m5lL2DIN-1640752865992)(D:\BaiduNetdiskDownload\01_课件\images\image-20211014153604802.png)]

步骤2:软件的卸载

方式1:通过控制面板方式

卸载MySQL8.0的程序可以和其他桌面应用程序一样直接在“控制面板”选择“卸载程序”,并在程序列表中找到MySQL8.0服务器程序,直接双击卸载即可,如图所示。这种方式删除,数据目录下的数据不会跟着删除。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tm4u0BSx-1640752865993)(D:\BaiduNetdiskDownload\01_课件\images\image-20211014153657668.png)]

方式2:通过360或电脑管家等软件卸载

方式3:通过安装包提供的卸载功能卸载

你也可以通过安装向导程序进行MySQL8.0服务器程序的卸载。

① 再次双击下载的mysql-installer-community-8.0.26.0.msi文件,打开安装向导。安装向导会自动检测已安装的MySQL服务器程序。

② 选择要卸载的MySQL服务器程序,单击“Remove”(移除),即可进行卸载。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i0SEliHC-1640752865993)(D:\BaiduNetdiskDownload\01_课件\images\image-20211014153722683.png)]

③ 单击“Next”(下一步)按钮,确认卸载。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ij0Mjhfi-1640752865994)(D:\BaiduNetdiskDownload\01_课件\images\image-20211014153747283.png)]

④ 弹出是否同时移除数据目录选择窗口。如果想要同时删除MySQL服务器中的数据,则勾选“Remove the data directory”,如图所示。

image-20211014154112574

⑤ 执行卸载。单击“Execute”(执行)按钮进行卸载。

image-20211014154006530

⑥ 完成卸载。单击“Finish”(完成)按钮即可。如果想要同时卸载MySQL8.0的安装向导程序,勾选“Yes,Uninstall MySQL Installer”即可,如图所示。

image-20211014154046268

步骤3:残余文件的清理

如果再次安装不成功,可以卸载后对残余文件进行清理后再安装。

(1)服务目录:mysql服务的安装目录

(2)数据目录:默认在C:\ProgramData\MySQL

如果自己单独指定过数据目录,就找到自己的数据目录进行删除即可。

注意:请在卸载前做好数据备份

在操作完以后,需要重启计算机,然后进行安装即可。如果仍然安装失败,需要继续操作如下步骤4。

步骤4:清理注册表(选做)

如果前几步做了,再次安装还是失败,那么可以清理注册表。

如何打开注册表编辑器:在系统的搜索框中输入regedit

HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\Eventlog\Application\MySQL服务 目录删除

HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\MySQL服务 目录删除

HKEY_LOCAL_MACHINE\SYSTEM\ControlSet002\Services\Eventlog\Application\MySQL服务 目录删除

HKEY_LOCAL_MACHINE\SYSTEM\ControlSet002\Services\MySQL服务 目录删除

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application\MySQL服务目录删除

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\MySQL服务删除

注册表中的ControlSet001,ControlSet002,不一定是001和002,可能是ControlSet005、006之类

步骤5:删除环境变量配置

找到path环境变量,将其中关于mysql的环境变量删除,切记不要全部删除。

例如:删除 D:\develop_tools\mysql\MySQLServer8.0.26\bin; 这个部分

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dpc84KFm-1640752865994)(D:\BaiduNetdiskDownload\01_课件\images\1575694476072.png)]

2. MySQL的下载、安装、配置

2.1 MySQL的4大版本

  • MySQL Community Server 社区版本,开源免费,自由下载,但不提供官方技术支持,适用于大多数普通用户。

  • MySQL Enterprise Edition 企业版本,需付费,不能在线下载,可以试用30天。提供了更多的功能和更完备的技术支持,更适合于对数据库的功能和可靠性要求较高的企业客户。

  • MySQL Cluster 集群版,开源免费。用于架设集群服务器,可将几个MySQL Server封装成一个Server。需要在社区版或企业版的基础上使用。

  • MySQL Cluster CGE 高级集群版,需付费。

  • 目前最新版本为8.0.27,发布时间2021年10月。此前,8.0.0 在 2016.9.12日就发布了。

  • 本课程中使用8.0.26版本

此外,官方还提供了MySQL Workbench(GUITOOL)一款专为MySQL设计的图形界面管理工具。MySQLWorkbench又分为两个版本,分别是社区版(MySQL Workbench OSS)、商用版(MySQL WorkbenchSE)。

2.2 软件的下载

1. 下载地址

官网:https://www.mysql.com

2. 打开官网,点击DOWNLOADS

然后,点击MySQL Community(GPL) Downloads

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hJ2xIgpp-1640752865995)(D:\BaiduNetdiskDownload\01_课件\images\image-20210817185920150.png)]

3. 点击 MySQL Community Server

image-20210817185955123

4. 在General Availability(GA) Releases中选择适合的版本

Windows平台下提供两种安装文件:MySQL二进制分发版(.msi安装文件)和免安装版(.zip压缩文件)。一般来讲,应当使用二进制分发版,因为该版本提供了图形化的安装向导过程,比其他的分发版使用起来要简单,不再需要其他工具启动就可以运行MySQL。

  • 这里在Windows 系统下推荐下载MSI安装程序;点击Go to Download Page进行下载即可
image-20210727192819147

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vH9oG6qz-1640752865995)(D:\BaiduNetdiskDownload\01_课件\images\image-20211014163001964.png)]

  • Windows下的MySQL8.0安装有两种安装程序
    • mysql-installer-web-community-8.0.26.0.msi 下载程序大小:2.4M;安装时需要联网安装组件。
    • mysql-installer-community-8.0.26.0.msi 下载程序大小:450.7M;安装时离线安装即可。推荐。
  • 如果安装MySQL5.7版本的话,选择Archives,接着选择MySQL5.7的相应版本即可。这里下载最近期的MySQL5.7.34版本。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UNr7EfFH-1640752865996)(D:\BaiduNetdiskDownload\01_课件\images\image-20211014163228051.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8b7tdH3T-1640752865996)(D:\BaiduNetdiskDownload\01_课件\images\image-20211014163353156.png)]

2.3 MySQL8.0 版本的安装

MySQL下载完成后,找到下载文件,双击进行安装,具体操作步骤如下。

步骤1:双击下载的mysql-installer-community-8.0.26.0.msi文件,打开安装向导。

步骤2:打开“Choosing a Setup Type”(选择安装类型)窗口,在其中列出了5种安装类型,分别是Developer Default(默认安装类型)、Server only(仅作为服务器)、Client only(仅作为客户端)、Full(完全安装)、Custom(自定义安装)。这里选择“Custom(自定义安装)”类型按钮,单击“Next(下一步)”按钮。

image-20211014170553535

步骤3:打开“Select Products” (选择产品)窗口,可以定制需要安装的产品清单。例如,选择“MySQL Server 8.0.26-X64”后,单击“→”添加按钮,即可选择安装MySQL服务器,如图所示。采用通用的方法,可以添加其他你需要安装的产品。

image-20211014170638699

此时如果直接“Next”(下一步),则产品的安装路径是默认的。如果想要自定义安装目录,则可以选中对应的产品,然后在下面会出现“Advanced Options”(高级选项)的超链接。

image-20211014170814386

单击“Advanced Options”(高级选项)则会弹出安装目录的选择窗口,如图所示,此时你可以分别设置MySQL的服务程序安装目录和数据存储目录。如果不设置,默认分别在C盘的Program Files目录和ProgramData目录(这是一个隐藏目录)。如果自定义安装目录,请避免“中文”目录。另外,建议服务目录和数据目录分开存放。

image-20211014170857263

步骤4:在上一步选择好要安装的产品之后,单击“Next”(下一步)进入确认窗口,如图所示。单击“Execute”(执行)按钮开始安装。

image-20211014170934889

步骤5:安装完成后在“Status”(状态)列表下将显示“Complete”(安装完成),如图所示。

image-20211014171002259

2.4 配置MySQL8.0

MySQL安装之后,需要对服务器进行配置。具体的配置步骤如下。

步骤1:在上一个小节的最后一步,单击“Next”(下一步)按钮,就可以进入产品配置窗口。

img

步骤2:单击“Next”(下一步)按钮,进入MySQL服务器类型配置窗口,如图所示。端口号一般选择默认端口号3306。

img

其中,“Config Type”选项用于设置服务器的类型。单击该选项右侧的下三角按钮,即可查看3个选项,如图所示。

img
  • Development Machine(开发机器):该选项代表典型个人用桌面工作站。此时机器上需要运行多个应用程序,那么MySQL服务器将占用最少的系统资源。

  • Server Machine(服务器):该选项代表服务器,MySQL服务器可以同其他服务器应用程序一起运行,例如Web服务器等。MySQL服务器配置成适当比例的系统资源。

  • Dedicated Machine(专用服务器):该选项代表只运行MySQL服务的服务器。MySQL服务器配置成使用所有可用系统资源。

步骤3:单击“Next”(下一步)按钮,打开设置授权方式窗口。其中,上面的选项是MySQL8.0提供的新的授权方式,采用SHA256基础的密码加密方法;下面的选项是传统授权方法(保留5.x版本兼容性)。

img

步骤4:单击“Next”(下一步)按钮,打开设置服务器root超级管理员的密码窗口,如图所示,需要输入两次同样的登录密码。也可以通过“Add User”添加其他用户,添加其他用户时,需要指定用户名、允许该用户名在哪台/哪些主机上登录,还可以指定用户角色等。此处暂不添加用户,用户管理在MySQL高级特性篇中讲解。

img

步骤5:单击“Next”(下一步)按钮,打开设置服务器名称窗口,如图所示。该服务名会出现在Windows服务列表中,也可以在命令行窗口中使用该服务名进行启动和停止服务。本书将服务名设置为“MySQL80”。如果希望开机自启动服务,也可以勾选“Start the MySQL Server at System Startup”选项(推荐)。

下面是选择以什么方式运行服务?可以选择“Standard System Account”(标准系统用户)或者“Custom User”(自定义用户)中的一个。这里推荐前者。

img

步骤6:单击“Next”(下一步)按钮,打开确认设置服务器窗口,单击“Execute”(执行)按钮。

img

步骤7:完成配置,如图所示。单击“Finish”(完成)按钮,即可完成服务器的配置。

img

步骤8:如果还有其他产品需要配置,可以选择其他产品,然后继续配置。如果没有,直接选择“Next”(下一步),直接完成整个安装和配置过程。

img

步骤9:结束安装和配置。

img

2.5 配置MySQL8.0 环境变量

如果不配置MySQL环境变量,就不能在命令行直接输入MySQL登录命令。下面说如何配置MySQL的环境变量:

步骤1:在桌面上右击【此电脑】图标,在弹出的快捷菜单中选择【属性】菜单命令。
步骤2:打开【系统】窗口,单击【高级系统设置】链接。
步骤3:打开【系统属性】对话框,选择【高级】选项卡,然后单击【环境变量】按钮。
步骤4:打开【环境变量】对话框,在系统变量列表中选择path变量。
步骤5:单击【编辑】按钮,在【编辑环境变量】对话框中,将MySQL应用程序的bin目录(C:\Program Files\MySQL\MySQL Server 8.0\bin)添加到变量值中,用分号将其与其他路径分隔开。
步骤6:添加完成之后,单击【确定】按钮,这样就完成了配置path变量的操作,然后就可以直接输入MySQL命令来登录数据库了。

2.6 MySQL5.7 版本的安装、配置

  • 安装

此版本的安装过程与上述过程除了版本号不同之外,其它环节都是相同的。所以这里省略了MySQL5.7.34版本的安装截图。

  • 配置

配置环节与MySQL8.0版本确有细微不同。大部分情况下直接选择“Next”即可,不影响整理使用。

这里配置MySQL5.7时,重点强调:与前面安装好的MySQL8.0不能使用相同的端口号。

2.7 安装失败问题

MySQL的安装和配置是一件非常简单的事,但是在操作过程中也可能出现问题,特别是初学者。

问题1:无法打开MySQL8.0软件安装包或者安装过程中失败,如何解决?

在运行MySQL8.0软件安装包之前,用户需要确保系统中已经安装了.Net Framework相关软件,如果缺少此软件,将不能正常地安装MySQL8.0软件。

img

解决方案:到这个地址https://www.microsoft.com/en-us/download/details.aspx?id=42642下载Microsoft .NET Framework 4.5并安装后,再去安装MySQL。

另外,还要确保Windows Installer正常安装。windows上安装mysql8.0需要操作系统提前已安装好Microsoft Visual C++ 2015-2019。

img img

解决方案同样是,提前到微软官网https://support.microsoft.com/en-us/topic/the-latest-supported-visual-c-downloads-2647da03-1eea-4433-9aff-95f26a218cc0,下载相应的环境。

问题2:卸载重装MySQL失败?

该问题通常是因为MySQL卸载时,没有完全清除相关信息导致的。

解决办法是,把以前的安装目录删除。如果之前安装并未单独指定过服务安装目录,则默认安装目录是“C:\Program Files\MySQL”,彻底删除该目录。同时删除MySQL的Data目录,如果之前安装并未单独指定过数据目录,则默认安装目录是“C:\ProgramData\MySQL”,该目录一般为隐藏目录。删除后,重新安装即可。

问题3:如何在Windows系统删除之前的未卸载干净的MySQL服务列表?

操作方法如下,在系统“搜索框”中输入“cmd”,按“Enter”(回车)键确认,弹出命令提示符界面。然后输入“sc delete MySQL服务名”,按“Enter”(回车)键,就能彻底删除残余的MySQL服务了。

3. MySQL的登录

3.1 服务的启动与停止

MySQL安装完毕之后,需要启动服务器进程,不然客户端无法连接数据库。

在前面的配置过程中,已经将MySQL安装为Windows服务,并且勾选当Windows启动、停止时,MySQL也自动启动、停止。

方式1:使用图形界面工具
  • 步骤1:打开windows服务

    • 方式1:计算机(点击鼠标右键)→ 管理(点击)→ 服务和应用程序(点击)→ 服务(点击)
    • 方式2:控制面板(点击)→ 系统和安全(点击)→ 管理工具(点击)→ 服务(点击)
    • 方式3:任务栏(点击鼠标右键)→ 启动任务管理器(点击)→ 服务(点击)
    • 方式4:单击【开始】菜单,在搜索框中输入“services.msc”,按Enter键确认
  • 步骤2:找到MySQL80(点击鼠标右键)→ 启动或停止(点击)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2rBXY4Zh-1640752865996)(D:\BaiduNetdiskDownload\01_课件\images\image-20211014183908375.png)]

方式2:使用命令行工具
# 启动 MySQL 服务命令:
net start MySQL服务名

# 停止 MySQL 服务命令:
net stop MySQL服务名
image-20211014184037414

说明:

  1. start和stop后面的服务名应与之前配置时指定的服务名一致。

  2. 如果当你输入命令后,提示“拒绝服务”,请以系统管理员身份打开命令提示符界面重新尝试。

3.2 自带客户端的登录与退出

当MySQL服务启动完成后,便可以通过客户端来登录MySQL数据库。注意:确认服务是开启的。

登录方式1:MySQL自带客户端

开始菜单 → 所有程序 → MySQL → MySQL 8.0 Command Line Client

image-20211014184425147

说明:仅限于root用户

登录方式2:windows命令行
  • 格式:
mysql -h 主机名 -P 端口号 -u 用户名 -p密码
  • 举例:
mysql -h localhost -P 3306 -u root -pabc123  # 这里我设置的root用户的密码是abc123
image-20211014185035137

注意:

(1)-p与密码之间不能有空格,其他参数名与参数值之间可以有空格也可以没有空格。如:

mysql -hlocalhost -P3306 -uroot -pabc123

(2)密码建议在下一行输入,保证安全

mysql -h localhost -P 3306 -u root -p
Enter password:****

(3)客户端和服务器在同一台机器上,所以输入localhost或者IP地址127.0.0.1。同时,因为是连接本机:
-hlocalhost就可以省略,如果端口号没有修改:-P3306也可以省略

简写成:

mysql -u root -p
Enter password:****

连接成功后,有关于MySQL Server服务版本的信息,还有第几次连接的id标识。

也可以在命令行通过以下方式获取MySQL Server服务版本的信息:

c:\> mysql -V
c:\> mysql --version

登录后,通过以下方式查看当前版本信息:

mysql> select version();
退出登录
exit或quit

4. MySQL演示使用

4.1 MySQL的使用演示

1、查看所有的数据库

show databases;

“information_schema”是 MySQL 系统自带的数据库,主要保存 MySQL 数据库服务器的系统信息,比如数据库的名称、数据表的名称、字段名称、存取权限、数据文件 所在的文件夹和系统使用的文件夹,等等

“performance_schema”是 MySQL 系统自带的数据库,可以用来监控 MySQL 的各类性能指标。

“sys”数据库是 MySQL 系统自带的数据库,主要作用是以一种更容易被理解的方式展示 MySQL 数据库服务器的各类性能指标,帮助系统管理员和开发人员监控 MySQL 的技术性能。

“mysql”数据库保存了 MySQL 数据库服务器运行时需要的系统信息,比如数据文件夹、当前使用的字符集、约束检查信息,等等

为什么 Workbench 里面我们只能看到“demo”和“sys”这 2 个数据库呢?

这是因为,Workbench 是图形化的管理工具,主要面向开发人 员,“demo”和“sys”这 2 个数据库已经够用了。如果有特殊需求,比如,需要监控 MySQL 数据库各项性能指标、直接操作 MySQL 数据库系统文件等,可以由 DBA 通过 SQL 语句,查看其它的系统数据库。

2、创建自己的数据库

create database 数据库名;#创建atguigudb数据库,该名称不能与已经存在的数据库重名。create database atguigudb;

3、使用自己的数据库

use 数据库名;#使用atguigudb数据库use atguigudb;

说明:如果没有使用use语句,后面针对数据库的操作也没有加“数据名”的限定,那么会报“ERROR 1046 (3D000): No database selected”(没有选择数据库)

使用完use语句之后,如果接下来的SQL都是针对一个数据库操作的,那就不用重复use了,如果要针对另一个数据库操作,那么要重新use。

4、查看某个库的所有表格

show tables;  #要求前面有use语句show tables from 数据库名;

5、创建新的表格

create table 表名称(	字段名  数据类型,	字段名 数据类型);

说明:如果是最后一个字段,后面就用加逗号,因为逗号的作用是分割每个字段。

#创建学生表create table student(	id int,    name varchar(20)  #说名字最长不超过20个字符);

6、查看一个表的数据

select * from 数据库表名称;
#查看学生表的数据select * from student;

7、添加一条记录

insert into 表名称 values(值列表);#添加两条记录到student表中insert into student values(1,'张三');insert into student values(2,'李四');

报错:

mysql> insert into student values(1,'张三');ERROR 1366 (HY000): Incorrect string value: '\xD5\xC5\xC8\xFD' for column 'name' at row 1mysql> insert into student values(2,'李四');ERROR 1366 (HY000): Incorrect string value: '\xC0\xEE\xCB\xC4' for column 'name' at row 1mysql> show create table student;

字符集的问题。

8、查看表的创建信息

show create table 表名称\G#查看student表的详细创建信息show create table student\G
#结果如下*************************** 1. row ***************************       Table: studentCreate Table: CREATE TABLE `student` (  `id` int(11) DEFAULT NULL,  `name` varchar(20) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin11 row in set (0.00 sec)

上面的结果显示student的表格的默认字符集是“latin1”不支持中文。

9、查看数据库的创建信息

show create database 数据库名\G#查看atguigudb数据库的详细创建信息show create database atguigudb\G
#结果如下*************************** 1. row ***************************       Database: atguigudbCreate Database: CREATE DATABASE `atguigudb` /*!40100 DEFAULT CHARACTER SET latin1 */1 row in set (0.00 sec)

上面的结果显示atguigudb数据库也不支持中文,字符集默认是latin1。

10、删除表格

drop table 表名称;
#删除学生表drop table student;

11、删除数据库

drop database 数据库名;
#删除atguigudb数据库drop database atguigudb;

4.2 MySQL的编码设置

MySQL5.7中

问题再现:命令行操作sql乱码问题

mysql> INSERT INTO t_stu VALUES(1,'张三','男');ERROR 1366 (HY000): Incorrect string value: '\xD5\xC5\xC8\xFD' for column 'sname' at row 1

问题解决

步骤1:查看编码命令

show variables like 'character_%';show variables like 'collation_%';

步骤2:修改mysql的数据目录下的my.ini配置文件

[mysql]  #大概在63行左右,在其下添加... default-character-set=utf8  #默认字符集[mysqld]  # 大概在76行左右,在其下添加...character-set-server=utf8collation-server=utf8_general_ci

注意:建议修改配置文件使用notepad++等高级文本编辑器,使用记事本等软件打开修改后可能会导致文件编码修改为“含BOM头”的编码,从而服务重启失败。

步骤3:重启服务

步骤4:查看编码命令

show variables like 'character_%';show variables like 'collation_%';
  • 如果是以上配置就说明对了。接着我们就可以新创建数据库、新创建数据表,接着添加包含中文的数据了。
MySQL8.0中

在MySQL 8.0版本之前,默认字符集为latin1,utf8字符集指向的是utf8mb3。网站开发人员在数据库设计的时候往往会将编码修改为utf8字符集。如果遗忘修改默认的编码,就会出现乱码的问题。从MySQL 8.0开始,数据库的默认编码改为utf8mb4,从而避免了上述的乱码问题。

5. MySQL图形化管理工具

MySQL图形化管理工具极大地方便了数据库的操作与管理,常用的图形化管理工具有:MySQL Workbench、phpMyAdmin、Navicat Preminum、MySQLDumper、SQLyog、dbeaver、MySQL ODBC Connector。

工具1. MySQL Workbench

MySQL官方提供的图形化管理工具MySQL Workbench完全支持MySQL 5.0以上的版本。MySQL Workbench分为社区版和商业版,社区版完全免费,而商业版则是按年收费。

MySQL Workbench 为数据库管理员、程序开发者和系统规划师提供可视化设计、模型建立、以及数据库管理功能。它包含了用于创建复杂的数据建模ER模型,正向和逆向数据库工程,也可以用于执行通常需要花费大量时间的、难以变更和管理的文档任务。

下载地址:http://dev.mysql.com/downloads/workbench/。

使用:

首先,我们点击 Windows 左下角的“开始”按钮,如果你是 Win10 系统,可以直接看到所有程序。接着,找到“MySQL”,点开,找到“MySQL Workbench 8.0 CE”。点击打开 Workbench,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dlEg4CrZ-1640752865997)(D:\BaiduNetdiskDownload\01_课件\images\image-20211007153522427.png)]

左下角有个本地连接,点击,录入 Root 的密码,登录本地 MySQL 数据库服务器,如下图所示:

image-20211014195108502 image-20211014195129219 image-20211014195142849

这是一个图形化的界面,我来给你介绍下这个界面。

  • 上方是菜单。左上方是导航栏,这里我们可以看到 MySQL 数据库服务器里面的数据 库,包括数据表、视图、存储过程和函数;左下方是信息栏,可以显示上方选中的数据 库、数据表等对象的信息。

  • 中间上方是工作区,你可以在这里写 SQL 语句,点击上方菜单栏左边的第三个运行按 钮,就可以执行工作区的 SQL 语句了。

  • 中间下方是输出区,用来显示 SQL 语句的运行情况,包括什么时间开始运行的、运行的 内容、运行的输出,以及所花费的时长等信息。

好了,下面我们就用 Workbench 实际创建一个数据库,并且导入一个 Excel 数据文件, 来生成一个数据表。数据表是存储数据的载体,有了数据表以后,我们就能对数据进行操作了。

工具2. Navicat

Navicat MySQL是一个强大的MySQL数据库服务器管理和开发工具。它可以与任何3.21或以上版本的MySQL一起工作,支持触发器、存储过程、函数、事件、视图、管理用户等,对于新手来说易学易用。其精心设计的图形用户界面(GUI)可以让用户用一种安全简便的方式来快速方便地创建、组织、访问和共享信息。Navicat支持中文,有免费版本提供。
下载地址:http://www.navicat.com/。

image-20210913180359685

工具3. SQLyog

SQLyog 是业界著名的 Webyog 公司出品的一款简洁高效、功能强大的图形化 MySQL 数据库管理工具。这款工具是使用C++语言开发的。该工具可以方便地创建数据库、表、视图和索引等,还可以方便地进行插入、更新和删除等操作,同时可以方便地进行数据库、数据表的备份和还原。该工具不仅可以通过SQL文件进行大量文件的导入和导出,还可以导入和导出XML、HTML和CSV等多种格式的数据。
下载地址:http://www.webyog.com/,读者也可以搜索中文版的下载地址。

image-20211014213018979 image-20211014213036470

工具4:dbeaver

DBeaver是一个通用的数据库管理工具和 SQL 客户端,支持所有流行的数据库:MySQL、PostgreSQL、SQLite、Oracle、DB2、SQL Server、 Sybase、MS Access、Teradata、 Firebird、Apache Hive、Phoenix、Presto等。DBeaver比大多数的SQL管理工具要轻量,而且支持中文界面。DBeaver社区版作为一个免费开源的产品,和其他类似的软件相比,在功能和易用性上都毫不逊色。

唯一需要注意是 DBeaver 是用Java编程语言开发的,所以需要拥有 JDK(Java Development ToolKit)环境。如果电脑上没有JDK,在选择安装DBeaver组件时,勾选“Include Java”即可。

下载地址:https://dbeaver.io/download/

image-20211014195237457 image-20211014195251371 image-20211014195300510 image-20211014195309805

可能出现连接问题:

有些图形界面工具,特别是旧版本的图形界面工具,在连接MySQL8时出现“Authentication plugin ‘caching_sha2_password’ cannot be loaded”错误。

image-20211019215249254

出现这个原因是MySQL8之前的版本中加密规则是mysql_native_password,而在MySQL8之后,加密规则是caching_sha2_password。解决问题方法有两种,第一种是升级图形界面工具版本,第二种是把MySQL8用户登录密码加密规则还原成mysql_native_password。

第二种解决方案如下,用命令行登录MySQL数据库之后,执行如下命令修改用户密码加密规则并更新用户密码,这里修改用户名为“root@localhost”的用户密码规则为“mysql_native_password”,密码值为“123456”,如图所示。

#使用mysql数据库USE mysql; #修改'root'@'localhost'用户的密码规则和密码ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'abc123'; #刷新权限FLUSH PRIVILEGES;
image-20211019215408965

6. MySQL目录结构与源码

6.1 主要目录结构

MySQL的目录结构说明
bin目录所有MySQL的可执行文件。如:mysql.exe
MySQLInstanceConfig.exe数据库的配置向导,在安装时出现的内容
data目录系统数据库所在的目录
my.ini文件MySQL的主要配置文件
c:\ProgramData\MySQL\MySQL Server 8.0\data\用户创建的数据库所在的目录

6.2 MySQL 源代码获取

首先,你要进入 MySQL下载界面。 这里你不要选择用默认的“Microsoft Windows”,而是要通过下拉栏,找到“Source Code”,在下面的操作系统版本里面, 选择 Windows(Architecture Independent),然后点击下载。

接下来,把下载下来的压缩文件解压,我们就得到了 MySQL 的源代码。

MySQL 是用 C++ 开发而成的,我简单介绍一下源代码的组成。

mysql-8.0.22 目录下的各个子目录,包含了 MySQL 各部分组件的源代码:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NFXj68Jw-1640752865997)(D:\BaiduNetdiskDownload\01_课件\images\image-20211007154113052.png)]

  • sql 子目录是 MySQL 核心代码;

  • libmysql 子目录是客户端程序 API;

  • mysql-test 子目录是测试工具;

  • mysys 子目录是操作系统相关函数和辅助函数;

源代码可以用记事本打开查看,如果你有 C++ 的开发环境,也可以在开发环境中打开查看。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U1HA0kzg-1640752865998)(D:\BaiduNetdiskDownload\01_课件\images\image-20211007154213156.png)]

如上图所示,源代码并不神秘,就是普通的 C++ 代码,跟你熟悉的一样,而且有很多注释,可以帮助你理解。阅读源代码就像在跟 MySQL 的开发人员对话一样,十分有趣。

7. 常见问题的解决(课外内容)

问题1:root用户密码忘记,重置的操作

1: 通过任务管理器或者服务管理,关掉mysqld(服务进程)
2: 通过命令行+特殊参数开启mysqld
mysqld --defaults-file=“D:\ProgramFiles\mysql\MySQLServer5.7Data\my.ini” --skip-grant-tables

3: 此时,mysqld服务进程已经打开。并且不需要权限检查
4: mysql -uroot 无密码登陆服务器。另启动一个客户端进行
5: 修改权限表
(1) use mysql;
(2)update user set authentication_string=password(‘新密码’) where user=‘root’ and Host=‘localhost’;
(3)flush privileges;
6: 通过任务管理器,关掉mysqld服务进程。
7: 再次通过服务管理,打开mysql服务。
8: 即可用修改后的新密码登陆。

问题2:mysql命令报“不是内部或外部命令”

如果输入mysql命令报“不是内部或外部命令”,把mysql安装目录的bin目录配置到环境变量path中。如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xi43UAA0-1640752865998)(D:\BaiduNetdiskDownload\01_课件\images\image-20210914093150145.png)]

问题3:错误ERROR :没有选择数据库就操作表格和数据

ERROR 1046 (3D000): No database selected
解决方案一:就是使用“USE 数据库名;”语句,这样接下来的语句就默认针对这个数据库进行操作
解决方案二:就是所有的表对象前面都加上“数据库.”

问题4:命令行客户端的字符集问题

mysql> INSERT INTO t_stu VALUES(1,'张三','男');ERROR 1366 (HY000): Incorrect string value: '\xD5\xC5\xC8\xFD' for column 'sname' at row 1

原因:服务器端认为你的客户端的字符集是utf-8,而实际上你的客户端的字符集是GBK。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nl62BKoX-1640752865999)(D:\BaiduNetdiskDownload\01_课件\images\1554912924219.png)]

查看所有字符集:SHOW VARIABLES LIKE ‘character_set_%’;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EdemwKrc-1640752865999)(D:\BaiduNetdiskDownload\01_课件\images\1554912943186.png)]

解决方案,设置当前连接的客户端字符集 “SET NAMES GBK;”

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KVgKNWH7-1640752865999)(D:\BaiduNetdiskDownload\01_课件\images\1554912957353.png)]

问题5:修改数据库和表的字符编码

修改编码:

(1)先停止服务,(2)修改my.ini文件(3)重新启动服务

说明:

如果是在修改my.ini之前建的库和表,那么库和表的编码还是原来的Latin1,要么删了重建,要么使用alter语句修改编码。

mysql> create database 0728db charset Latin1;Query OK, 1 row affected (0.00 sec)
mysql> use 0728db;Database changed
mysql> create table student (id int , name varchar(20)) charset Latin1;Query OK, 0 rows affected (0.02 sec)mysql> show create table student\G*************************** 1. row ***************************       Table: studentCreate Table: CREATE TABLE `student` (  `id` int(11) NOT NULL,  `name` varchar(20) DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=latin11 row in set (0.00 sec)
mysql> alter table student charset utf8; #修改表字符编码为UTF8Query OK, 0 rows affected (0.01 sec)Records: 0  Duplicates: 0  Warnings: 0mysql> show create table student\G*************************** 1. row ***************************       Table: studentCreate Table: CREATE TABLE `student` (  `id` int(11) NOT NULL,  `name` varchar(20) CHARACTER SET latin1 DEFAULT NULL,  #字段仍然是latin1编码  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf81 row in set (0.00 sec)mysql> alter table student modify name varchar(20) charset utf8; #修改字段字符编码为UTF8Query OK, 0 rows affected (0.05 sec)Records: 0  Duplicates: 0  Warnings: 0mysql> show create table student\G*************************** 1. row ***************************       Table: studentCreate Table: CREATE TABLE `student` (  `id` int(11) NOT NULL,  `name` varchar(20) DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf81 row in set (0.00 sec)
mysql> show create database 0728db;;+--------+-----------------------------------------------------------------+|Database| Create Database                                                 |+------+-------------------------------------------------------------------+|0728db| CREATE DATABASE `0728db` /*!40100 DEFAULT CHARACTER SET latin1 */ |+------+-------------------------------------------------------------------+1 row in set (0.00 sec)mysql> alter database 0728db charset utf8; #修改数据库的字符编码为utf8Query OK, 1 row affected (0.00 sec)mysql> show create database 0728db;+--------+-----------------------------------------------------------------+|Database| Create Database                                                 |+--------+-----------------------------------------------------------------+| 0728db | CREATE DATABASE `0728db` /*!40100 DEFAULT CHARACTER SET utf8 */ |+--------+-----------------------------------------------------------------+1 row in set (0.00 sec)

第03章_基本的SELECT语句

讲师:尚硅谷-宋红康(江湖人称:康师傅)

官网:http://www.atguigu.com


1. SQL概述

1.1 SQL背景知识

  • 1946 年,世界上第一台电脑诞生,如今,借由这台电脑发展起来的互联网已经自成江湖。在这几十年里,无数的技术、产业在这片江湖里沉浮,有的方兴未艾,有的已经几幕兴衰。但在这片浩荡的波动里,有一门技术从未消失,甚至“老当益壮”,那就是 SQL。

    • 45 年前,也就是 1974 年,IBM 研究员发布了一篇揭开数据库技术的论文《SEQUEL:一门结构化的英语查询语言》,直到今天这门结构化的查询语言并没有太大的变化,相比于其他语言,SQL 的半衰期可以说是非常长了。
  • 不论是前端工程师,还是后端算法工程师,都一定会和数据打交道,都需要了解如何又快又准确地提取自己想要的数据。更别提数据分析师了,他们的工作就是和数据打交道,整理不同的报告,以便指导业务决策。

  • SQL(Structured Query Language,结构化查询语言)是使用关系模型的数据库应用语言,与数据直接打交道,由IBM上世纪70年代开发出来。后由美国国家标准局(ANSI)开始着手制定SQL标准,先后有SQL-86SQL-89SQL-92SQL-99等标准。

    • SQL 有两个重要的标准,分别是 SQL92 和 SQL99,它们分别代表了 92 年和 99 年颁布的 SQL 标准,我们今天使用的 SQL 语言依然遵循这些标准。
  • 不同的数据库生产厂商都支持SQL语句,但都有特有内容。

SQLisputonghua

1.2 SQL语言排行榜

自从 SQL 加入了 TIOBE 编程语言排行榜,就一直保持在 Top 10。

image-20211014230114639

1.3 SQL 分类

SQL语言在功能上主要分为如下3大类:

  • DDL(Data Definition Languages、数据定义语言),这些语句定义了不同的数据库、表、视图、索引等数据库对象,还可以用来创建、删除、修改数据库和数据表的结构。

    • 主要的语句关键字包括CREATEDROPALTER等。
  • DML(Data Manipulation Language、数据操作语言),用于添加、删除、更新和查询数据库记录,并检查数据完整性。

    • 主要的语句关键字包括INSERTDELETEUPDATESELECT等。
    • SELECT是SQL语言的基础,最为重要。
  • DCL(Data Control Language、数据控制语言),用于定义数据库、表、字段、用户的访问权限和安全级别。

    • 主要的语句关键字包括GRANTREVOKECOMMITROLLBACKSAVEPOINT等。

因为查询语句使用的非常的频繁,所以很多人把查询语句单拎出来一类:DQL(数据查询语言)。

还有单独将COMMITROLLBACK 取出来称为TCL (Transaction Control Language,事务控制语言)。

2. SQL语言的规则与规范

2.1 基本规则

  • SQL 可以写在一行或者多行。为了提高可读性,各子句分行写,必要时使用缩进
  • 每条命令以 ; 或 \g 或 \G 结束
  • 关键字不能被缩写也不能分行
  • 关于标点符号
    • 必须保证所有的()、单引号、双引号是成对结束的
    • 必须使用英文状态下的半角输入方式
    • 字符串型和日期时间类型的数据可以使用单引号(’ ')表示
    • 列的别名,尽量使用双引号(" "),而且不建议省略as

2.2 SQL大小写规范 (建议遵守)

  • MySQL 在 Windows 环境下是大小写不敏感的
  • MySQL 在 Linux 环境下是大小写敏感的
    • 数据库名、表名、表的别名、变量名是严格区分大小写的
    • 关键字、函数名、列名(或字段名)、列的别名(字段的别名) 是忽略大小写的。
  • 推荐采用统一的书写规范:
    • 数据库名、表名、表别名、字段名、字段别名等都小写
    • SQL 关键字、函数名、绑定变量等都大写

2.3 注 释

可以使用如下格式的注释结构

单行注释:#注释文字(MySQL特有的方式)
单行注释:-- 注释文字(--后面必须包含一个空格。)
多行注释:/* 注释文字  */

2.4 命名规则(暂时了解)

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

举例:

#以下两句是一样的,不区分大小写
show databases;
SHOW DATABASES;

#创建表格
#create table student info(...); #表名错误,因为表名有空格
create table student_info(...); 

#其中order使用``飘号,因为order和系统关键字或系统函数名等预定义标识符重名了
CREATE TABLE `order`(
    id INT,
    lname VARCHAR(20)
);

select id as "编号", `name` as "姓名" from t_stu; #起别名时,as都可以省略
select id as 编号, `name` as 姓名 from t_stu; #如果字段别名中没有空格,那么可以省略""
select id as 编 号, `name` as 姓 名 from t_stu; #错误,如果字段别名中有空格,那么不能省略""

2.5 数据导入指令

在命令行客户端登录mysql,使用source指令导入

mysql> source d:\mysqldb.sql
mysql> desc employees;
+----------------+-------------+------+-----+---------+-------+
| Field          | Type        | Null | Key | Default | Extra |
+----------------+-------------+------+-----+---------+-------+
| employee_id    | int(6)      | NO   | PRI | 0       |       |
| first_name     | varchar(20) | YES  |     | NULL    |       |
| last_name      | varchar(25) | NO   |     | NULL    |       |
| email          | varchar(25) | NO   | UNI | NULL    |       |
| phone_number   | varchar(20) | YES  |     | NULL    |       |
| hire_date      | date        | NO   |     | NULL    |       |
| job_id         | varchar(10) | NO   | MUL | NULL    |       |
| salary         | double(8,2) | YES  |     | NULL    |       |
| commission_pct | double(2,2) | YES  |     | NULL    |       |
| manager_id     | int(6)      | YES  | MUL | NULL    |       |
| department_id  | int(4)      | YES  | MUL | NULL    |       |
+----------------+-------------+------+-----+---------+-------+
11 rows in set (0.00 sec)

3. 基本的SELECT语句

3.0 SELECT…

SELECT 1; #没有任何子句
SELECT 9/2; #没有任何子句

3.1 SELECT … FROM

  • 语法:
SELECT   标识选择哪些列
FROM     标识从哪个表中选择
  • 选择全部列:
SELECT *
FROM   departments;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y4HzhOSY-1640752866000)(D:/BaiduNetdiskDownload/01_课件/images/1554950890895.png)]

一般情况下,除非需要使用表中所有的字段数据,最好不要使用通配符‘*’。使用通配符虽然可以节省输入查询语句的时间,但是获取不需要的列数据通常会降低查询和所使用的应用程序的效率。通配符的优势是,当不知道所需要的列的名称时,可以通过它获取它们。

在生产环境下,不推荐你直接使用SELECT *进行查询。

  • 选择特定的列:
SELECT department_id, location_id
FROM   departments;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nUJB4XBl-1640752866000)(D:/BaiduNetdiskDownload/01_课件/images/1554950947969.png)]

MySQL中的SQL语句是不区分大小写的,因此SELECT和select的作用是相同的,但是,许多开发人员习惯将关键字大写、数据列和表名小写,读者也应该养成一个良好的编程习惯,这样写出来的代码更容易阅读和维护。

3.2 列的别名

  • 重命名一个列

  • 便于计算

  • 紧跟列名,也可以在列名和别名之间加入关键字AS,别名使用双引号,以便在别名中包含空格或特殊的字符并区分大小写。

  • AS 可以省略

  • 建议别名简短,见名知意

  • 举例

    SELECT last_name AS name, commission_pct commFROM   employees;
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cNOPHKIQ-1640752866001)(D:/BaiduNetdiskDownload/01_课件/images/1554951616598.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EkfiMkia-1640752866001)(D:/BaiduNetdiskDownload/01_课件/images/1554951622467.png)]

    SELECT last_name "Name", salary*12 "Annual Salary"FROM   employees;
    

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a6z6kBYS-1640752866001)(D:/BaiduNetdiskDownload/01_课件/images/1554951648377.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ao8jVRAI-1640752866002)(D:/BaiduNetdiskDownload/01_课件/images/1554951655368.png)]

3.3 去除重复行

默认情况下,查询会返回全部行,包括重复行。

SELECT department_idFROM   employees;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OblOMfMU-1640752866002)(D:/BaiduNetdiskDownload/01_课件/images/1554951711115.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M2xfo85b-1640752866003)(D:/BaiduNetdiskDownload/01_课件/images/1554951715923.png)]

在SELECT语句中使用关键字DISTINCT去除重复行

SELECT DISTINCT department_idFROM   employees;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qKBpQuQ2-1640752866003)(D:/BaiduNetdiskDownload/01_课件/images/1554951796570.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tY995cFn-1640752866003)(D:/BaiduNetdiskDownload/01_课件/images/1554951801044.png)]

针对于:

SELECT DISTINCT department_id,salary FROM employees;

这里有两点需要注意:

  1. DISTINCT 需要放到所有列名的前面,如果写成SELECT salary, DISTINCT department_id FROM employees会报错。
  2. DISTINCT 其实是对后面所有列名的组合进行去重,你能看到最后的结果是 74 条,因为这 74 个部门id不同,都有 salary 这个属性值。如果你想要看都有哪些不同的部门(department_id),只需要写DISTINCT department_id即可,后面不需要再加其他的列名了。

3.4 空值参与运算

  • 所有运算符或列值遇到null值,运算的结果都为null
SELECT employee_id,salary,commission_pct,12 * salary * (1 + commission_pct) "annual_sal"FROM employees;

这里你一定要注意,在 MySQL 里面, 空值不等于空字符串。一个空字符串的长度是 0,而一个空值的长度是空。而且,在 MySQL 里面,空值是占用空间的。

3.5 着重号

  • 错误的
mysql> SELECT * FROM ORDER;ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ORDER' at line 1
  • 正确的
mysql> SELECT * FROM `ORDER`;+----------+------------+| order_id | order_name |+----------+------------+|        1 | shkstart   ||        2 | tomcat     ||        3 | dubbo      |+----------+------------+3 rows in set (0.00 sec)mysql> SELECT * FROM `order`;+----------+------------+| order_id | order_name |+----------+------------+|        1 | shkstart   ||        2 | tomcat     ||        3 | dubbo      |+----------+------------+3 rows in set (0.00 sec)
  • 结论

我们需要保证表中的字段、表名等没有和保留字、数据库系统或常用方法冲突。如果真的相同,请在SQL语句中使用一对``(着重号)引起来。

3.6 5、查询常数

SELECT 查询还可以对常数进行查询。对的,就是在 SELECT 查询结果中增加一列固定的常数列。这列的取值是我们指定的,而不是从数据表中动态取出的。

你可能会问为什么我们还要对常数进行查询呢?

SQL 中的 SELECT 语法的确提供了这个功能,一般来说我们只从一个表中查询数据,通常不需要增加一个固定的常数列,但如果我们想整合不同的数据源,用常数列作为这个表的标记,就需要查询常数。

比如说,我们想对 employees 数据表中的员工姓名进行查询,同时增加一列字段corporation,这个字段固定值为“尚硅谷”,可以这样写:

SELECT '尚硅谷' as corporation, last_name FROM employees;

4. 显示表结构

使用DESCRIBE 或 DESC 命令,表示表结构。

DESCRIBE employees;或DESC employees;
mysql> desc employees;+----------------+-------------+------+-----+---------+-------+| Field          | Type        | Null | Key | Default | Extra |+----------------+-------------+------+-----+---------+-------+| employee_id    | int(6)      | NO   | PRI | 0       |       || first_name     | varchar(20) | YES  |     | NULL    |       || last_name      | varchar(25) | NO   |     | NULL    |       || email          | varchar(25) | NO   | UNI | NULL    |       || phone_number   | varchar(20) | YES  |     | NULL    |       || hire_date      | date        | NO   |     | NULL    |       || job_id         | varchar(10) | NO   | MUL | NULL    |       || salary         | double(8,2) | YES  |     | NULL    |       || commission_pct | double(2,2) | YES  |     | NULL    |       || manager_id     | int(6)      | YES  | MUL | NULL    |       || department_id  | int(4)      | YES  | MUL | NULL    |       |+----------------+-------------+------+-----+---------+-------+11 rows in set (0.00 sec)

其中,各个字段的含义分别解释如下:

  • Field:表示字段名称。
  • Type:表示字段类型,这里 barcode、goodsname 是文本型的,price 是整数类型的。
  • Null:表示该列是否可以存储NULL值。
  • Key:表示该列是否已编制索引。PRI表示该列是表主键的一部分;UNI表示该列是UNIQUE索引的一部分;MUL表示在列中某个给定值允许出现多次。
  • Default:表示该列是否有默认值,如果有,那么值是多少。
  • Extra:表示可以获取的与给定列有关的附加信息,例如AUTO_INCREMENT等。

5. 过滤数据

  • 背景:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EZsG7XDz-1640752866004)(D:/BaiduNetdiskDownload/01_课件/images/1554952199742.png)]

  • 语法:

    SELECT 字段1,字段2FROM 表名WHERE 过滤条件
    
    • 使用WHERE 子句,将不满足条件的行过滤掉
    • WHERE子句紧随 FROM子句
  • 举例

SELECT employee_id, last_name, job_id, department_idFROM   employeesWHERE  department_id = 90 ;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v5bWWwHb-1640752866004)(D:/BaiduNetdiskDownload/01_课件/images/1554952277028.png)]

第04章_运算符

讲师:尚硅谷-宋红康(江湖人称:康师傅)

官网:http://www.atguigu.com


1. 算术运算符

算术运算符主要用于数学运算,其可以连接运算符前后的两个数值或表达式,对数值或表达式进行加(+)、减(-)、乘(*)、除(/)和取模(%)运算。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X78g76ZU-1640752866004)(D:/BaiduNetdiskDownload/01_课件/images/image-20211012100749193.png)]

1.加法与减法运算符

mysql> SELECT 100, 100 + 0, 100 - 0, 100 + 50, 100 + 50 -30, 100 + 35.5, 100 - 35.5 FROM dual;
+-----+---------+---------+----------+--------------+------------+------------+
| 100 | 100 + 0 | 100 - 0 | 100 + 50 | 100 + 50 -30 | 100 + 35.5 | 100 - 35.5 |
+-----+---------+---------+----------+--------------+------------+------------+
| 100 |     100 |     100 |      150 |          120 |      135.5 |       64.5 |
+-----+---------+---------+----------+--------------+------------+------------+
1 row in set (0.00 sec)

由运算结果可以得出如下结论:

  • 一个整数类型的值对整数进行加法和减法操作,结果还是一个整数;
  • 一个整数类型的值对浮点数进行加法和减法操作,结果是一个浮点数;
  • 加法和减法的优先级相同,进行先加后减操作与进行先减后加操作的结果是一样的;
  • 在Java中,+的左右两边如果有字符串,那么表示字符串的拼接。但是在MySQL中+只表示数值相加。如果遇到非数值类型,先尝试转成数值,如果转失败,就按0计算。(补充:MySQL中字符串拼接要使用字符串函数CONCAT()实现)

2.乘法与除法运算符

mysql> SELECT 100, 100 * 1, 100 * 1.0, 100 / 1.0, 100 / 2,100 + 2 * 5 / 2,100 /3, 100 DIV 0 FROM dual;
+-----+---------+-----------+-----------+---------+-----------------+---------+-----------+
| 100 | 100 * 1 | 100 * 1.0 | 100 / 1.0 | 100 / 2 | 100 + 2 * 5 / 2 | 100 /3  | 100 DIV 0 |
+-----+---------+-----------+-----------+---------+-----------------+---------+-----------+
| 100 |     100 |     100.0 |  100.0000 | 50.0000 |        105.0000 | 33.3333 |      NULL |
+-----+---------+-----------+-----------+---------+-----------------+---------+-----------+
1 row in set (0.00 sec)
#计算出员工的年基本工资
SELECT employee_id,salary,salary * 12 annual_sal 
FROM employees;

由运算结果可以得出如下结论:

  • 一个数乘以整数1和除以整数1后仍得原数;
  • 一个数乘以浮点数1和除以浮点数1后变成浮点数,数值与原数相等;
  • 一个数除以整数后,不管是否能除尽,结果都为一个浮点数;
  • 一个数除以另一个数,除不尽时,结果为一个浮点数,并保留到小数点后4位;
  • 乘法和除法的优先级相同,进行先乘后除操作与先除后乘操作,得出的结果相同。
  • 在数学运算中,0不能用作除数,在MySQL中,一个数除以0为NULL。

3.求模(求余)运算符
将t22表中的字段i对3和5进行求模(求余)运算。

mysql> SELECT 12 % 3, 12 MOD 5 FROM dual;
+--------+----------+
| 12 % 3 | 12 MOD 5 |
+--------+----------+
|      0 |        2 |
+--------+----------+
1 row in set (0.00 sec)
#筛选出employee_id是偶数的员工
SELECT * FROM employees
WHERE employee_id MOD 2 = 0;

可以看到,100对3求模后的结果为3,对5求模后的结果为0。

2. 比较运算符

比较运算符用来对表达式左边的操作数和右边的操作数进行比较,比较的结果为真则返回1,比较的结果为假则返回0,其他情况则返回NULL。

比较运算符经常被用来作为SELECT查询语句的条件来使用,返回符合条件的结果记录。

image-20211012101110021

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R4naT5nV-1640752866005)(D:/BaiduNetdiskDownload/01_课件/images/image-20211012104955094.png)]

1.等号运算符

  • 等号运算符(=)判断等号两边的值、字符串或表达式是否相等,如果相等则返回1,不相等则返回0。

  • 在使用等号运算符时,遵循如下规则:

    • 如果等号两边的值、字符串或表达式都为字符串,则MySQL会按照字符串进行比较,其比较的是每个字符串中字符的ANSI编码是否相等。
    • 如果等号两边的值都是整数,则MySQL会按照整数来比较两个值的大小。
    • 如果等号两边的值一个是整数,另一个是字符串,则MySQL会将字符串转化为数字进行比较。
    • 如果等号两边的值、字符串或表达式中有一个为NULL,则比较结果为NULL。
  • 对比:SQL中赋值符号使用 :=

mysql> SELECT 1 = 1, 1 = '1', 1 = 0, 'a' = 'a', (5 + 3) = (2 + 6), '' = NULL , NULL = NULL; 
+-------+---------+-------+-----------+-------------------+-----------+-------------+
| 1 = 1 | 1 = '1' | 1 = 0 | 'a' = 'a' | (5 + 3) = (2 + 6) | '' = NULL | NULL = NULL |
+-------+---------+-------+-----------+-------------------+-----------+-------------+
|    1  |     1   |   0   |      1    |             1     |    NULL   |        NULL  |
+-------+---------+-------+-----------+-------------------+-----------+-------------+
1 row in set (0.00 sec)
mysql> SELECT 1 = 2, 0 = 'abc', 1 = 'abc' FROM dual;
+-------+-----------+-----------+
| 1 = 2 | 0 = 'abc' | 1 = 'abc' |
+-------+-----------+-----------+
|     0 |         1 |         0 |
+-------+-----------+-----------+
1 row in set, 2 warnings (0.00 sec)
#查询salary=10000,注意在Java中比较是==
SELECT employee_id,salary FROM employees WHERE salary = 10000;

2.安全等于运算符
安全等于运算符(<=>)与等于运算符(=)的作用是相似的,唯一的区别是‘<=>’可以用来对NULL进行判断。在两个操作数均为NULL时,其返回值为1,而不为NULL;当一个操作数为NULL时,其返回值为0,而不为NULL。

mysql> SELECT 1 <=> '1', 1 <=> 0, 'a' <=> 'a', (5 + 3) <=> (2 + 6), '' <=> NULL,NULL <=> NULL FROM dual;+-----------+---------+-------------+---------------------+-------------+---------------+| 1 <=> '1' | 1 <=> 0 | 'a' <=> 'a' | (5 + 3) <=> (2 + 6) | '' <=> NULL | NULL <=> NULL |+-----------+---------+-------------+---------------------+-------------+---------------+|         1 |       0 |           1 |                   1 |           0 |             1 |+-----------+---------+-------------+---------------------+-------------+---------------+1 row in set (0.00 sec)
#查询commission_pct等于0.40SELECT employee_id,commission_pct FROM employees WHERE commission_pct = 0.40;SELECT employee_id,commission_pct FROM employees WHERE commission_pct <=> 0.40;#如果把0.40改成 NULL 呢?

可以看到,使用安全等于运算符时,两边的操作数的值都为NULL时,返回的结果为1而不是NULL,其他返回结果与等于运算符相同。

3.不等于运算符
不等于运算符(<>和!=)用于判断两边的数字、字符串或者表达式的值是否不相等,如果不相等则返回1,相等则返回0。不等于运算符不能判断NULL值。如果两边的值有任意一个为NULL,或两边都为NULL,则结果为NULL。
SQL语句示例如下:

mysql> SELECT 1 <> 1, 1 != 2, 'a' != 'b', (3+4) <> (2+6), 'a' != NULL, NULL <> NULL; +--------+--------+------------+----------------+-------------+--------------+| 1 <> 1 | 1 != 2 | 'a' != 'b' | (3+4) <> (2+6) | 'a' != NULL | NULL <> NULL |+--------+--------+------------+----------------+-------------+--------------+|      0 |   1    |       1    |            1   |     NULL    |         NULL |+--------+--------+------------+----------------+-------------+--------------+1 row in set (0.00 sec)

此外,还有非符号类型的运算符:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sGATtD8w-1640752866005)(D:/BaiduNetdiskDownload/01_课件/images/image-20211012105303219.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WfitlMpb-1640752866006)(D:/BaiduNetdiskDownload/01_课件/images/image-20211012105030527.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pfQ1To66-1640752866006)(D:/BaiduNetdiskDownload/01_课件/images/image-20211012105052456.png)]

4. 空运算符
空运算符(IS NULL或者ISNULL)判断一个值是否为NULL,如果为NULL则返回1,否则返回0。
SQL语句示例如下:

mysql> SELECT NULL IS NULL, ISNULL(NULL), ISNULL('a'), 1 IS NULL;+--------------+--------------+-------------+-----------+| NULL IS NULL | ISNULL(NULL) | ISNULL('a') | 1 IS NULL |+--------------+--------------+-------------+-----------+|            1 |            1 |           0 |         0 |+--------------+--------------+-------------+-----------+1 row in set (0.00 sec)
#查询commission_pct等于NULL。比较如下的四种写法SELECT employee_id,commission_pct FROM employees WHERE commission_pct IS NULL;SELECT employee_id,commission_pct FROM employees WHERE commission_pct <=> NULL;SELECT employee_id,commission_pct FROM employees WHERE ISNULL(commission_pct);SELECT employee_id,commission_pct FROM employees WHERE commission_pct = NULL;
SELECT last_name, manager_idFROM   employeesWHERE  manager_id IS NULL;

5. 非空运算符
非空运算符(IS NOT NULL)判断一个值是否不为NULL,如果不为NULL则返回1,否则返回0。
SQL语句示例如下:

mysql> SELECT NULL IS NOT NULL, 'a' IS NOT NULL,  1 IS NOT NULL; +------------------+-----------------+---------------+| NULL IS NOT NULL | 'a' IS NOT NULL | 1 IS NOT NULL |+------------------+-----------------+---------------+|                0 |               1 |             1 |+------------------+-----------------+---------------+1 row in set (0.01 sec)
#查询commission_pct不等于NULLSELECT employee_id,commission_pct FROM employees WHERE commission_pct IS NOT NULL;SELECT employee_id,commission_pct FROM employees WHERE NOT commission_pct <=> NULL;SELECT employee_id,commission_pct FROM employees WHERE NOT ISNULL(commission_pct);

6. 最小值运算符
语法格式为:LEAST(值1,值2,…,值n)。其中,“值n”表示参数列表中有n个值。在有两个或多个参数的情况下,返回最小值。

mysql> SELECT LEAST (1,0,2), LEAST('b','a','c'), LEAST(1,NULL,2);+---------------+--------------------+-----------------+| LEAST (1,0,2) | LEAST('b','a','c') | LEAST(1,NULL,2) |+---------------+--------------------+-----------------+|       0       |        a           |      NULL       |+---------------+--------------------+-----------------+1 row in set (0.00 sec)

由结果可以看到,当参数是整数或者浮点数时,LEAST将返回其中最小的值;当参数为字符串时,返回字母表中顺序最靠前的字符;当比较值列表中有NULL时,不能判断大小,返回值为NULL。

7. 最大值运算符
语法格式为:GREATEST(值1,值2,…,值n)。其中,n表示参数列表中有n个值。当有两个或多个参数时,返回值为最大值。假如任意一个自变量为NULL,则GREATEST()的返回值为NULL。

mysql> SELECT GREATEST(1,0,2), GREATEST('b','a','c'), GREATEST(1,NULL,2);+-----------------+-----------------------+--------------------+| GREATEST(1,0,2) | GREATEST('b','a','c') | GREATEST(1,NULL,2) |+-----------------+-----------------------+--------------------+|               2 | c                     |               NULL |+-----------------+-----------------------+--------------------+1 row in set (0.00 sec)

由结果可以看到,当参数中是整数或者浮点数时,GREATEST将返回其中最大的值;当参数为字符串时,返回字母表中顺序最靠后的字符;当比较值列表中有NULL时,不能判断大小,返回值为NULL。

8. BETWEEN AND运算符
BETWEEN运算符使用的格式通常为SELECT D FROM TABLE WHERE C BETWEEN A AND B,此时,当C大于或等于A,并且C小于或等于B时,结果为1,否则结果为0。

mysql> SELECT 1 BETWEEN 0 AND 1, 10 BETWEEN 11 AND 12, 'b' BETWEEN 'a' AND 'c';+-------------------+----------------------+-------------------------+| 1 BETWEEN 0 AND 1 | 10 BETWEEN 11 AND 12 | 'b' BETWEEN 'a' AND 'c' |+-------------------+----------------------+-------------------------+|                 1 |                    0 |                       1 |+-------------------+----------------------+-------------------------+1 row in set (0.00 sec)
SELECT last_name, salaryFROM   employeesWHERE  salary BETWEEN 2500 AND 3500;

9. IN运算符
IN运算符用于判断给定的值是否是IN列表中的一个值,如果是则返回1,否则返回0。如果给定的值为NULL,或者IN列表中存在NULL,则结果为NULL。

mysql> SELECT 'a' IN ('a','b','c'), 1 IN (2,3), NULL IN ('a','b'), 'a' IN ('a', NULL);+----------------------+------------+-------------------+--------------------+| 'a' IN ('a','b','c') | 1 IN (2,3) | NULL IN ('a','b') | 'a' IN ('a', NULL) |+----------------------+------------+-------------------+--------------------+|            1         |        0   |         NULL      |         1          |+----------------------+------------+-------------------+--------------------+1 row in set (0.00 sec)
SELECT employee_id, last_name, salary, manager_idFROM   employeesWHERE  manager_id IN (100, 101, 201);

10. NOT IN运算符
NOT IN运算符用于判断给定的值是否不是IN列表中的一个值,如果不是IN列表中的一个值,则返回1,否则返回0。

mysql> SELECT 'a' NOT IN ('a','b','c'), 1 NOT IN (2,3);+--------------------------+----------------+| 'a' NOT IN ('a','b','c') | 1 NOT IN (2,3) |+--------------------------+----------------+|                 0        |            1   |+--------------------------+----------------+1 row in set (0.00 sec)

11. LIKE运算符
LIKE运算符主要用来匹配字符串,通常用于模糊匹配,如果满足条件则返回1,否则返回0。如果给定的值或者匹配条件为NULL,则返回结果为NULL。

LIKE运算符通常使用如下通配符:

“%”:匹配0个或多个字符。“_”:只能匹配一个字符。

SQL语句示例如下:

mysql> SELECT NULL LIKE 'abc', 'abc' LIKE NULL;  +-----------------+-----------------+| NULL LIKE 'abc' | 'abc' LIKE NULL |+-----------------+-----------------+|          NULL   |          NULL   |+-----------------+-----------------+1 row in set (0.00 sec)
SELECT	first_nameFROM 	employeesWHERE	first_name LIKE 'S%';
SELECT last_nameFROM   employeesWHERE  last_name LIKE '_o%';

ESCAPE

  • 回避特殊符号的:使用转义符。例如:将[%]转为[ %]、[]转为[ ],然后再加上[ESCAPE‘$’]即可。
SELECT job_idFROM   jobsWHERE  job_id LIKE ‘IT\_%‘;
  • 如果使用\表示转义,要省略ESCAPE。如果不是\,则要加上ESCAPE。
SELECT job_idFROM   jobsWHERE  job_id LIKE ‘IT$_%‘ escape ‘$‘;

12. REGEXP运算符

REGEXP运算符用来匹配字符串,语法格式为:expr REGEXP 匹配条件。如果expr满足匹配条件,返回1;如果不满足,则返回0。若expr或匹配条件任意一个为NULL,则结果为NULL。

REGEXP运算符在进行匹配时,常用的有下面几种通配符:

(1)‘^’匹配以该字符后面的字符开头的字符串。(2)‘$’匹配以该字符前面的字符结尾的字符串。(3)‘.’匹配任何一个单字符。(4)“[...]”匹配在方括号内的任何字符。例如,“[abc]”匹配“a”或“b”或“c”。为了命名字符的范围,使用一个‘-’。“[a-z]”匹配任何字母,而“[0-9]”匹配任何数字。(5)‘*’匹配零个或多个在它前面的字符。例如,“x*”匹配任何数量的‘x’字符,“[0-9]*”匹配任何数量的数字,而“*”匹配任何数量的任何字符。

SQL语句示例如下:

mysql> SELECT 'shkstart' REGEXP '^s', 'shkstart' REGEXP 't$', 'shkstart' REGEXP 'hk';+------------------------+------------------------+-------------------------+| 'shkstart' REGEXP '^s' | 'shkstart' REGEXP 't$' | 'shkstart' REGEXP 'hk'  |+------------------------+------------------------+-------------------------+|                      1 |                      1 |                       1 |+------------------------+------------------------+-------------------------+1 row in set (0.01 sec)
mysql> SELECT 'atguigu' REGEXP 'gu.gu', 'atguigu' REGEXP '[ab]';+--------------------------+-------------------------+| 'atguigu' REGEXP 'gu.gu' | 'atguigu' REGEXP '[ab]' |+--------------------------+-------------------------+|                        1 |                       1 |+--------------------------+-------------------------+1 row in set (0.00 sec)

3. 逻辑运算符

逻辑运算符主要用来判断表达式的真假,在MySQL中,逻辑运算符的返回结果为1、0或者NULL。

MySQL中支持4种逻辑运算符如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zBipsgj2-1640752866006)(D:/BaiduNetdiskDownload/01_课件/images/image-20211012110241418.png)]

1.逻辑非运算符
逻辑非(NOT或!)运算符表示当给定的值为0时返回1;当给定的值为非0值时返回0;当给定的值为NULL时,返回NULL。

mysql> SELECT NOT 1, NOT 0, NOT(1+1), NOT !1, NOT NULL;    +-------+-------+----------+--------+----------+| NOT 1 | NOT 0 | NOT(1+1) | NOT !1 | NOT NULL |+-------+-------+----------+--------+----------+|     0 |     1 |        0 |      1 |     NULL |+-------+-------+----------+--------+----------+1 row in set, 1 warning (0.00 sec)
SELECT last_name, job_idFROM   employeesWHERE  job_id NOT IN ('IT_PROG', 'ST_CLERK', 'SA_REP');

2.逻辑与运算符
逻辑与(AND或&&)运算符是当给定的所有值均为非0值,并且都不为NULL时,返回1;当给定的一个值或者多个值为0时则返回0;否则返回NULL。

mysql> SELECT 1 AND -1, 0 AND 1, 0 AND NULL, 1 AND NULL;+----------+---------+------------+------------+| 1 AND -1 | 0 AND 1 | 0 AND NULL | 1 AND NULL |+----------+---------+------------+------------+|        1 |       0 |          0 |       NULL |+----------+---------+------------+------------+1 row in set (0.00 sec)
SELECT employee_id, last_name, job_id, salaryFROM   employeesWHERE  salary >=10000AND    job_id LIKE '%MAN%';

3.逻辑或运算符
逻辑或(OR或||)运算符是当给定的值都不为NULL,并且任何一个值为非0值时,则返回1,否则返回0;当一个值为NULL,并且另一个值为非0值时,返回1,否则返回NULL;当两个值都为NULL时,返回NULL。

mysql> SELECT 1 OR -1, 1 OR 0, 1 OR NULL, 0 || NULL, NULL || NULL;     +---------+--------+-----------+-----------+--------------+| 1 OR -1 | 1 OR 0 | 1 OR NULL | 0 || NULL | NULL || NULL |+---------+--------+-----------+-----------+--------------+|       1 |      1 |         1 |    NULL   |       NULL   |+---------+--------+-----------+-----------+--------------+1 row in set, 2 warnings (0.00 sec)
#查询基本薪资不在9000-12000之间的员工编号和基本薪资SELECT employee_id,salary FROM employees WHERE NOT (salary >= 9000 AND salary <= 12000);SELECT employee_id,salary FROM employees WHERE salary <9000 OR salary > 12000;SELECT employee_id,salary FROM employees WHERE salary NOT BETWEEN 9000 AND 12000;
SELECT employee_id, last_name, job_id, salaryFROM   employeesWHERE  salary >= 10000OR     job_id LIKE '%MAN%';

注意:

OR可以和AND一起使用,但是在使用时要注意两者的优先级,由于AND的优先级高于OR,因此先对AND两边的操作数进行操作,再与OR中的操作数结合。

4.逻辑异或运算符
逻辑异或(XOR)运算符是当给定的值中任意一个值为NULL时,则返回NULL;如果两个非NULL的值都是0或者都不等于0时,则返回0;如果一个值为0,另一个值不为0时,则返回1。

mysql> SELECT 1 XOR -1, 1 XOR 0, 0 XOR 0, 1 XOR NULL, 1 XOR 1 XOR 1, 0 XOR 0 XOR 0;+----------+---------+---------+------------+---------------+---------------+| 1 XOR -1 | 1 XOR 0 | 0 XOR 0 | 1 XOR NULL | 1 XOR 1 XOR 1 | 0 XOR 0 XOR 0 |+----------+---------+---------+------------+---------------+---------------+|        0 |       1 |       0 |       NULL |             1 |             0 |+----------+---------+---------+------------+---------------+---------------+1 row in set (0.00 sec)
select last_name,department_id,salary from employeeswhere department_id in (10,20) XOR salary > 8000;

4. 位运算符

位运算符是在二进制数上进行计算的运算符。位运算符会先将操作数变成二进制数,然后进行位运算,最后将计算结果从二进制变回十进制数。

MySQL支持的位运算符如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XBNvVyLn-1640752866007)(D:/BaiduNetdiskDownload/01_课件/images/image-20211012110511223.png)]

1.按位与运算符
按位与(&)运算符将给定值对应的二进制数逐位进行逻辑与运算。当给定值对应的二进制位的数值都为1时,则该位返回1,否则返回0。

mysql> SELECT 1 & 10, 20 & 30;+--------+---------+| 1 & 10 | 20 & 30 |+--------+---------+|      0 |      20 |+--------+---------+1 row in set (0.00 sec)

1的二进制数为0001,10的二进制数为1010,所以1 & 10的结果为0000,对应的十进制数为0。20的二进制数为10100,30的二进制数为11110,所以20 & 30的结果为10100,对应的十进制数为20。

2. 按位或运算符
按位或(|)运算符将给定的值对应的二进制数逐位进行逻辑或运算。当给定值对应的二进制位的数值有一个或两个为1时,则该位返回1,否则返回0。

mysql> SELECT 1 | 10, 20 | 30; +--------+---------+| 1 | 10 | 20 | 30 |+--------+---------+|     11 |      30 |+--------+---------+1 row in set (0.00 sec)

1的二进制数为0001,10的二进制数为1010,所以1 | 10的结果为1011,对应的十进制数为11。20的二进制数为10100,30的二进制数为11110,所以20 | 30的结果为11110,对应的十进制数为30。

3. 按位异或运算符
按位异或(^)运算符将给定的值对应的二进制数逐位进行逻辑异或运算。当给定值对应的二进制位的数值不同时,则该位返回1,否则返回0。

mysql> SELECT 1 ^ 10, 20 ^ 30; +--------+---------+| 1 ^ 10 | 20 ^ 30 |+--------+---------+|     11 |      10 |+--------+---------+1 row in set (0.00 sec)

1的二进制数为0001,10的二进制数为1010,所以1 ^ 10的结果为1011,对应的十进制数为11。20的二进制数为10100,30的二进制数为11110,所以20 ^ 30的结果为01010,对应的十进制数为10。

再举例:

mysql> SELECT 12 & 5, 12 | 5,12 ^ 5 FROM DUAL;+--------+--------+--------+| 12 & 5 | 12 | 5 | 12 ^ 5 |+--------+--------+--------+|      4 |     13 |      9 |+--------+--------+--------+1 row in set (0.00 sec)
image-20211023115738415

4. 按位取反运算符
按位取反(~)运算符将给定的值的二进制数逐位进行取反操作,即将1变为0,将0变为1。

mysql> SELECT 10 & ~1;+---------+| 10 & ~1 |+---------+|      10 |+---------+1 row in set (0.00 sec)

由于按位取反(~)运算符的优先级高于按位与(&)运算符的优先级,所以10 & ~1,首先,对数字1进行按位取反操作,结果除了最低位为0,其他位都为1,然后与10进行按位与操作,结果为10。

5. 按位右移运算符
按位右移(>>)运算符将给定的值的二进制数的所有位右移指定的位数。右移指定的位数后,右边低位的数值被移出并丢弃,左边高位空出的位置用0补齐。

mysql> SELECT 1 >> 2, 4 >> 2;+--------+--------+| 1 >> 2 | 4 >> 2 |+--------+--------+|      0 |      1 |+--------+--------+1 row in set (0.00 sec)

1的二进制数为0000 0001,右移2位为0000 0000,对应的十进制数为0。4的二进制数为0000 0100,右移2位为0000 0001,对应的十进制数为1。

6. 按位左移运算符
按位左移(<<)运算符将给定的值的二进制数的所有位左移指定的位数。左移指定的位数后,左边高位的数值被移出并丢弃,右边低位空出的位置用0补齐。

mysql> SELECT 1 << 2, 4 << 2;  +--------+--------+| 1 << 2 | 4 << 2 |+--------+--------+|      4 |     16 |+--------+--------+1 row in set (0.00 sec)

1的二进制数为0000 0001,左移两位为0000 0100,对应的十进制数为4。4的二进制数为0000 0100,左移两位为0001 0000,对应的十进制数为16。

5. 运算符的优先级

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d3PdeCGW-1640752866007)(D:/BaiduNetdiskDownload/01_课件/images/image-20211012111042395.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eZLUJj79-1640752866008)(D:/BaiduNetdiskDownload/01_课件/images/image-20211012110731059.png)]

数字编号越大,优先级越高,优先级高的运算符先进行计算。可以看到,赋值运算符的优先级最低,使用“()”括起来的表达式的优先级最高。

拓展:使用正则表达式查询

正则表达式通常被用来检索或替换那些符合某个模式的文本内容,根据指定的匹配模式匹配文本中符合要求的特殊字符串。例如,从一个文本文件中提取电话号码,查找一篇文章中重复的单词或者替换用户输入的某些敏感词语等,这些地方都可以使用正则表达式。正则表达式强大而且灵活,可以应用于非常复杂的查询。

MySQL中使用REGEXP关键字指定正则表达式的字符匹配模式。下表列出了REGEXP操作符中常用字符匹配列表。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BrPgcqQM-1640752866008)(D:/BaiduNetdiskDownload/01_课件/images/image-20210926151249943.png)]

1. 查询以特定字符或字符串开头的记录
字符‘^’匹配以特定字符或者字符串开头的文本。

在fruits表中,查询f_name字段以字母‘b’开头的记录,SQL语句如下:

mysql> SELECT * FROM fruits WHERE f_name REGEXP '^b';

2. 查询以特定字符或字符串结尾的记录
字符‘$’匹配以特定字符或者字符串结尾的文本。

在fruits表中,查询f_name字段以字母‘y’结尾的记录,SQL语句如下:

mysql> SELECT * FROM fruits WHERE f_name REGEXP 'y$';

3. 用符号"."来替代字符串中的任意一个字符
字符‘.’匹配任意一个字符。
在fruits表中,查询f_name字段值包含字母‘a’与‘g’且两个字母之间只有一个字母的记录,SQL语句如下:

mysql> SELECT * FROM fruits WHERE f_name REGEXP 'a.g';

4. 使用"*“和”+"来匹配多个字符
星号‘*’匹配前面的字符任意多次,包括0次。加号‘+’匹配前面的字符至少一次。

在fruits表中,查询f_name字段值以字母‘b’开头且‘b’后面出现字母‘a’的记录,SQL语句如下:

mysql> SELECT * FROM fruits WHERE f_name REGEXP '^ba*';

在fruits表中,查询f_name字段值以字母‘b’开头且‘b’后面出现字母‘a’至少一次的记录,SQL语句如下:

mysql> SELECT * FROM fruits WHERE f_name REGEXP '^ba+';

5. 匹配指定字符串
正则表达式可以匹配指定字符串,只要这个字符串在查询文本中即可,如要匹配多个字符串,多个字符串之间使用分隔符‘|’隔开。

在fruits表中,查询f_name字段值包含字符串“on”的记录,SQL语句如下:

mysql> SELECT * FROM fruits WHERE f_name REGEXP 'on';

在fruits表中,查询f_name字段值包含字符串“on”或者“ap”的记录,SQL语句如下:

mysql> SELECT * FROM fruits WHERE f_name REGEXP 'on|ap';

之前介绍过,LIKE运算符也可以匹配指定的字符串,但与REGEXP不同,LIKE匹配的字符串如果在文本中间出现,则找不到它,相应的行也不会返回。REGEXP在文本内进行匹配,如果被匹配的字符串在文本中出现,REGEXP将会找到它,相应的行也会被返回。对比结果如下所示。

在fruits表中,使用LIKE运算符查询f_name字段值为“on”的记录,SQL语句如下:

mysql> SELECT * FROM fruits WHERE f_name like 'on';Empty set(0.00 sec)

6. 匹配指定字符中的任意一个
方括号“[]”指定一个字符集合,只匹配其中任何一个字符,即为所查找的文本。

在fruits表中,查找f_name字段中包含字母‘o’或者‘t’的记录,SQL语句如下:

mysql> SELECT * FROM fruits WHERE f_name REGEXP '[ot]';

在fruits表中,查询s_id字段中包含4、5或者6的记录,SQL语句如下:

mysql> SELECT * FROM fruits WHERE s_id REGEXP '[456]';

7. 匹配指定字符以外的字符
“[^字符集合]”匹配不在指定集合中的任何字符。

在fruits表中,查询f_id字段中包含字母ae和数字12以外字符的记录,SQL语句如下:

mysql> SELECT * FROM fruits WHERE f_id REGEXP '[^a-e1-2]';

8. 使用{n,}或者{n,m}来指定字符串连续出现的次数
“字符串{n,}”表示至少匹配n次前面的字符;“字符串{n,m}”表示匹配前面的字符串不少于n次,不多于m次。例如,a{2,}表示字母a连续出现至少2次,也可以大于2次;a{2,4}表示字母a连续出现最少2次,最多不能超过4次。

在fruits表中,查询f_name字段值出现字母‘x’至少2次的记录,SQL语句如下:

mysql> SELECT * FROM fruits WHERE f_name REGEXP 'x{2,}';

在fruits表中,查询f_name字段值出现字符串“ba”最少1次、最多3次的记录,SQL语句如下:

mysql> SELECT * FROM fruits WHERE f_name REGEXP 'ba{1,3}';

第05章_排序与分页

讲师:尚硅谷-宋红康(江湖人称:康师傅)

官网:http://www.atguigu.com


1. 排序数据

1.1 排序规则

  • 使用 ORDER BY 子句排序
    • ASC(ascend): 升序
    • DESC(descend):降序
  • ORDER BY 子句在SELECT语句的结尾。

1.2 单列排序

SELECT   last_name, job_id, department_id, hire_date
FROM     employees
ORDER BY hire_date ;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7r3p6F3f-1640752866009)(D:\BaiduNetdiskDownload\01_课件\images\1554974255957.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-99AAjI8r-1640752866009)(D:\BaiduNetdiskDownload\01_课件\images\1554974260133.png)]

SELECT   last_name, job_id, department_id, hire_date
FROM     employees
ORDER BY hire_date DESC ;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AoIYOV5T-1640752866010)(D:\BaiduNetdiskDownload\01_课件\images\1554974822229.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XO93eeEY-1640752866010)(D:\BaiduNetdiskDownload\01_课件\images\1554974827522.png)]

SELECT employee_id, last_name, salary*12 annsal
FROM   employees
ORDER BY annsal;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I6WjBUFs-1640752866011)(D:\BaiduNetdiskDownload\01_课件\images\1554974853194.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Zv5chke-1640752866011)(D:\BaiduNetdiskDownload\01_课件\images\1554974858252.png)]

1.3 多列排序

SELECT last_name, department_id, salary
FROM   employees
ORDER BY department_id, salary DESC;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jtTJEn33-1640752866012)(D:\BaiduNetdiskDownload\01_课件\images\1554974901572.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3DMRq7Tv-1640752866012)(D:\BaiduNetdiskDownload\01_课件\images\1554974907498.png)]

  • 可以使用不在SELECT列表中的列排序。
  • 在对多列进行排序的时候,首先排序的第一列必须有相同的列值,才会对第二列进行排序。如果第一列数据中所有值都是唯一的,将不再对第二列进行排序。

2. 分页

2.1 背景

背景1:查询返回的记录太多了,查看起来很不方便,怎么样能够实现分页查询呢?

背景2:表里有 4 条数据,我们只想要显示第 2、3 条数据怎么办呢?

2.2 实现规则

  • 分页原理

    所谓分页显示,就是将数据库中的结果集,一段一段显示出来需要的条件。

  • MySQL中使用 LIMIT 实现分页

  • 格式:

    LIMIT [位置偏移量,] 行数
    

    第一个“位置偏移量”参数指示MySQL从哪一行开始显示,是一个可选参数,如果不指定“位置偏移量”,将会从表中的第一条记录开始(第一条记录的位置偏移量是0,第二条记录的位置偏移量是1,以此类推);第二个参数“行数”指示返回的记录条数。

  • 举例

--前10条记录:
SELECT * FROM 表名 LIMIT 0,10;
或者
SELECT * FROM 表名 LIMIT 10;

--第11至20条记录:
SELECT * FROM 表名 LIMIT 10,10;

--第21至30条记录: 
SELECT * FROM 表名 LIMIT 20,10;

MySQL 8.0中可以使用“LIMIT 3 OFFSET 4”,意思是获取从第5条记录开始后面的3条记录,和“LIMIT 4,3;”返回的结果相同。

  • 分页显式公式**:(当前页数-1)每页条数,每页条数*
SELECT * FROM table 
LIMIT(PageNo - 1)*PageSize,PageSize;
  • 注意:LIMIT 子句必须放在整个SELECT语句的最后!
  • 使用 LIMIT 的好处

约束返回结果的数量可以减少数据表的网络传输量,也可以提升查询效率。如果我们知道返回结果只有 1 条,就可以使用LIMIT 1,告诉 SELECT 语句只需要返回一条记录即可。这样的好处就是 SELECT 不需要扫描完整的表,只需要检索到一条符合条件的记录即可返回。

2.3 拓展

在不同的 DBMS 中使用的关键字可能不同。在 MySQL、PostgreSQL、MariaDB 和 SQLite 中使用 LIMIT 关键字,而且需要放到 SELECT 语句的最后面。

  • 如果是 SQL Server 和 Access,需要使用 TOP 关键字,比如:
SELECT TOP 5 name, hp_max FROM heros ORDER BY hp_max DESC
  • 如果是 DB2,使用FETCH FIRST 5 ROWS ONLY这样的关键字:
SELECT name, hp_max FROM heros ORDER BY hp_max DESC FETCH FIRST 5 ROWS ONLY
  • 如果是 Oracle,你需要基于 ROWNUM 来统计行数:
SELECT rownum,last_name,salary FROM employees WHERE rownum < 5 ORDER BY salary DESC;

需要说明的是,这条语句是先取出来前 5 条数据行,然后再按照 hp_max 从高到低的顺序进行排序。但这样产生的结果和上述方法的并不一样。我会在后面讲到子查询,你可以使用

SELECT rownum, last_name,salaryFROM (    SELECT last_name,salary    FROM employees    ORDER BY salary DESC)WHERE rownum < 10;

得到与上述方法一致的结果。

第06章_多表查询

讲师:尚硅谷-宋红康(江湖人称:康师傅)

官网:http://www.atguigu.com


多表查询,也称为关联查询,指两个或更多个表一起完成查询操作。

前提条件:这些一起查询的表之间是有关系的(一对一、一对多),它们之间一定是有关联字段,这个关联字段可能建立了外键,也可能没有建立外键。比如:员工表和部门表,这两个表依靠“部门编号”进行关联。

1. 一个案例引发的多表连接

1.1 案例说明

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v9Y96Udj-1640752866013)(D:/BaiduNetdiskDownload/01_课件/images/1554974984600.png)]

从多个表中获取数据:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sz7PI8iA-1640752866013)(D:/BaiduNetdiskDownload/01_课件/images/1554975020388.png)]

#案例:查询员工的姓名及其部门名称
SELECT last_name, department_name
FROM employees, departments;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HRVYSbsY-1640752866014)(D:/BaiduNetdiskDownload/01_课件/images/1554975097631.png)]

查询结果:

+-----------+----------------------+
| last_name | department_name      |
+-----------+----------------------+
| King      | Administration       |
| King      | Marketing            |
| King      | Purchasing           |
| King      | Human Resources      |
| King      | Shipping             |
| King      | IT                   |
| King      | Public Relations     |
| King      | Sales                |
| King      | Executive            |
| King      | Finance              |
| King      | Accounting           |
| King      | Treasury             |
...
| Gietz     | IT Support           |
| Gietz     | NOC                  |
| Gietz     | IT Helpdesk          |
| Gietz     | Government Sales     |
| Gietz     | Retail Sales         |
| Gietz     | Recruiting           |
| Gietz     | Payroll              |
+-----------+----------------------+
2889 rows in set (0.01 sec)

分析错误情况:

SELECT COUNT(employee_id) FROM employees;
#输出107行

SELECT COUNT(department_id)FROM departments;
#输出27行

SELECT 107*27 FROM dual;

我们把上述多表查询中出现的问题称为:笛卡尔积的错误。

1.2 笛卡尔积(或交叉连接)的理解

笛卡尔乘积是一个数学运算。假设我有两个集合 X 和 Y,那么 X 和 Y 的笛卡尔积就是 X 和 Y 的所有可能组合,也就是第一个对象来自于 X,第二个对象来自于 Y 的所有可能。组合的个数即为两个集合中元素个数的乘积数。

img

SQL92中,笛卡尔积也称为交叉连接,英文是 CROSS JOIN。在 SQL99 中也是使用 CROSS JOIN表示交叉连接。它的作用就是可以把任意表进行连接,即使这两张表不相关。在MySQL中如下情况会出现笛卡尔积:

#查询员工姓名和所在部门名称
SELECT last_name,department_name FROM employees,departments;
SELECT last_name,department_name FROM employees CROSS JOIN departments;
SELECT last_name,department_name FROM employees INNER JOIN departments;
SELECT last_name,department_name FROM employees JOIN departments;

1.3 案例分析与问题解决

  • 笛卡尔积的错误会在下面条件下产生

    • 省略多个表的连接条件(或关联条件)
    • 连接条件(或关联条件)无效
    • 所有表中的所有行互相连接
  • 为了避免笛卡尔积, 可以在 WHERE 加入有效的连接条件。

  • 加入连接条件后,查询语法:

    SELECT	table1.column, table2.column
    FROM	table1, table2
    WHERE	table1.column1 = table2.column2;  #连接条件
    
    • 在 WHERE子句中写入连接条件。
  • 正确写法:

    #案例:查询员工的姓名及其部门名称
    SELECT last_name, department_name
    FROM employees, departments
    WHERE employees.department_id = departments.department_id;
    
  • 在表中有相同列时,在列名之前加上表名前缀。

2. 多表查询分类讲解

分类1:等值连接 vs 非等值连接

等值连接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NMOiZQIi-1640752866014)(D:/BaiduNetdiskDownload/01_课件/images/1554975496900.png)]

SELECT employees.employee_id, employees.last_name, 
       employees.department_id, departments.department_id,
       departments.location_id
FROM   employees, departments
WHERE  employees.department_id = departments.department_id;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qtXzoAZ1-1640752866015)(D:/BaiduNetdiskDownload/01_课件/images/1554975522600.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aEgMYd40-1640752866015)(D:/BaiduNetdiskDownload/01_课件/images/1554975526339.png)]

拓展1:多个连接条件与 AND 操作符

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EhX6ecX4-1640752866015)(D:/BaiduNetdiskDownload/01_课件/images/1554975606231.png)]

拓展2:区分重复的列名

  • 多个表中有相同列时,必须在列名之前加上表名前缀。
  • 在不同表中具有相同列名的列可以用表名加以区分。
SELECT employees.last_name, departments.department_name,employees.department_id
FROM employees, departments
WHERE employees.department_id = departments.department_id;

拓展3:表的别名

  • 使用别名可以简化查询。

  • 列名前使用表名前缀可以提高查询效率。

SELECT e.employee_id, e.last_name, e.department_id,       d.department_id, d.location_idFROM   employees e , departments dWHERE  e.department_id = d.department_id;

需要注意的是,如果我们使用了表的别名,在查询字段中、过滤条件中就只能使用别名进行代替,不能使用原有的表名,否则就会报错。

阿里开发规范

强制】对于数据库中表记录的查询和变更,只要涉及多个表,都需要在列名前加表的别名(或 表名)进行限定。

说明:对多表进行查询记录、更新记录、删除记录时,如果对操作列没有限定表的别名(或表名),并且操作列在多个表中存在时,就会抛异常。

正例:select t1.name from table_first as t1 , table_second as t2 where t1.id=t2.id;

反例:在某业务中,由于多表关联查询语句没有加表的别名(或表名)的限制,正常运行两年后,最近在 某个表中增加一个同名字段,在预发布环境做数据库变更后,线上查询语句出现出 1052 异常:Column ‘name’ in field list is ambiguous。

拓展4:连接多个表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GLKaMtrC-1640752866016)(D:/BaiduNetdiskDownload/01_课件/images/1554978354431.png)]

**总结:连接 n个表,至少需要n-1个连接条件。**比如,连接三个表,至少需要两个连接条件。

练习:查询出公司员工的 last_name,department_name, city

非等值连接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w4AWpY2U-1640752866016)(D:/BaiduNetdiskDownload/01_课件/images/1554978442447.png)]

SELECT e.last_name, e.salary, j.grade_levelFROM   employees e, job_grades jWHERE  e.salary BETWEEN j.lowest_sal AND j.highest_sal;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0K6mWPa9-1640752866017)(D:/BaiduNetdiskDownload/01_课件/images/1554978477013.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Ta41i4i-1640752866017)(D:/BaiduNetdiskDownload/01_课件/images/1554978482652.png)]

分类2:自连接 vs 非自连接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aFJlRsbp-1640752866017)(D:/BaiduNetdiskDownload/01_课件/images/1554978514321.png)]

  • 当table1和table2本质上是同一张表,只是用取别名的方式虚拟成两张表以代表不同的意义。然后两个表再进行内连接,外连接等查询。

题目:查询employees表,返回“Xxx works for Xxx”

SELECT CONCAT(worker.last_name ,' works for '        , manager.last_name)FROM   employees worker, employees managerWHERE  worker.manager_id = manager.employee_id ;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7VOuEtcI-1640752866018)(D:/BaiduNetdiskDownload/01_课件/images/1554978684947.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GZKxnnGI-1640752866018)(D:/BaiduNetdiskDownload/01_课件/images/1554978690764.png)]

练习:查询出last_name为 ‘Chen’ 的员工的 manager 的信息。

分类3:内连接 vs 外连接

除了查询满足条件的记录以外,外连接还可以查询某一方不满足条件的记录。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lJRs6t9A-1640752866019)(D:/BaiduNetdiskDownload/01_课件/images/1554978955659.png)]

  • 内连接: 合并具有同一列的两个以上的表的行, 结果集中不包含一个表与另一个表不匹配的行

  • 外连接: 两个表在连接过程中除了返回满足连接条件的行以外还返回左(或右)表中不满足条件的行 ,这种连接称为左(或右) 外连接。没有匹配的行时, 结果表中相应的列为空(NULL)。

  • 如果是左外连接,则连接条件中左边的表也称为主表,右边的表称为从表

    如果是右外连接,则连接条件中右边的表也称为主表,左边的表称为从表

SQL92:使用(+)创建连接
  • 在 SQL92 中采用(+)代表从表所在的位置。即左或右外连接中,(+) 表示哪个是从表。

  • Oracle 对 SQL92 支持较好,而 MySQL 则不支持 SQL92 的外连接。

    #左外连接SELECT last_name,department_nameFROM employees ,departmentsWHERE employees.department_id = departments.department_id(+);#右外连接SELECT last_name,department_nameFROM employees ,departmentsWHERE employees.department_id(+) = departments.department_id;
    
  • 而且在 SQL92 中,只有左外连接和右外连接,没有满(或全)外连接。

3. SQL99语法实现多表查询

3.1 基本语法

  • 使用JOIN…ON子句创建连接的语法结构:

    SELECT table1.column, table2.column,table3.columnFROM table1    JOIN table2 ON table1 和 table2 的连接条件        JOIN table3 ON table2 和 table3 的连接条件
    

    它的嵌套逻辑类似我们使用的 FOR 循环:

    for t1 in table1:    for t2 in table2:       if condition1:           for t3 in table3:              if condition2:                  output t1 + t2 + t3
    

    SQL99 采用的这种嵌套结构非常清爽、层次性更强、可读性更强,即使再多的表进行连接也都清晰可见。如果你采用 SQL92,可读性就会大打折扣。

  • 语法说明:

    • 可以使用 ON 子句指定额外的连接条件
    • 这个连接条件是与其它条件分开的。
    • ON 子句使语句具有更高的易读性
    • 关键字 JOIN、INNER JOIN、CROSS JOIN 的含义是一样的,都表示内连接

3.2 内连接(INNER JOIN)的实现

  • 语法:
SELECT 字段列表FROM A表 INNER JOIN B表ON 关联条件WHERE 等其他子句;

题目1:

SELECT e.employee_id, e.last_name, e.department_id,        d.department_id, d.location_idFROM   employees e JOIN departments dON     (e.department_id = d.department_id);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-osBJV3ci-1640752866019)(D:/BaiduNetdiskDownload/01_课件/images/1554979073996.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1NTl0akV-1640752866020)(D:/BaiduNetdiskDownload/01_课件/images/1554979079395.png)]

题目2:

SELECT employee_id, city, department_nameFROM   employees e JOIN   departments dON     d.department_id = e.department_id JOIN   locations lON     d.location_id = l.location_id;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7EZw9hZI-1640752866021)(D:/BaiduNetdiskDownload/01_课件/images/1554979110008.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dFxKgM7W-1640752866021)(D:/BaiduNetdiskDownload/01_课件/images/1554979115642.png)]

3.3 外连接(OUTER JOIN)的实现

3.3.1 左外连接(LEFT OUTER JOIN)
  • 语法:
#实现查询结果是ASELECT 字段列表FROM A表 LEFT JOIN B表ON 关联条件WHERE 等其他子句;
  • 举例:
SELECT e.last_name, e.department_id, d.department_nameFROM   employees eLEFT OUTER JOIN departments dON   (e.department_id = d.department_id) ;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HoBnope9-1640752866022)(D:/BaiduNetdiskDownload/01_课件/images/1554979200961.png)]

3.3.2 右外连接(RIGHT OUTER JOIN)
  • 语法:
#实现查询结果是BSELECT 字段列表FROM A表 RIGHT JOIN B表ON 关联条件WHERE 等其他子句;
  • 举例:
SELECT e.last_name, e.department_id, d.department_nameFROM   employees eRIGHT OUTER JOIN departments dON    (e.department_id = d.department_id) ;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JA3TSBuL-1640752866022)(D:/BaiduNetdiskDownload/01_课件/images/1554979243194.png)]

需要注意的是,LEFT JOIN 和 RIGHT JOIN 只存在于 SQL99 及以后的标准中,在 SQL92 中不存在,只能用 (+) 表示。

3.3.3 满外连接(FULL OUTER JOIN)
  • 满外连接的结果 = 左右表匹配的数据 + 左表没有匹配到的数据 + 右表没有匹配到的数据。
  • SQL99是支持满外连接的。使用FULL JOIN 或 FULL OUTER JOIN来实现。
  • 需要注意的是,MySQL不支持FULL JOIN,但是可以用 LEFT JOIN UNION RIGHT join代替。

4. UNION的使用

合并查询结果
利用UNION关键字,可以给出多条SELECT语句,并将它们的结果组合成单个结果集。合并时,两个表对应的列数和数据类型必须相同,并且相互对应。各个SELECT语句之间使用UNION或UNION ALL关键字分隔。

语法格式:

SELECT column,... FROM table1UNION [ALL]SELECT column,... FROM table2

UNION操作符

1554979317187

UNION 操作符返回两个查询的结果集的并集,去除重复记录。

UNION ALL操作符

1554979343634

UNION ALL操作符返回两个查询的结果集的并集。对于两个结果集的重复部分,不去重。

注意:执行UNION ALL语句时所需要的资源比UNION语句少。如果明确知道合并数据后的结果数据不存在重复数据,或者不需要去除重复的数据,则尽量使用UNION ALL语句,以提高数据查询的效率。

举例:查询部门编号>90或邮箱包含a的员工信息

#方式1SELECT * FROM employees WHERE email LIKE '%a%' OR department_id>90;
#方式2SELECT * FROM employees  WHERE email LIKE '%a%'UNIONSELECT * FROM employees  WHERE department_id>90;

举例:查询中国用户中男性的信息以及美国用户中年男性的用户信息

SELECT id,cname FROM t_chinamale WHERE csex='男'UNION ALLSELECT id,tname FROM t_usmale WHERE tGender='male';

5. 7种SQL JOINS的实现

1554979255233

5.7.1 代码实现

#中图:内连接 A∩BSELECT employee_id,last_name,department_nameFROM employees e JOIN departments dON e.`department_id` = d.`department_id`;
#左上图:左外连接SELECT employee_id,last_name,department_nameFROM employees e LEFT JOIN departments dON e.`department_id` = d.`department_id`;
#右上图:右外连接SELECT employee_id,last_name,department_nameFROM employees e RIGHT JOIN departments dON e.`department_id` = d.`department_id`;
#左中图:A - A∩BSELECT employee_id,last_name,department_nameFROM employees e LEFT JOIN departments dON e.`department_id` = d.`department_id`WHERE d.`department_id` IS NULL
#右中图:B-A∩BSELECT employee_id,last_name,department_nameFROM employees e RIGHT JOIN departments dON e.`department_id` = d.`department_id`WHERE e.`department_id` IS NULL
#左下图:满外连接# 左中图 + 右上图  A∪BSELECT employee_id,last_name,department_nameFROM employees e LEFT JOIN departments dON e.`department_id` = d.`department_id`WHERE d.`department_id` IS NULLUNION ALL  #没有去重操作,效率高SELECT employee_id,last_name,department_nameFROM employees e RIGHT JOIN departments dON e.`department_id` = d.`department_id`;
#右下图#左中图 + 右中图  A ∪B- A∩B 或者 (A -  A∩B) ∪ (B - A∩B)SELECT employee_id,last_name,department_nameFROM employees e LEFT JOIN departments dON e.`department_id` = d.`department_id`WHERE d.`department_id` IS NULLUNION ALLSELECT employee_id,last_name,department_nameFROM employees e RIGHT JOIN departments dON e.`department_id` = d.`department_id`WHERE e.`department_id` IS NULL

5.7.2 语法格式小结

  • 左中图
#实现A -  A∩Bselect 字段列表from A表 left join B表on 关联条件where 从表关联字段 is null and 等其他子句;
  • 右中图
#实现B -  A∩Bselect 字段列表from A表 right join B表on 关联条件where 从表关联字段 is null and 等其他子句;
  • 左下图
#实现查询结果是A∪B#用左外的A,union 右外的Bselect 字段列表from A表 left join B表on 关联条件where 等其他子句union select 字段列表from A表 right join B表on 关联条件where 等其他子句;
  • 右下图
#实现A∪B -  A∩B  或   (A -  A∩B) ∪ (B - A∩B)#使用左外的 (A -  A∩B)  union 右外的(B - A∩B)select 字段列表from A表 left join B表on 关联条件where 从表关联字段 is null and 等其他子句unionselect 字段列表from A表 right join B表on 关联条件where 从表关联字段 is null and 等其他子句

6. SQL99语法新特性

6.1 自然连接

SQL99 在 SQL92 的基础上提供了一些特殊语法,比如 NATURAL JOIN 用来表示自然连接。我们可以把自然连接理解为 SQL92 中的等值连接。它会帮你自动查询两张连接表中所有相同的字段,然后进行等值连接

在SQL92标准中:

SELECT employee_id,last_name,department_nameFROM employees e JOIN departments dON e.`department_id` = d.`department_id`AND e.`manager_id` = d.`manager_id`;

在 SQL99 中你可以写成:

SELECT employee_id,last_name,department_nameFROM employees e NATURAL JOIN departments d;

6.2 USING连接

当我们进行连接的时候,SQL99还支持使用 USING 指定数据表里的同名字段进行等值连接。但是只能配合JOIN一起使用。比如:

SELECT employee_id,last_name,department_nameFROM employees e JOIN departments dUSING (department_id);

你能看出与自然连接 NATURAL JOIN 不同的是,USING 指定了具体的相同的字段名称,你需要在 USING 的括号 () 中填入要指定的同名字段。同时使用 JOIN...USING 可以简化 JOIN ON 的等值连接。它与下面的 SQL 查询结果是相同的:

SELECT employee_id,last_name,department_nameFROM employees e ,departments dWHERE e.department_id = d.department_id;

7. 章节小结

表连接的约束条件可以有三种方式:WHERE, ON, USING

  • WHERE:适用于所有关联查询

  • ON:只能和JOIN一起使用,只能写关联条件。虽然关联条件可以并到WHERE中和其他条件一起写,但分开写可读性更好。

  • USING:只能和JOIN一起使用,而且要求两个关联字段在关联表中名称一致,而且只能表示关联字段值相等

#关联条件#把关联条件写在where后面SELECT last_name,department_name FROM employees,departments WHERE employees.department_id = departments.department_id;#把关联条件写在on后面,只能和JOIN一起使用SELECT last_name,department_name FROM employees INNER JOIN departments ON employees.department_id = departments.department_id;SELECT last_name,department_name FROM employees CROSS JOIN departments ON employees.department_id = departments.department_id;SELECT last_name,department_name  FROM employees JOIN departments ON employees.department_id = departments.department_id;#把关联字段写在using()中,只能和JOIN一起使用#而且两个表中的关联字段必须名称相同,而且只能表示=#查询员工姓名与基本工资SELECT last_name,job_titleFROM employees INNER JOIN jobs USING(job_id);#n张表关联,需要n-1个关联条件#查询员工姓名,基本工资,部门名称SELECT last_name,job_title,department_name FROM employees,departments,jobs WHERE employees.department_id = departments.department_id AND employees.job_id = jobs.job_id;SELECT last_name,job_title,department_name FROM employees INNER JOIN departments INNER JOIN jobs ON employees.department_id = departments.department_id AND employees.job_id = jobs.job_id;

注意:

我们要控制连接表的数量。多表连接就相当于嵌套 for 循环一样,非常消耗资源,会让 SQL 查询性能下降得很严重,因此不要连接不必要的表。在许多 DBMS 中,也都会有最大连接表的限制。

【强制】超过三个表禁止 join。需要 join 的字段,数据类型保持绝对一致;多表关联查询时, 保证被关联的字段需要有索引。

说明:即使双表 join 也要注意表索引、SQL 性能。

来源:阿里巴巴《Java开发手册》

附录:常用的 SQL 标准有哪些

在正式开始讲连接表的种类时,我们首先需要知道 SQL 存在不同版本的标准规范,因为不同规范下的表连接操作是有区别的。

SQL 有两个主要的标准,分别是 SQL92SQL99。92 和 99 代表了标准提出的时间,SQL92 就是 92 年提出的标准规范。当然除了 SQL92 和 SQL99 以外,还存在 SQL-86、SQL-89、SQL:2003、SQL:2008、SQL:2011 和 SQL:2016 等其他的标准。

这么多标准,到底该学习哪个呢?实际上最重要的 SQL 标准就是 SQL92 和 SQL99。一般来说 SQL92 的形式更简单,但是写的 SQL 语句会比较长,可读性较差。而 SQL99 相比于 SQL92 来说,语法更加复杂,但可读性更强。我们从这两个标准发布的页数也能看出,SQL92 的标准有 500 页,而 SQL99 标准超过了 1000 页。实际上从 SQL99 之后,很少有人能掌握所有内容,因为确实太多了。就好比我们使用 Windows、Linux 和 Office 的时候,很少有人能掌握全部内容一样。我们只需要掌握一些核心的功能,满足日常工作的需求即可。

**SQL92 和 SQL99 是经典的 SQL 标准,也分别叫做 SQL-2 和 SQL-3 标准。**也正是在这两个标准发布之后,SQL 影响力越来越大,甚至超越了数据库领域。现如今 SQL 已经不仅仅是数据库领域的主流语言,还是信息领域中信息处理的主流语言。在图形检索、图像检索以及语音检索中都能看到 SQL 语言的使用。

第07章_单行函数

讲师:尚硅谷-宋红康(江湖人称:康师傅)

官网:http://www.atguigu.com


1. 函数的理解

1.1 什么是函数

函数在计算机语言的使用中贯穿始终,函数的作用是什么呢?它可以把我们经常使用的代码封装起来,需要的时候直接调用即可。这样既提高了代码效率,又提高了可维护性。在 SQL 中我们也可以使用函数对检索出来的数据进行函数操作。使用这些函数,可以极大地提高用户对数据库的管理效率

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OjdVtsrL-1640752866023)(D:\BaiduNetdiskDownload\01_课件\images\1554979529525.png)]

从函数定义的角度出发,我们可以将函数分成内置函数自定义函数。在 SQL 语言中,同样也包括了内置函数和自定义函数。内置函数是系统内置的通用函数,而自定义函数是我们根据自己的需要编写的,本章及下一章讲解的是 SQL 的内置函数。

1.2 不同DBMS函数的差异

我们在使用 SQL 语言的时候,不是直接和这门语言打交道,而是通过它使用不同的数据库软件,即 DBMS。DBMS 之间的差异性很大,远大于同一个语言不同版本之间的差异。实际上,只有很少的函数是被 DBMS 同时支持的。比如,大多数 DBMS 使用(||)或者(+)来做拼接符,而在 MySQL 中的字符串拼接函数为concat()。大部分 DBMS 会有自己特定的函数,这就意味着采用 SQL 函数的代码可移植性是很差的,因此在使用函数的时候需要特别注意。

1.3 MySQL的内置函数及分类

MySQL提供了丰富的内置函数,这些函数使得数据的维护与管理更加方便,能够更好地提供数据的分析与统计功能,在一定程度上提高了开发人员进行数据分析与统计的效率。

MySQL提供的内置函数从实现的功能角度可以分为数值函数、字符串函数、日期和时间函数、流程控制函数、加密与解密函数、获取MySQL信息函数、聚合函数等。这里,我将这些丰富的内置函数再分为两类:单行函数聚合函数(或分组函数)

两种SQL函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uCSsw3Jf-1640752866023)(D:\BaiduNetdiskDownload\01_课件\images\1555433204337.png)]

单行函数

  • 操作数据对象
  • 接受参数返回一个结果
  • 只对一行进行变换
  • 每行返回一个结果
  • 可以嵌套
  • 参数可以是一列或一个值

2. 数值函数

2.1 基本函数

函数用法
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),
FLOOR(-43.23),MOD(12,5)
FROM DUAL;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DAIH6UGU-1640752866024)(D:\BaiduNetdiskDownload\01_课件\images\image-20211025162304844.png)]

SELECT RAND(),RAND(),RAND(10),RAND(10),RAND(-1),RAND(-1)
FROM DUAL;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-getRPQQy-1640752866024)(D:\BaiduNetdiskDownload\01_课件\images\image-20211025162538958.png)]

SELECT ROUND(12.33),ROUND(12.343,2),ROUND(12.324,-1),TRUNCATE(12.66,1),TRUNCATE(12.66,-1)
FROM DUAL;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q2B0arFE-1640752866025)(D:\BaiduNetdiskDownload\01_课件\images\image-20211025162730421.png)]

2.2 角度与弧度互换函数

函数用法
RADIANS(x)将角度转化为弧度,其中,参数x为角度值
DEGREES(x)将弧度转化为角度,其中,参数x为弧度值
SELECT RADIANS(30),RADIANS(60),RADIANS(90),DEGREES(2*PI()),DEGREES(RADIANS(90))
FROM DUAL;

2.3 三角函数

函数用法
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为弧度值

举例:

ATAN2(M,N)函数返回两个参数的反正切值。
与ATAN(X)函数相比,ATAN2(M,N)需要两个参数,例如有两个点point(x1,y1)和point(x2,y2),使用ATAN(X)函数计算反正切值为ATAN((y2-y1)/(x2-x1)),使用ATAN2(M,N)计算反正切值则为ATAN2(y2-y1,x2-x1)。由使用方式可以看出,当x2-x1等于0时,ATAN(X)函数会报错,而ATAN2(M,N)函数则仍然可以计算。

ATAN2(M,N)函数的使用示例如下:

SELECT SIN(RADIANS(30)),DEGREES(ASIN(1)),TAN(RADIANS(45)),DEGREES(ATAN(1)),DEGREES(ATAN2(1,1))
FROM DUAL;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XBjteuOm-1640752866025)(D:\BaiduNetdiskDownload\01_课件\images\image-20211025163846974.png)]

2.4 指数与对数

函数用法
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
mysql> SELECT POW(2,5),POWER(2,4),EXP(2),LN(10),LOG10(10),LOG2(4)
    -> FROM DUAL;
+----------+------------+------------------+-------------------+-----------+---------+
| POW(2,5) | POWER(2,4) | EXP(2)           | LN(10)            | LOG10(10) | LOG2(4) |
+----------+------------+------------------+-------------------+-----------+---------+
|       32 |         16 | 7.38905609893065 | 2.302585092994046 |         1 |       2 |
+----------+------------+------------------+-------------------+-----------+---------+
1 row in set (0.00 sec)

2.5 进制间的转换

函数用法
BIN(x)返回x的二进制编码
HEX(x)返回x的十六进制编码
OCT(x)返回x的八进制编码
CONV(x,f1,f2)返回f1进制数变成f2进制数
mysql> SELECT BIN(10),HEX(10),OCT(10),CONV(10,2,8)
    -> FROM DUAL;
+---------+---------+---------+--------------+
| BIN(10) | HEX(10) | OCT(10) | CONV(10,2,8) |
+---------+---------+---------+--------------+
| 1010    | A       | 12      | 2            |
+---------+---------+---------+--------------+
1 row in set (0.00 sec)

3. 字符串函数

函数用法
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
INSERT(str, idx, len, replacestr)将字符串str从第idx位置开始,len个字符长的子串替换为字符串replacestr
REPLACE(str, a, b)用字符串b替换字符串str中所有出现的字符串a
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 s1 FROM s)去掉字符串s开始处的s1
TRIM(TRAILING s1 FROM s)去掉字符串s结尾处的s1
REPEAT(str, n)返回str重复n次的结果
SPACE(n)返回n个空格
STRCMP(s1,s2)比较字符串s1,s2的ASCII码值的大小
SUBSTR(s,index,len)返回从字符串s的index位置其len个字符,作用与SUBSTRING(s,n,len)、MID(s,n,len)相同
LOCATE(substr,str)返回字符串substr在字符串str中首次出现的位置,作用于POSITION(substr IN 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

注意:MySQL中,字符串的位置是从1开始的。

举例:

mysql> SELECT FIELD('mm','hello','msm','amma'),FIND_IN_SET('mm','hello,mm,amma')
    -> FROM DUAL;
+----------------------------------+-----------------------------------+
| FIELD('mm','hello','msm','amma') | FIND_IN_SET('mm','hello,mm,amma') |
+----------------------------------+-----------------------------------+
|                                0 |                                 2 |
+----------------------------------+-----------------------------------+
1 row in set (0.00 sec)
mysql> SELECT NULLIF('mysql','mysql'),NULLIF('mysql', '');+-------------------------+---------------------+| NULLIF('mysql','mysql') | NULLIF('mysql', '') |+-------------------------+---------------------+| NULL                    | mysql               |+-------------------------+---------------------+1 row in set (0.00 sec)

4. 日期和时间函数

4.1 获取日期、时间

函数用法
CURDATE() ,CURRENT_DATE()返回当前日期,只包含年、月、日
CURTIME() , CURRENT_TIME()返回当前时间,只包含时、分、秒
NOW() / SYSDATE() / CURRENT_TIMESTAMP() / LOCALTIME() / LOCALTIMESTAMP()返回当前系统日期和时间
UTC_DATE()返回UTC(世界标准时间)日期
UTC_TIME()返回UTC(世界标准时间)时间

举例:

SELECT CURDATE(),CURTIME(),NOW(),SYSDATE()+0,UTC_DATE(),UTC_DATE()+0,UTC_TIME(),UTC_TIME()+0FROM DUAL;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v6vrg9Jm-1640752866026)(D:\BaiduNetdiskDownload\01_课件\images\image-20211025193742633.png)]

4.2 日期与时间戳的转换

函数用法
UNIX_TIMESTAMP()以UNIX时间戳的形式返回当前时间。SELECT UNIX_TIMESTAMP() ->1634348884
UNIX_TIMESTAMP(date)将时间date以UNIX时间戳的形式返回。
FROM_UNIXTIME(timestamp)将UNIX时间戳的时间转换为普通格式的时间

举例:

mysql> SELECT UNIX_TIMESTAMP(now());+-----------------------+| UNIX_TIMESTAMP(now()) |+-----------------------+|            1576380910 |+-----------------------+1 row in set (0.01 sec)mysql> SELECT UNIX_TIMESTAMP(CURDATE());+---------------------------+| UNIX_TIMESTAMP(CURDATE()) |+---------------------------+|                1576339200 |+---------------------------+1 row in set (0.00 sec)mysql> SELECT UNIX_TIMESTAMP(CURTIME());+---------------------------+| UNIX_TIMESTAMP(CURTIME()) |+---------------------------+|                1576380969 |+---------------------------+1 row in set (0.00 sec)mysql> SELECT UNIX_TIMESTAMP('2011-11-11 11:11:11')+---------------------------------------+| UNIX_TIMESTAMP('2011-11-11 11:11:11') |+---------------------------------------+|                            1320981071 |+---------------------------------------+1 row in set (0.00 sec)
mysql> SELECT FROM_UNIXTIME(1576380910);+---------------------------+| FROM_UNIXTIME(1576380910) |+---------------------------+| 2019-12-15 11:35:10       |+---------------------------+1 row in set (0.00 sec)

4.3 获取月份、星期、星期数、天数等函数

函数用法
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()),HOUR(CURTIME()),MINUTE(NOW()),SECOND(SYSDATE())FROM DUAL;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gK1vMA7F-1640752866026)(D:\BaiduNetdiskDownload\01_课件\images\image-20211025213504115.png)]

SELECT MONTHNAME('2021-10-26'),DAYNAME('2021-10-26'),WEEKDAY('2021-10-26'),QUARTER(CURDATE()),WEEK(CURDATE()),DAYOFYEAR(NOW()),DAYOFMONTH(NOW()),DAYOFWEEK(NOW())FROM DUAL;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hb7WUIBT-1640752866026)(D:\BaiduNetdiskDownload\01_课件\images\image-20211025214818623.png)]

4.4 日期的操作函数

函数用法
EXTRACT(type FROM date)返回指定日期中特定的部分,type指定返回的值

EXTRACT(type FROM date)函数中type的取值与含义:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BueOxwk5-1640752866027)(D:\BaiduNetdiskDownload\01_课件\images\image-20211012142639469.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iX04Ogs5-1640752866027)(D:\BaiduNetdiskDownload\01_课件\images\image-20211012142746444.png)]

SELECT EXTRACT(MINUTE FROM NOW()),EXTRACT( WEEK FROM NOW()),EXTRACT( QUARTER FROM NOW()),EXTRACT( MINUTE_SECOND FROM NOW())FROM DUAL;

4.5 时间和秒钟转换的函数

函数用法
TIME_TO_SEC(time)将 time 转化为秒并返回结果值。转化的公式为:小时*3600+分钟*60+秒
SEC_TO_TIME(seconds)将 seconds 描述转化为包含小时、分钟和秒的时间

举例:

mysql> SELECT TIME_TO_SEC(NOW());+--------------------+| TIME_TO_SEC(NOW()) |+--------------------+|               78774 |+--------------------+1 row in set (0.00 sec)
mysql> SELECT SEC_TO_TIME(78774);+--------------------+| SEC_TO_TIME(78774) |+--------------------+| 21:52:54            |+--------------------+1 row in set (0.12 sec)

4.6 计算日期和时间的函数

第1组:

函数用法
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时间间隔的日期

上述函数中type的取值:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WJ561u2G-1640752866027)(D:\BaiduNetdiskDownload\01_课件\images\image-20211012143203355.png)]

举例:

SELECT DATE_ADD(NOW(), INTERVAL 1 DAY) AS col1,DATE_ADD('2021-10-21 23:32:12',INTERVAL 1 SECOND) AS col2,ADDDATE('2021-10-21 23:32:12',INTERVAL 1 SECOND) AS col3,DATE_ADD('2021-10-21 23:32:12',INTERVAL '1_1' MINUTE_SECOND) AS col4,DATE_ADD(NOW(), INTERVAL -1 YEAR) AS col5, #可以是负数DATE_ADD(NOW(), INTERVAL '1_1' YEAR_MONTH) AS col6 #需要单引号FROM DUAL;
SELECT DATE_SUB('2021-01-21',INTERVAL 31 DAY) AS col1,SUBDATE('2021-01-21',INTERVAL 31 DAY) AS col2,DATE_SUB('2021-01-21 02:01:01',INTERVAL '1 1' DAY_HOUR) AS col3FROM DUAL;

第2组:

函数用法
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 ADDTIME(NOW(),20),SUBTIME(NOW(),30),SUBTIME(NOW(),'1:1:3'),DATEDIFF(NOW(),'2021-10-01'),TIMEDIFF(NOW(),'2021-10-25 22:10:10'),FROM_DAYS(366),TO_DAYS('0000-12-25'),LAST_DAY(NOW()),MAKEDATE(YEAR(NOW()),12),MAKETIME(10,21,23),PERIOD_ADD(20200101010101,10)FROM DUAL;
mysql> SELECT ADDTIME(NOW(), 50);+---------------------+| ADDTIME(NOW(), 50)  |+---------------------+| 2019-12-15 22:17:47 |+---------------------+1 row in set (0.00 sec)mysql> SELECT ADDTIME(NOW(), '1:1:1');+-------------------------+| ADDTIME(NOW(), '1:1:1') |+-------------------------+| 2019-12-15 23:18:46     |+-------------------------+1 row in set (0.00 sec)
mysql> SELECT SUBTIME(NOW(), '1:1:1');+-------------------------+| SUBTIME(NOW(), '1:1:1') |+-------------------------+| 2019-12-15 21:23:50     |+-------------------------+1 row in set (0.00 sec)mysql> SELECT SUBTIME(NOW(), '-1:-1:-1'); +----------------------------+| SUBTIME(NOW(), '-1:-1:-1') |+----------------------------+| 2019-12-15 22:25:11        |+----------------------------+1 row in set, 1 warning (0.00 sec)
mysql> SELECT FROM_DAYS(366);+----------------+| FROM_DAYS(366) |+----------------+| 0001-01-01     |+----------------+1 row in set (0.00 sec)
mysql> SELECT MAKEDATE(2020,1);+------------------+| MAKEDATE(2020,1) |+------------------+| 2020-01-01       |+------------------+1 row in set (0.00 sec)mysql> SELECT MAKEDATE(2020,32);+-------------------+| MAKEDATE(2020,32) |+-------------------+| 2020-02-01        |+-------------------+1 row in set (0.00 sec)
mysql> SELECT MAKETIME(1,1,1);+-----------------+| MAKETIME(1,1,1) |+-----------------+| 01:01:01        |+-----------------+1 row in set (0.00 sec)
mysql> SELECT PERIOD_ADD(20200101010101,1);+------------------------------+| PERIOD_ADD(20200101010101,1) |+------------------------------+|               20200101010102 |+------------------------------+1 row in set (0.00 sec)
mysql> SELECT TO_DAYS(NOW());+----------------+| TO_DAYS(NOW()) |+----------------+|          737773 |+----------------+1 row in set (0.00 sec)

举例:查询 7 天内的新增用户数有多少?

SELECT COUNT(*) as num FROM new_user WHERE TO_DAYS(NOW())-TO_DAYS(regist_time)<=7

4.7 日期的格式化与解析

函数用法
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参数常用的格式符:

格式符说明格式符说明
%Y4位数字表示年份%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为周中第一天
%T24小时制%r12小时制
%pAM或PM%%表示%

GET_FORMAT函数中date_type和format_type参数取值如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tdlKYG4T-1640752866028)(D:\BaiduNetdiskDownload\01_课件\images\image-20211012145231321.png)]

举例:

mysql> SELECT DATE_FORMAT(NOW(), '%H:%i:%s');+--------------------------------+| DATE_FORMAT(NOW(), '%H:%i:%s') |+--------------------------------+| 22:57:34                        |+--------------------------------+1 row in set (0.00 sec)
SELECT STR_TO_DATE('09/01/2009','%m/%d/%Y')FROM DUAL;SELECT STR_TO_DATE('20140422154706','%Y%m%d%H%i%s')FROM DUAL;SELECT STR_TO_DATE('2014-04-22 15:47:06','%Y-%m-%d %H:%i:%s')FROM DUAL;
mysql> SELECT GET_FORMAT(DATE, 'USA');+-------------------------+| GET_FORMAT(DATE, 'USA') |+-------------------------+| %m.%d.%Y                |+-------------------------+1 row in set (0.00 sec)SELECT DATE_FORMAT(NOW(),GET_FORMAT(DATE,'USA')),FROM DUAL;
mysql> SELECT STR_TO_DATE('2020-01-01 00:00:00','%Y-%m-%d'); +-----------------------------------------------+| STR_TO_DATE('2020-01-01 00:00:00','%Y-%m-%d') |+-----------------------------------------------+| 2020-01-01                                    |+-----------------------------------------------+1 row in set, 1 warning (0.00 sec)

5. 流程控制函数

流程处理函数可以根据不同的条件,执行不同的处理流程,可以在SQL语句中实现不同的条件选择。MySQL中的流程处理函数主要包括IF()、IFNULL()和CASE()函数。

函数用法
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…
SELECT IF(1 > 0,'正确','错误')    ->正确
SELECT IFNULL(null,'Hello Word')->Hello Word
SELECT CASE   WHEN 1 > 0  THEN '1 > 0'  WHEN 2 > 0  THEN '2 > 0'  ELSE '3 > 0'  END->1 > 0
SELECT CASE 1   WHEN 1 THEN '我是1'  WHEN 2 THEN '我是2'ELSE '你是谁'
SELECT employee_id,salary, CASE WHEN salary>=15000 THEN '高薪' 				  WHEN salary>=10000 THEN '潜力股'  				  WHEN salary>=8000 THEN '屌丝' 				  ELSE '草根' END  "描述"FROM employees; 
SELECT oid,`status`, CASE `status` WHEN 1 THEN '未付款' 								   WHEN 2 THEN '已付款' 								   WHEN 3 THEN '已发货'  								   WHEN 4 THEN '确认收货'  								   ELSE '无效订单' END FROM t_order;
mysql> SELECT CASE WHEN 1 > 0 THEN 'yes' WHEN 1 <= 0 THEN 'no' ELSE 'unknown' END;+---------------------------------------------------------------------+| CASE WHEN 1 > 0 THEN 'yes' WHEN 1 <= 0 THEN 'no' ELSE 'unknown' END |+---------------------------------------------------------------------+| yes                                                                  |+---------------------------------------------------------------------+1 row in set (0.00 sec)mysql> SELECT CASE WHEN 1 < 0 THEN 'yes' WHEN 1 = 0 THEN 'no' ELSE 'unknown' END;  +--------------------------------------------------------------------+| CASE WHEN 1 < 0 THEN 'yes' WHEN 1 = 0 THEN 'no' ELSE 'unknown' END |+--------------------------------------------------------------------+| unknown                                                             |+--------------------------------------------------------------------+1 row in set (0.00 sec)
mysql> SELECT CASE 1 WHEN 0 THEN 0 WHEN 1 THEN 1 ELSE -1 END;+------------------------------------------------+| CASE 1 WHEN 0 THEN 0 WHEN 1 THEN 1 ELSE -1 END |+------------------------------------------------+|                                               1 |+------------------------------------------------+1 row in set (0.00 sec)mysql> SELECT CASE -1 WHEN 0 THEN 0 WHEN 1 THEN 1 ELSE -1 END;+-------------------------------------------------+| CASE -1 WHEN 0 THEN 0 WHEN 1 THEN 1 ELSE -1 END |+-------------------------------------------------+|                                               -1 |+-------------------------------------------------+1 row in set (0.00 sec)
SELECT employee_id,12 * salary * (1 + IFNULL(commission_pct,0))FROM employees;
SELECT last_name, job_id, salary,       CASE job_id WHEN 'IT_PROG'  THEN  1.10*salary                   WHEN 'ST_CLERK' THEN  1.15*salary                   WHEN 'SA_REP'   THEN  1.20*salary       			   ELSE      salary END     "REVISED_SALARY"FROM   employees;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hCTFv2MG-1640752866028)(D:\BaiduNetdiskDownload\01_课件\images\1554980865631.png)]

练习:查询部门号为 10,20, 30 的员工信息, 若部门号为 10, 则打印其工资的 1.1 倍, 20 号部门, 则打印其工资的 1.2 倍, 30 号部门打印其工资的 1.3 倍数。

6. 加密与解密函数

加密与解密函数主要用于对数据库中的数据进行加密和解密处理,以防止数据被他人窃取。这些函数在保证数据库安全时非常有用。

函数用法
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

可以看到,ENCODE(value,password_seed)函数与DECODE(value,password_seed)函数互为反函数。

举例:

mysql> SELECT PASSWORD('mysql'), PASSWORD(NULL);+-------------------------------------------+----------------+| PASSWORD('mysql')                         | PASSWORD(NULL) |+-------------------------------------------+----------------+| *E74858DB86EBA20BC33D0AECAE8A8108C56B17FA |                |+-------------------------------------------+----------------+1 row in set, 1 warning (0.00 sec)
SELECT md5('123')->202cb962ac59075b964b07152d234b70
SELECT SHA('Tom123')->c7c506980abc31cc390a2438c90861d0f1216d50
mysql> SELECT ENCODE('mysql', 'mysql');+--------------------------+| ENCODE('mysql', 'mysql') |+--------------------------+| íg ¼ ìÉ                  |+--------------------------+1 row in set, 1 warning (0.01 sec)
mysql> SELECT DECODE(ENCODE('mysql','mysql'),'mysql');+-----------------------------------------+| DECODE(ENCODE('mysql','mysql'),'mysql') |+-----------------------------------------+| mysql                                   |+-----------------------------------------+1 row in set, 2 warnings (0.00 sec)

7. MySQL信息函数

MySQL中内置了一些可以查询MySQL信息的函数,这些函数主要用于帮助数据库开发或运维人员更好地对数据库进行维护工作。

函数用法
VERSION()返回当前MySQL的版本号
CONNECTION_ID()返回当前MySQL服务器的连接数
DATABASE(),SCHEMA()返回MySQL命令行当前所在的数据库
USER(),CURRENT_USER()、SYSTEM_USER(),SESSION_USER()返回当前连接MySQL的用户名,返回结果格式为“主机名@用户名”
CHARSET(value)返回字符串value自变量的字符集
COLLATION(value)返回字符串value的比较规则

举例:

mysql> SELECT DATABASE();+------------+| DATABASE() |+------------+| test       |+------------+1 row in set (0.00 sec)mysql> SELECT DATABASE();+------------+| DATABASE() |+------------+| test       |+------------+1 row in set (0.00 sec)
mysql> SELECT USER(), CURRENT_USER(), SYSTEM_USER(),SESSION_USER();+----------------+----------------+----------------+----------------+| USER()         | CURRENT_USER() | SYSTEM_USER()  | SESSION_USER() |+----------------+----------------+----------------+----------------+| root@localhost | root@localhost | root@localhost | root@localhost |+----------------+----------------+----------------+----------------+
mysql> SELECT CHARSET('ABC');+----------------+| CHARSET('ABC') |+----------------+| utf8mb4        |+----------------+1 row in set (0.00 sec)
mysql> SELECT COLLATION('ABC');+--------------------+| COLLATION('ABC')   |+--------------------+| utf8mb4_general_ci |+--------------------+1 row in set (0.00 sec)

8. 其他函数

MySQL中有些函数无法对其进行具体的分类,但是这些函数在MySQL的开发和运维过程中也是不容忽视的。

函数用法
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 USING char_code)将value所使用的字符编码修改为char_code

举例:

# 如果n的值小于或者等于0,则只保留整数部分mysql> SELECT FORMAT(123.123, 2), FORMAT(123.523, 0), FORMAT(123.123, -2); +--------------------+--------------------+---------------------+| FORMAT(123.123, 2) | FORMAT(123.523, 0) | FORMAT(123.123, -2) |+--------------------+--------------------+---------------------+| 123.12             | 124                | 123                 |+--------------------+--------------------+---------------------+1 row in set (0.00 sec)
mysql> SELECT CONV(16, 10, 2), CONV(8888,10,16), CONV(NULL, 10, 2);+-----------------+------------------+-------------------+| CONV(16, 10, 2) | CONV(8888,10,16) | CONV(NULL, 10, 2) |+-----------------+------------------+-------------------+| 10000           | 22B8             | NULL              |+-----------------+------------------+-------------------+1 row in set (0.00 sec)
mysql> SELECT INET_ATON('192.168.1.100');+----------------------------+| INET_ATON('192.168.1.100') |+----------------------------+|                 3232235876 |+----------------------------+1 row in set (0.00 sec)# 以“192.168.1.100”为例,计算方式为192乘以256的3次方,加上168乘以256的2次方,加上1乘以256,再加上100。
mysql> SELECT INET_NTOA(3232235876);+-----------------------+| INET_NTOA(3232235876) |+-----------------------+| 192.168.1.100         |+-----------------------+1 row in set (0.00 sec)
mysql> SELECT BENCHMARK(1, MD5('mysql'));+----------------------------+| BENCHMARK(1, MD5('mysql')) |+----------------------------+|                          0 |+----------------------------+1 row in set (0.00 sec)mysql> SELECT BENCHMARK(1000000, MD5('mysql')); +----------------------------------+| BENCHMARK(1000000, MD5('mysql')) |+----------------------------------+|                                0 |+----------------------------------+1 row in set (0.20 sec)
mysql> SELECT CHARSET('mysql'), CHARSET(CONVERT('mysql' USING 'utf8'));+------------------+----------------------------------------+| CHARSET('mysql') | CHARSET(CONVERT('mysql' USING 'utf8')) |+------------------+----------------------------------------+| utf8mb4          | utf8                                   |+------------------+----------------------------------------+1 row in set, 1 warning (0.00 sec)

第08章_聚合函数

讲师:尚硅谷-宋红康(江湖人称:康师傅)

官网:http://www.atguigu.com


我们上一章讲到了 SQL 单行函数。实际上 SQL 函数还有一类,叫做聚合(或聚集、分组)函数,它是对一组数据进行汇总的函数,输入的是一组数据的集合,输出的是单个值。

1. 聚合函数介绍

  • 什么是聚合函数

聚合函数作用于一组数据,并对一组数据返回一个值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iTCiCUVY-1640752866029)(D:\BaiduNetdiskDownload\01_课件\images\1554980924940.png)]

  • 聚合函数类型

    • AVG()
    • SUM()
    • MAX()
    • MIN()
    • **COUNT() **
  • 聚合函数语法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fH7eOWme-1640752866029)(D:\BaiduNetdiskDownload\01_课件\images\1554981029920.png)]

  • 聚合函数不能嵌套调用。比如不能出现类似“AVG(SUM(字段名称))”形式的调用。

1.1 AVG和SUM函数

可以对数值型数据使用AVG 和 SUM 函数。

SELECT AVG(salary), MAX(salary),MIN(salary), SUM(salary)
FROM   employees
WHERE  job_id LIKE '%REP%';

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kmNgbypp-1640752866031)(D:\BaiduNetdiskDownload\01_课件\images\1554981279723.png)]

1.2 MIN和MAX函数

可以对任意数据类型的数据使用 MIN 和 MAX 函数。

SELECT MIN(hire_date), MAX(hire_date)
FROM	  employees;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BGEunF4o-1640752866031)(D:\BaiduNetdiskDownload\01_课件\images\1554981253194.png)]

1.3 COUNT函数

  • COUNT(*)返回表中记录总数,适用于任意数据类型
SELECT COUNT(*)
FROM	  employees
WHERE  department_id = 50;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cv3htmbs-1640752866032)(D:\BaiduNetdiskDownload\01_课件\images\1554981241299.png)]

  • COUNT(expr) 返回expr不为空的记录总数。
SELECT COUNT(commission_pct)
FROM   employees
WHERE  department_id = 50;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sZBNtHwE-1640752866032)(D:\BaiduNetdiskDownload\01_课件\images\1554981328678.png)]

  • 问题:用count(*),count(1),count(列名)谁好呢?

    其实,对于MyISAM引擎的表是没有区别的。这种引擎内部有一计数器在维护着行数。

    Innodb引擎的表用count(*),count(1)直接读行数,复杂度是O(n),因为innodb真的要去数一遍。但好于具体的count(列名)。

  • 问题:能不能使用count(列名)替换count(*)?

    不要使用 count(列名)来替代 count(*)count(*)是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。

    说明:count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。

2. GROUP BY

2.1 基本使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d8VXF8yB-1640752866033)(D:\BaiduNetdiskDownload\01_课件\images\1554981374920.png)]

可以使用GROUP BY子句将表中的数据分成若干组

SELECT column, group_function(column)
FROM table
[WHERE	condition]
[GROUP BY	group_by_expression]
[ORDER BY	column];

明确:WHERE一定放在FROM后面

在SELECT列表中所有未包含在组函数中的列都应该包含在 GROUP BY子句中

SELECT   department_id, AVG(salary)
FROM     employees
GROUP BY department_id ;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RSuZZPg6-1640752866034)(D:\BaiduNetdiskDownload\01_课件\images\1554981539408.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sfom376d-1640752866035)(D:\BaiduNetdiskDownload\01_课件\images\1554981544191.png)]

包含在 GROUP BY 子句中的列不必包含在SELECT 列表中

SELECT   AVG(salary)
FROM     employees
GROUP BY department_id ;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JkqMxgng-1640752866036)(D:\BaiduNetdiskDownload\01_课件\images\1554981574152.png)]

2.2 使用多个列分组

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3l07DKwH-1640752866036)(D:\BaiduNetdiskDownload\01_课件\images\1554981607442.png)]

SELECT   department_id dept_id, job_id, SUM(salary)
FROM     employees
GROUP BY department_id, job_id ;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f7N36uHa-1640752866036)(D:\BaiduNetdiskDownload\01_课件\images\1554981624864.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mbMcJ7Jz-1640752866037)(D:\BaiduNetdiskDownload\01_课件\images\1554981629733.png)]

2.3 GROUP BY中使用WITH ROLLUP

使用WITH ROLLUP关键字之后,在所有查询出的分组记录之后增加一条记录,该记录计算查询出的所有记录的总和,即统计记录数量。

SELECT department_id,AVG(salary)FROM employeesWHERE department_id > 80GROUP BY department_id WITH ROLLUP;

注意:

当使用ROLLUP时,不能同时使用ORDER BY子句进行结果排序,即ROLLUP和ORDER BY是互相排斥的。

3. HAVING

3.1 基本使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0AaEV2pl-1640752866038)(D:\BaiduNetdiskDownload\01_课件\images\1554981656798.png)]

过滤分组:HAVING子句

  1. 行已经被分组。
  2. 使用了聚合函数。
  3. 满足HAVING 子句中条件的分组将被显示。
  4. HAVING 不能单独使用,必须要跟 GROUP BY 一起使用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oORoptSs-1640752866038)(D:\BaiduNetdiskDownload\01_课件\images\1554981808091.png)]

SELECT   department_id, MAX(salary)FROM     employeesGROUP BY department_idHAVING   MAX(salary)>10000 ;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5EC2Muy0-1640752866039)(D:\BaiduNetdiskDownload\01_课件\images\1554981824564.png)]

  • **非法使用聚合函数 : 不能在 WHERE 子句中使用聚合函数。**如下:
SELECT   department_id, AVG(salary)FROM     employeesWHERE    AVG(salary) > 8000GROUP BY department_id;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o5pv9H7W-1640752866039)(D:\BaiduNetdiskDownload\01_课件\images\1554981724375.png)]

3.2 WHERE和HAVING的对比

区别1:WHERE 可以直接使用表中的字段作为筛选条件,但不能使用分组中的计算函数作为筛选条件;HAVING 必须要与 GROUP BY 配合使用,可以把分组计算的函数和分组字段作为筛选条件。

这决定了,在需要对数据进行分组统计的时候,HAVING 可以完成 WHERE 不能完成的任务。这是因为,在查询语法结构中,WHERE 在 GROUP BY 之前,所以无法对分组结果进行筛选。HAVING 在 GROUP BY 之后,可以使用分组字段和分组中的计算函数,对分组的结果集进行筛选,这个功能是 WHERE 无法完成的。另外,WHERE排除的记录不再包括在分组中。

区别2:如果需要通过连接从关联表中获取需要的数据,WHERE 是先筛选后连接,而 HAVING 是先连接后筛选。 这一点,就决定了在关联查询中,WHERE 比 HAVING 更高效。因为 WHERE 可以先筛选,用一个筛选后的较小数据集和关联表进行连接,这样占用的资源比较少,执行效率也比较高。HAVING 则需要先把结果集准备好,也就是用未被筛选的数据集进行关联,然后对这个大的数据集进行筛选,这样占用的资源就比较多,执行效率也较低。

小结如下:

优点缺点
WHERE先筛选数据再关联,执行效率高不能使用分组中的计算函数进行筛选
HAVING可以使用分组中的计算函数在最后的结果集中进行筛选,执行效率较低

开发中的选择:

WHERE 和 HAVING 也不是互相排斥的,我们可以在一个查询里面同时使用 WHERE 和 HAVING。包含分组统计函数的条件用 HAVING,普通条件用 WHERE。这样,我们就既利用了 WHERE 条件的高效快速,又发挥了 HAVING 可以使用包含分组统计函数的查询条件的优点。当数据量特别大的时候,运行效率会有很大的差别。

4. SELECT的执行过程

4.1 查询的结构

#方式1:SELECT ...,....,...FROM ...,...,....WHERE 多表的连接条件AND 不包含组函数的过滤条件GROUP BY ...,...HAVING 包含组函数的过滤条件ORDER BY ... ASC/DESCLIMIT ...,...#方式2:SELECT ...,....,...FROM ... JOIN ... ON 多表的连接条件JOIN ...ON ...WHERE 不包含组函数的过滤条件AND/OR 不包含组函数的过滤条件GROUP BY ...,...HAVING 包含组函数的过滤条件ORDER BY ... ASC/DESCLIMIT ...,...#其中:#(1)from:从哪些表中筛选#(2)on:关联多表查询时,去除笛卡尔积#(3)where:从表中筛选的条件#(4)group by:分组依据#(5)having:在统计结果中再次筛选#(6)order by:排序#(7)limit:分页

4.2 SELECT执行顺序

你需要记住 SELECT 查询时的两个顺序:

1. 关键字的顺序是不能颠倒的:

SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT...

2.SELECT 语句的执行顺序(在 MySQL 和 Oracle 中,SELECT 执行顺序基本相同):

FROM -> WHERE -> GROUP BY -> HAVING -> SELECT 的字段 -> DISTINCT -> ORDER BY -> LIMIT

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-19kFwfhh-1640752866040)(D:\BaiduNetdiskDownload\01_课件\images\1566872301088.png)]

比如你写了一个 SQL 语句,那么它的关键字顺序和执行顺序是下面这样的:

SELECT DISTINCT player_id, player_name, count(*) as num # 顺序 5FROM player JOIN team ON player.team_id = team.team_id # 顺序 1WHERE height > 1.80 # 顺序 2GROUP BY player.team_id # 顺序 3HAVING num > 2 # 顺序 4ORDER BY num DESC # 顺序 6LIMIT 2 # 顺序 7

在 SELECT 语句执行这些步骤的时候,每个步骤都会产生一个虚拟表,然后将这个虚拟表传入下一个步骤中作为输入。需要注意的是,这些步骤隐含在 SQL 的执行过程中,对于我们来说是不可见的。

4.3 SQL 的执行原理

SELECT 是先执行 FROM 这一步的。在这个阶段,如果是多张表联查,还会经历下面的几个步骤:

  1. 首先先通过 CROSS JOIN 求笛卡尔积,相当于得到虚拟表 vt(virtual table)1-1;
  2. 通过 ON 进行筛选,在虚拟表 vt1-1 的基础上进行筛选,得到虚拟表 vt1-2;
  3. 添加外部行。如果我们使用的是左连接、右链接或者全连接,就会涉及到外部行,也就是在虚拟表 vt1-2 的基础上增加外部行,得到虚拟表 vt1-3。

当然如果我们操作的是两张以上的表,还会重复上面的步骤,直到所有表都被处理完为止。这个过程得到是我们的原始数据。

当我们拿到了查询数据表的原始数据,也就是最终的虚拟表 vt1,就可以在此基础上再进行 WHERE 阶段。在这个阶段中,会根据 vt1 表的结果进行筛选过滤,得到虚拟表 vt2

然后进入第三步和第四步,也就是 GROUP 和 HAVING 阶段。在这个阶段中,实际上是在虚拟表 vt2 的基础上进行分组和分组过滤,得到中间的虚拟表 vt3vt4

当我们完成了条件筛选部分之后,就可以筛选表中提取的字段,也就是进入到 SELECT 和 DISTINCT 阶段

首先在 SELECT 阶段会提取想要的字段,然后在 DISTINCT 阶段过滤掉重复的行,分别得到中间的虚拟表 vt5-1vt5-2

当我们提取了想要的字段数据之后,就可以按照指定的字段进行排序,也就是 ORDER BY 阶段,得到虚拟表 vt6

最后在 vt6 的基础上,取出指定行的记录,也就是 LIMIT 阶段,得到最终的结果,对应的是虚拟表 vt7

当然我们在写 SELECT 语句的时候,不一定存在所有的关键字,相应的阶段就会省略。

同时因为 SQL 是一门类似英语的结构化查询语言,所以我们在写 SELECT 语句的时候,还要注意相应的关键字顺序,所谓底层运行的原理,就是我们刚才讲到的执行顺序。

第09章_子查询

讲师:尚硅谷-宋红康(江湖人称:康师傅)

官网:http://www.atguigu.com


子查询指一个查询语句嵌套在另一个查询语句内部的查询,这个特性从MySQL 4.1开始引入。

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

1. 需求分析与问题解决

1.1 实际问题

1554991034688

现有解决方式:

#方式一:
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 salary
		FROM employees
		WHERE last_name = 'Abel'
		);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xzxBKmz4-1640752866040)(D:\BaiduNetdiskDownload\01_课件\images\1554991316599.png)]

1.2 子查询的基本使用

  • 子查询的基本语法结构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DVritYOf-1640752866041)(D:\BaiduNetdiskDownload\01_课件\images\1554991054388.png)]

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

1.3 子查询的分类

分类方式1:

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

  • 单行子查询

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VQFxWfcO-1640752866041)(D:\BaiduNetdiskDownload\01_课件\images\1554991538719.png)]

  • 多行子查询

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7rzjIniS-1640752866042)(D:\BaiduNetdiskDownload\01_课件\images\1554991555669.png)]

分类方式2:

我们按内查询是否被执行多次,将子查询划分为相关(或关联)子查询不相关(或非关联)子查询

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

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

2. 单行子查询

2.1 单行比较操作符

操作符含义
=equal to
>greater than
>=greater than or equal to
<less than
<=less than or equal to
<>not equal to

2.2 代码示例

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aYeTdqbC-1640752866043)(D:\BaiduNetdiskDownload\01_课件\images\image-20210914232952626.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ROwCpRTB-1640752866043)(D:\BaiduNetdiskDownload\01_课件\images\image-20210914232935062.png)]

题目:返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资

SELECT last_name, job_id, salary
FROM   employees
WHERE  job_id =  
                (SELECT job_id
                 FROM   employees
                 WHERE  employee_id = 141)
AND    salary >
                (SELECT salary
                 FROM   employees
                 WHERE  employee_id = 143);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JEO5P7nH-1640752866044)(D:\BaiduNetdiskDownload\01_课件\images\1554991892770.png)]

题目:返回公司工资最少的员工的last_name,job_id和salary

SELECT last_name, job_id, salary
FROM   employees
WHERE  salary = 
                (SELECT MIN(salary)
                 FROM   employees);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kxUKptZu-1640752866044)(D:\BaiduNetdiskDownload\01_课件\images\1554991935186.png)]

题目:查询与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_id
                   FROM    employees
                   WHERE   employee_id IN (174,141))
AND     department_id IN 
		  (SELECT  department_id
                   FROM    employees
                   WHERE   employee_id IN (174,141))
AND	employee_id NOT IN(174,141);

实现方式2:成对比较

SELECT	employee_id, manager_id, department_id
FROM	employees
WHERE  (manager_id, department_id) IN
                      (SELECT manager_id, department_id
                       FROM   employees
                       WHERE  employee_id IN (141,174))
AND	employee_id NOT IN (141,174);

2.3 HAVING 中的子查询

  • 首先执行子查询。
  • 向主查询中的HAVING 子句返回结果。

题目:查询最低工资大于50号部门最低工资的部门id和其最低工资

SELECT   department_id, MIN(salary)
FROM     employees
GROUP BY department_id
HAVING   MIN(salary) >
                       (SELECT MIN(salary)
                        FROM   employees
                        WHERE  department_id = 50);

2.4 CASE中的子查询

在CASE表达式中使用单列子查询:

题目:显式员工的employee_id,last_name和location。其中,若员工department_id与location_id为1800的department_id相同,则location为’Canada’,其余则为’USA’。

SELECT employee_id, last_name,
       (CASE department_id
        WHEN
             (SELECT department_id FROM departments
	      WHERE location_id = 1800)           
        THEN 'Canada' ELSE 'USA' END) location
FROM   employees;

2.5 子查询中的空值问题

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C9wbxCQC-1640752866045)(D:\BaiduNetdiskDownload\01_课件\images\1554992067381.png)]

子查询不返回任何行

2.5 非法使用子查询

SELECT employee_id, last_nameFROM   employeesWHERE  salary =                (SELECT   MIN(salary)                 FROM     employees                 GROUP BY department_id);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ifETA3Ri-1640752866045)(D:\BaiduNetdiskDownload\01_课件\images\1554992135819.png)]

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

3. 多行子查询

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

3.1 多行比较操作符

操作符含义
IN等于列表中的任意一个
ANY需要和单行比较操作符一起使用,和子查询返回的某一个值比较
ALL需要和单行比较操作符一起使用,和子查询返回的所有值比较
SOME实际上是ANY的别名,作用相同,一般常使用ANY

体会 ANY 和 ALL 的区别

3.2 代码示例

题目:返回其它job_id中比job_id为‘IT_PROG’部门任一工资低的员工的员工号、姓名、job_id 以及salary

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1gYPAShm-1640752866045)(D:\BaiduNetdiskDownload\01_课件\images\1554992658876.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y4fBmwkj-1640752866046)(D:\BaiduNetdiskDownload\01_课件\images\1554992664594.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xJvbIoGS-1640752866046)(D:\BaiduNetdiskDownload\01_课件\images\1554992668429.png)]

题目:返回其它job_id中比job_id为‘IT_PROG’部门所有工资都低的员工的员工号、姓名、job_id以及salary

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dxOkPP0I-1640752866046)(D:\BaiduNetdiskDownload\01_课件\images\1554992753654.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lovqtOaY-1640752866047)(D:\BaiduNetdiskDownload\01_课件\images\1554992759467.png)]

题目:查询平均工资最低的部门id

#方式1:SELECT department_idFROM employeesGROUP BY department_idHAVING AVG(salary) = (			SELECT MIN(avg_sal)			FROM (				SELECT AVG(salary) avg_sal				FROM employees				GROUP BY department_id				) dept_avg_sal			)
#方式2:SELECT department_idFROM employeesGROUP BY department_idHAVING AVG(salary) <= ALL (				SELECT AVG(salary) avg_sal				FROM employees				GROUP BY department_id)

3.3 空值问题

SELECT last_nameFROM employeesWHERE employee_id NOT IN (			SELECT manager_id			FROM employees			);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lTx4rW4a-1640752866047)(D:\BaiduNetdiskDownload\01_课件\images\image-20211027195906773.png)]

4. 相关子查询

4.1 相关子查询执行流程

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

相关子查询按照一行接一行的顺序执行,主查询的每一行都执行一次子查询。

1554992898234 1554992925281

说明:子查询中使用主查询中的列

4.2 代码示例

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

方式一:相关子查询

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kYzDLlUs-1640752866047)(D:\BaiduNetdiskDownload\01_课件\images\1554992986225.png)]

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

SELECT last_name,salary,e1.department_idFROM employees e1,(SELECT department_id,AVG(salary) dept_avg_sal FROM employees GROUP BY department_id) e2WHERE e1.`department_id` = e2.department_idAND e2.dept_avg_sal < e1.`salary`;

from型的子查询:子查询是作为from的一部分,子查询要用()引起来,并且要给这个子查询取别名,
把它当成一张“临时的虚拟的表”来使用。

在ORDER BY 中使用子查询:

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

SELECT employee_id,salaryFROM employees eORDER BY (	  SELECT department_name	  FROM departments d	  WHERE 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_idFROM   employees e WHERE  2 <= (SELECT COUNT(*)             FROM   job_history              WHERE  employee_id = e.employee_id);

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_idFROM   employees e1WHERE  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_idFROM   employees e1 JOIN employees e2WHERE e1.employee_id = e2.manager_id;

方式三:

SELECT employee_id,last_name,job_id,department_idFROM employeesWHERE employee_id IN (		     SELECT DISTINCT manager_id		     FROM employees		     		     );

题目:查询departments表中,不存在于employees表中的部门的department_id和department_name

SELECT department_id, department_nameFROM departments dWHERE NOT EXISTS (SELECT 'X'                  FROM   employees                  WHERE  department_id = d.department_id);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DBehVR12-1640752866048)(D:\BaiduNetdiskDownload\01_课件\images\1554993169269.png)]

4.4 相关更新

UPDATE table1 alias1SET    column = (SELECT expression                 FROM   table2 alias2                 WHERE  alias1.column = alias2.column);

使用相关子查询依据一个表中的数据更新另一个表的数据。

题目:在employees中增加一个department_name字段,数据为员工对应的部门名称

# 1)ALTER TABLE employeesADD(department_name VARCHAR2(14));# 2)UPDATE employees eSET department_name =  (SELECT department_name 	                       FROM   departments d	                       WHERE  e.department_id = d.department_id);

4.4 相关删除

 DELETE FROM table1 alias1 WHERE column operator (SELECT expression                        FROM   table2 alias2                        WHERE  alias1.column = alias2.column);

使用相关子查询依据一个表中的数据删除另一个表的数据。

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

DELETE FROM employees eWHERE employee_id in             (SELECT employee_id            FROM   emp_history             WHERE  employee_id = e.employee_id);

5. 抛一个思考题

**问题:**谁的工资比Abel的高?

解答:

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

**问题:**以上两种方式有好坏之分吗?

**解答:**自连接方式好!

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

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

第10章_创建和管理表

讲师:尚硅谷-宋红康(江湖人称:康师傅)

官网:http://www.atguigu.com


1. 基础知识

1.1 一条数据存储的过程

存储数据是处理数据的第一步。只有正确地把数据存储起来,我们才能进行有效的处理和分析。否则,只能是一团乱麻,无从下手。

那么,怎样才能把用户各种经营相关的、纷繁复杂的数据,有序、高效地存储起来呢? 在 MySQL 中,一个完整的数据存储过程总共有 4 步,分别是创建数据库、确认字段、创建数据表、插入数据。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LmP84CXQ-1640752866049)(D:/BaiduNetdiskDownload/01_课件/images/image-20211007155810920.png)]

我们要先创建一个数据库,而不是直接创建数据表呢?

因为从系统架构的层次上看,MySQL 数据库系统从大到小依次是数据库服务器数据库数据表、数据表的行与列

MySQL 数据库服务器之前已经安装。所以,我们就从创建数据库开始。

1.2 标识符命名规则

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

1.3 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

其中,常用的几类类型介绍如下:

数据类型描述
INT从-231到231-1的整型数据。存储大小为 4个字节
CHAR(size)定长字符数据。若未指定,默认为1个字符,最大长度255
VARCHAR(size)可变长字符数据,根据字符串实际长度保存,必须指定长度
FLOAT(M,D)单精度,占用4个字节,M=整数位+小数位,D=小数位。 D<=M<=255,0<=D<=30,默认M+D<=6
DOUBLE(M,D)双精度,占用8个字节,D<=M<=255,0<=D<=30,默认M+D<=15
DECIMAL(M,D)高精度小数,占用M+2个字节,D<=M<=65,0<=D<=30,最大取值范围与DOUBLE相同。
DATE日期型数据,格式’YYYY-MM-DD’
BLOB二进制形式的长文本数据,最大可达4G
TEXT长文本数据,最大可达4G

2. 创建和管理数据库

2.1 创建数据库

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

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

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

2.2 使用数据库

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

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

2.3 修改数据库

  • 更改数据库字符集
ALTER DATABASE 数据库名 CHARACTER SET 字符集;  #比如:gbk、utf8等

2.4 删除数据库

  • 方式1:删除指定的数据库
DROP DATABASE 数据库名;
  • 方式2:删除指定的数据库(推荐
DROP DATABASE IF EXISTS 数据库名;

3. 创建表

3.1 创建方式1

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

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u1J3WbSE-1640752866049)(D:/BaiduNetdiskDownload/01_课件/images/image-20211016160557995.png)]

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QS01eTZ0-1640752866049)(D:/BaiduNetdiskDownload/01_课件/images/image-20211016160643445.png)]

在MySQL 8.x版本中,不再推荐为INT类型指定显示长度,并在未来的版本中可能去掉这样的语法。

3.2 创建方式2

  • 使用 AS subquery 选项,将创建表和插入数据结合起来

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uv4GBws6-1640752866050)(D:/BaiduNetdiskDownload/01_课件/images/1554997882872.png)]

  • 指定的列和子查询中的列要一一对应

  • 通过列名和默认值定义列

CREATE TABLE emp1 AS SELECT * FROM employees;CREATE TABLE emp2 AS SELECT * FROM employees WHERE 1=2; -- 创建的emp2是空表
CREATE TABLE dept80AS SELECT  employee_id, last_name, salary*12 ANNSAL, hire_dateFROM    employeesWHERE   department_id = 80;
DESCRIBE dept80;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nHmlL7dg-1640752866050)(D:/BaiduNetdiskDownload/01_课件/images/1554997998148.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qqu4yob4-1640752866050)(D:/BaiduNetdiskDownload/01_课件/images/1554998004494.png)]

3.3 查看数据表结构

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

语法格式如下:

SHOW CREATE TABLE 表名\G

使用SHOW CREATE TABLE语句不仅可以查看表创建时的详细语句,还可以查看存储引擎和字符编码。

4. 修改表

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

使用 ALTER TABLE 语句可以实现:

  • 向已有的表中添加列

  • 修改现有表中的列

  • 删除现有表中的列

  • 重命名现有表中的列

4.1 追加一个列

语法格式如下:

ALTER TABLE 表名 ADD 【COLUMN】 字段名 字段类型 【FIRST|AFTER 字段名】;

举例:

ALTER TABLE dept80 ADD job_id varchar(15);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8hHumt9g-1640752866051)(D:/BaiduNetdiskDownload/01_课件/images/1554998139815.png)]

4.2 修改一个列

  • 可以修改列的数据类型,长度、默认值和位置

  • 修改字段数据类型、长度、默认值、位置的语法格式如下:

ALTER TABLE 表名 MODIFY 【COLUMN】 字段名1 字段类型 【DEFAULT 默认值】【FIRST|AFTER 字段名2】;
  • 举例:
ALTER TABLE	dept80MODIFY last_name VARCHAR(30);
ALTER TABLE	dept80MODIFY salary double(9,2) default 1000;
  • 对默认值的修改只影响今后对表的修改
  • 此外,还可以通过此种方式修改列的约束。这里暂先不讲。

4.3 重命名一个列

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

ALTER TABLE 表名 CHANGE 【column】 列名 新列名 新数据类型;

举例:

ALTER TABLE  dept80CHANGE department_name dept_name varchar(15); 

4.4 删除一个列

删除表中某个字段的语法格式如下:

ALTER TABLE 表名 DROP 【COLUMN】字段名

举例:

ALTER TABLE  dept80DROP COLUMN  job_id; 

5. 重命名表

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

6. 删除表

  • 在MySQL中,当一张数据表没有与其他任何数据表形成关联关系时,可以将当前数据表直接删除。

  • 数据和结构都被删除

  • 所有正在运行的相关事务被提交

  • 所有相关索引被删除

  • 语法格式:

DROP TABLE [IF EXISTS] 数据表1 [, 数据表2, …, 数据表n];

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

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

7. 清空表

  • TRUNCATE TABLE语句:

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

TRUNCATE TABLE detail_dept;
  • TRUNCATE语句不能回滚,而使用 DELETE 语句删除数据,可以回滚

  • 对比:

SET autocommit = FALSE;  DELETE FROM emp2; #TRUNCATE TABLE emp2;  SELECT * FROM emp2;  ROLLBACK;  SELECT * FROM emp2;

阿里开发规范:

【参考】TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但 TRUNCATE 无事务且不触发 TRIGGER,有可能造成事故,故不建议在开发代码中使用此语句。

说明:TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。

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
  • 推荐】库名与应用名称尽量一致。

  • 【参考】合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度。

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

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Ylsgzkq-1640752866051)(D:/BaiduNetdiskDownload/01_课件/images/image-20211024012735469.png)]

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

表删除操作将把表的定义和表中的数据一起删除,并且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;USE mytest;CREATE TABLE book1(book_id INT ,book_name VARCHAR(255));SHOW TABLES;

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

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

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

mysql> SHOW TABLES;Empty set (0.00 sec)

从结果可以看出,虽然删除操作时报错了,但是仍然删除了数据表book1。

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

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

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

mysql> show tables;+------------------+| Tables_in_mytest |+------------------+| book1            |+------------------+1 row in set (0.00 sec)

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

第11章_数据处理之增删改

讲师:尚硅谷-宋红康(江湖人称:康师傅)

官网:http://www.atguigu.com


1. 插入数据

1.1 实际问题

1555425366064

解决方式:使用 INSERT 语句向表中插入数据。

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语句中只向部分字段中插入值,而其他字段的值为表定义时的默认值。

在 INSERT 子句中随意列出列名,但是一旦列出,VALUES中要插入的value1,…valuen需要与column1,…columnn列一一对应。如果类型不同,将无法插入,并且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]);

举例:

mysql> INSERT INTO emp(emp_id,emp_name)
    -> VALUES (1001,'shkstart'),
    -> (1002,'atguigu'),
    -> (1003,'Tom');
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是标准写法。

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

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 employeesWHERE department_id = 90;
INSERT INTO sales_reps(id, name, salary, commission_pct)SELECT employee_id, last_name, salary, commission_pctFROM   employeesWHERE  job_id LIKE '%REP%';

2. 更新数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mo5VRCZ1-1640752866052)(D:\BaiduNetdiskDownload\01_课件\images\1555425824246.png)]

  • 使用 UPDATE 语句更新数据。语法如下:
UPDATE table_nameSET column1=value1, column2=value2, … , column=valuen[WHERE condition]
  • 可以一次更新多条数据。

  • 如果需要回滚数据,需要保证在DML前,进行设置:SET AUTOCOMMIT = FALSE;


  • 使用 WHERE 子句指定需要更新的数据。
UPDATE employeesSET    department_id = 70WHERE  employee_id = 113;
  • 如果省略 WHERE 子句,则表中的所有数据都将被更新。
UPDATE 	copy_empSET    	department_id = 110;
  • 更新中的数据完整性错误
UPDATE employeesSET    department_id = 55WHERE  department_id = 110;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KesGQ8Bi-1640752866052)(D:\BaiduNetdiskDownload\01_课件\images\1555426069578.png)]

说明:不存在 55 号部门

3. 删除数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YKtWzIFX-1640752866052)(D:\BaiduNetdiskDownload\01_课件\images\1555426124751.png)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-km6lRMfj-1640752866053)(D:\BaiduNetdiskDownload\01_课件\images\1555426162264.png)]

DELETE FROM table_name [WHERE <condition>];

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MuFw9lPo-1640752866053)(D:\BaiduNetdiskDownload\01_课件\images\1555426258516.png)]

说明:You cannot delete a row that contains a primary key that is used as a foreign key in another table.

4. MySQL8新特性:计算列

什么叫计算列呢?简单来说就是某一列的值是通过别的列计算得来的。例如,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 GENERATED ALWAYS AS (a + b) VIRTUAL);

插入演示数据,语句如下:

INSERT INTO tb1(a,b) VALUES (100,200);

查询数据表tb1中的数据,结果如下:

mysql> SELECT * FROM tb1;+------+------+------+------+| id   | a    | b    | c    |+------+------+------+------+| NULL |  100 |  200 |  300 |+------+------+------+------+1 row in set (0.00 sec)

更新数据中的数据,语句如下:

mysql> UPDATE tb1 SET a = 500;Query OK, 0 rows affected (0.00 sec)Rows matched: 1  Changed: 0  Warnings: 0

5. 综合案例

# 1、创建数据库test01_library# 2、创建表 books,表结构如下:
字段名字段说明数据类型
id书编号INT
name书名VARCHAR(50)
authors作者VARCHAR(100)
price价格FLOAT
pubdate出版日期YEAR
note说明VARCHAR(100)
num库存INT
# 3、向books表中插入记录# 1)不指定字段名称,插入第一条记录# 2)指定所有字段名称,插入第二记录# 3)同时插入多条记录(剩下的所有记录)
idnameauthorspricepubdatenotenum
1Tal of AAADickes231995novel11
2EmmaTJane lura351993joke22
3Story of JaneJane Tim402001novel0
4Lovey DayGeorge Byron202005novel30
5Old landHonore Blade302010law0
6The BattleUpton Sara301999medicine40
7Rose HoodRichard haggard282008cartoon28
# 4、将小说类型(novel)的书的价格都增加5。# 5、将名称为EmmaT的书的价格改为40,并将说明改为drama。# 6、删除库存为0的记录。
# 7、统计书名中包含a字母的书# 8、统计书名中包含a字母的书的数量和库存总量# 9、找出“novel”类型的书,按照价格降序排列# 10、查询图书信息,按照库存量降序排列,如果库存量相同的按照note升序排列# 11、按照note分类统计书的数量# 12、按照note分类统计书的库存量,显示库存量超过30本的# 13、查询所有图书,每页显示5本,显示第二页# 14、按照note分类统计书的库存量,显示库存量最多的# 15、查询书名达到10个字符的书,不包括里面的空格# 16、查询书名和类型,其中note值为novel显示小说,law显示法律,medicine显示医药,cartoon显示卡通,joke显示笑话# 17、查询书名、库存,其中num值超过30本的,显示滞销,大于0并低于10的,显示畅销,为0的显示需要无货# 18、统计每一种note的库存量,并合计总量# 19、统计每一种note的数量,并合计总量# 20、统计库存量前三名的图书# 21、找出最早出版的一本书# 22、找出novel中价格最高的一本书# 23、找出书名中字数最多的一本书,不含空格

答案:

#1、创建数据库test01_libraryCREATE DATABASE IF NOT EXISTS test01_library CHARACTER SET 'utf8';#指定使用哪个数据库USE test01_library;#2、创建表 booksCREATE 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',11);# 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 books SET price=40,note='drama' 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;

第12章_MySQL数据类型精讲

讲师:尚硅谷-宋红康(江湖人称:康师傅)

官网:http://www.atguigu.com


1. 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指定一个字符集

2. 整数类型

2.1 类型介绍

整数类型一共有 5 种,包括 TINYINT、SMALLINT、MEDIUMINT、INT(INTEGER)和 BIGINT。

它们的区别如下表所示:

整数类型字节有符号数取值范围无符号数取值范围
TINYINT1-128~1270~255
SMALLINT2-32768~327670~65535
MEDIUMINT3-8388608~83886070~16777215
INT、INTEGER4-2147483648~21474836470~4294967295
BIGINT8-9223372036854775808~92233720368547758070~18446744073709551615

2.2 可选属性

整数类型的可选属性有三个:

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;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
|   x   | tinyint(4)   | YES  |     | NULL    |       |
|  y   | smallint(6)  | YES  |     | NULL    |       |
|  z   | mediumint(9) | YES  |     | NULL    |       |
|  m   | int(11)      | YES  |     | NULL    |       |
|  n   | bigint(20)   | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
5 rows in set (0.00 sec)

TINYINT有符号数和无符号数的取值范围分别为-128127和0255,由于负号占了一个数字位,因此TINYINT默认的显示宽度为4。同理,其他整数类型的默认显示宽度与其有符号数的最小值的宽度相同。

举例:

CREATE TABLE test_int2(
f1 INT,
f2 INT(5),
f3 INT(5) ZEROFILL
)

DESC test_int2;

INSERT INTO test_int2(f1,f2,f3)
VALUES(1,123,123);

INSERT INTO test_int2(f1,f2)
VALUES(123456,123456);

INSERT INTO test_int2(f1,f2,f3)
VALUES(123456,123456,123456);
mysql> SELECT * FROM test_int2;
+--------+--------+--------+
| f1     | f2     | f3     |
+--------+--------+--------+
|      1 |    123 |  00123 |
| 123456 | 123456 |   NULL |
| 123456 | 123456 | 123456 |
+--------+--------+--------+
3 rows in set (0.00 sec)
2.2.2 UNSIGNED

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

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

CREATE TABLE test_int3(
f1 INT UNSIGNED
);

mysql> desc test_int3;
+-------+------------------+------+-----+---------+-------+
| Field | Type             | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| f1    | int(10) unsigned | YES  |     | NULL    |       |
+-------+------------------+------+-----+---------+-------+
1 row in set (0.00 sec)
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 进行填充。

2.3 适用场景

TINYINT:一般用于枚举数据,比如系统设定取值范围很小且固定的场景。

SMALLINT:可以用于较小范围的统计数据,比如统计工厂的固定资产库存数量等。

MEDIUMINT:用于较大整数的计算,比如车站每日的客流量等。

INT、INTEGER:取值范围足够大,一般情况下不用考虑超限问题,用得最多。比如商品编号。

BIGINT:只有当你处理特别巨大的整数时才会用到。比如双十一的交易量、大型门户网站点击量、证券公司衍生产品持仓等。

2.4 如何选择?

在评估用哪种整数类型的时候,你需要考虑存储空间可靠性的平衡问题:一方 面,用占用字节数少的整数类型可以节省存储空间;另一方面,要是为了节省存储空间, 使用的整数类型取值范围太小,一旦遇到超出取值范围的情况,就可能引起系统错误,影响可靠性。

举个例子,商品编号采用的数据类型是 INT。原因就在于,客户门店中流通的商品种类较多,而且,每天都有旧商品下架,新商品上架,这样不断迭代,日积月累。

如果使用 SMALLINT 类型,虽然占用字节数比 INT 类型的整数少,但是却不能保证数据不会超出范围 65535。相反,使用 INT,就能确保有足够大的取值范围,不用担心数据超出范围影响可靠性的问题。

你要注意的是,在实际工作中,系统故障产生的成本远远超过增加几个字段存储空间所产生的成本。因此,我建议你首先确保数据不会超过取值范围,在这个前提之下,再去考虑如何节省存储空间。

3. 浮点类型

3.1 类型介绍

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r8Y1lB3S-1640752866053)(D:\BaiduNetdiskDownload\01_课件\images\image-20211007173312237.png)]

  • 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 的浮点数都会存储表示符号的部分。因此, 所谓的无符号数取值范围,其实就是有符号数取值范围大于等于零的部分。

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也不推荐使用了,将来也可能被移除。

  • 举例

    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;
    

3.3 精度误差说明

浮点数类型有个缺陷,就是不精准。下面我来重点解释一下为什么 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

4. 定点数类型

4.1 类型介绍

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

    数据类型字节数含义
    DECIMAL(M,D),DEC,NUMERICM+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(f1 DECIMAL,f2 DECIMAL(5,2));DESC test_decimal1;INSERT INTO test_decimal1(f1,f2)VALUES(123.123,123.456);#Out of range value for column 'f2' at row 1INSERT INTO test_decimal1(f2)VALUES(1234.34);
    
    mysql> SELECT * FROM test_decimal1;+------+--------+| f1   | f2     |+------+--------+|  123 | 123.46 |+------+--------+1 row in set (0.00 sec)
    
  • 举例

    我们运行下面的语句,把test_double2表中字段“f1”的数据类型修改为 DECIMAL(5,2):

    ALTER TABLE test_double2MODIFY f1 DECIMAL(5,2);
    

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

    mysql> SELECT SUM(f1)    -> FROM test_double2;+---------+| SUM(f1) |+---------+|    1.10 |+---------+1 row in set (0.00 sec)
    
    mysql> SELECT SUM(f1) = 1.1    -> FROM test_double2;+---------------+| SUM(f1) = 1.1 |+---------------+|             1 |+---------------+1 row in set (0.00 sec)
    

4.2 开发中经验

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

5. 位类型:BIT

BIT类型中存储的是二进制值,类似010110。

二进制字符串类型长度长度范围占用空间
BIT(M)M1 <= M <= 64约为(M + 7)/8个字节

BIT类型,如果没有指定(M),默认是1位。这个1位,表示只能存1位的二进制值。这里(M)是表示二进制的位数,位数最小值为1,最大值为64。

CREATE TABLE test_bit1(f1 BIT,f2 BIT(5),f3 BIT(64));INSERT INTO test_bit1(f1)VALUES(1);#Data too long for column 'f1' at row 1INSERT INTO test_bit1(f1)VALUES(2);INSERT INTO test_bit1(f2)VALUES(23);

注意:在向BIT类型的字段中插入数据时,一定要确保插入的数据在BIT类型支持的范围内。

使用SELECT命令查询位字段时,可以用BIN()HEX()函数进行读取。

mysql> SELECT * FROM test_bit1;+------------+------------+------------+| f1         | f2         | f3         |+------------+------------+------------+| 0x01       | NULL       | NULL       || NULL       | 0x17       | NULL       |+------------+------------+------------+2 rows in set (0.00 sec)
mysql> SELECT BIN(f2),HEX(f2)    -> FROM test_bit1;+---------+---------+| BIN(f2) | HEX(f2) |+---------+---------+| NULL    | NULL    || 10111   | 17      |+---------+---------+2 rows in set (0.00 sec)
mysql> SELECT f2 + 0    -> FROM test_bit1;+--------+| f2 + 0 |+--------+|   NULL ||     23 |+--------+2 rows in set (0.00 sec)

可以看到,使用b+0查询数据时,可以直接查询出存储的十进制数据的值。

6. 日期与时间类型

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

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

  • YEAR类型通常用来表示年
  • DATE类型通常用来表示年、月、日
  • TIME类型通常用来表示时、分、秒
  • DATETIME类型通常用来表示年、月、日、时、分、秒
  • TIMESTAMP类型通常用来表示带时区的年、月、日、时、分、秒
类型名称字节日期格式最小值最大值
YEAR1YYYY或YY19012155
TIME时间3HH:MM:SS-838:59:59838:59:59
DATE日期3YYYY-MM-DD1000-01-019999-12-03
DATETIME日期时间8YYYY-MM-DD HH:MM:SS1000-01-01 00:00:009999-12-31 23:59:59
TIMESTAMP日期时间4YYYY-MM-DD HH:MM:SS1970-01-01 00:00:00 UTC2038-01-19 03:14:07UTC

可以看到,不同数据类型表示的时间内容不同、取值范围不同,而且占用的字节数也不一样,你要根据实际需要灵活选取。

为什么时间类型 TIME 的取值范围不是 -23:59:59~23:59:59 呢?原因是 MySQL 设计的 TIME 类型,不光表示一天之内的时间,而且可以用来表示一个时间间隔,这个时间间隔可以超过 24 小时。

6.1 YEAR类型

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

在MySQL中,YEAR有以下几种存储格式:

  • 以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)数据类型。

CREATE TABLE test_year(f1 YEAR,f2 YEAR(4));
mysql> DESC test_year;+-------+---------+------+-----+---------+-------+| Field | Type    | Null | Key | Default | Extra |+-------+---------+------+-----+---------+-------+| f1    | year(4) | YES  |     | NULL    |       || f2    | year(4) | YES  |     | NULL    |       |+-------+---------+------+-----+---------+-------+2 rows in set (0.00 sec)
INSERT INTO test_yearVALUES('2020','2021');mysql> SELECT * FROM test_year;+------+------+| f1   | f2   |+------+------+| 2020 | 2021 |+------+------+1 rows in set (0.00 sec)
INSERT INTO test_yearVALUES('45','71');INSERT INTO test_yearVALUES(0,'0');mysql> SELECT * FROM test_year;+------+------+| f1   | f2   |+------+------+| 2020 | 2021 || 2045 | 1971 || 0000 | 2000 |+------+------+3 rows in set (0.00 sec)

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);Query OK, 0 rows affected (0.13 sec)

插入数据:

INSERT INTO test_date1VALUES ('2020-10-01'), ('20201001'),(20201001);INSERT INTO test_date1VALUES ('00-01-01'), ('000101'), ('69-10-01'), ('691001'), ('70-01-01'), ('700101'), ('99-01-01'), ('990101');INSERT INTO test_date1VALUES (000301), (690301), (700301), (990301); INSERT INTO test_date1VALUES (CURRENT_DATE()), (NOW());SELECT *FROM test_date1;

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);Query OK, 0 rows affected (0.02 sec)
INSERT INTO test_time1VALUES('2 12:30:29'), ('12:35:29'), ('12:40'), ('2 12:40'),('1 05'), ('45');INSERT INTO test_time1VALUES ('123520'), (124011),(1210);INSERT INTO test_time1VALUES (NOW()), (CURRENT_TIME());SELECT * FROM test_time1;

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_datetime1VALUES ('2021-01-01 06:50:30'), ('20210101065030');INSERT INTO test_datetime1VALUES ('99-01-01 00:00:00'), ('990101000000'), ('20-01-01 00:00:00'), ('200101000000');INSERT INTO test_datetime1VALUES (20200101000000), (200101000000), (19990101000000), (990101000000); INSERT INTO test_datetime1VALUES (CURRENT_TIMESTAMP()), (NOW());

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_timestamp1VALUES ('1999-01-01 03:04:50'), ('19990101030405'), ('99-01-01 03:04:05'), ('990101030405');INSERT INTO test_timestamp1VALUES ('2020@01@01@00@00@00'), ('20@01@01@00@00@00');INSERT INTO test_timestamp1VALUES (CURRENT_TIMESTAMP()), (NOW());#Incorrect datetime valueINSERT INTO test_timestamp1VALUES ('2038-01-20 03:14:07');

TIMESTAMP和DATETIME的区别:

  • TIMESTAMP存储空间比较小,表示的日期时间范围也比较小

  • 底层存储方式不同,TIMESTAMP底层存储的是毫秒值,距离1970-1-1 0:0:0 0毫秒的毫秒值。

  • 两个日期比较大小或日期计算时,TIMESTAMP更方便、更快。

  • TIMESTAMP和时区有关。TIMESTAMP会根据用户的时区不同,显示不同的结果。而DATETIME则只能反映出插入时当地的时区,其他时区的人查看数据必然会有误差的。

    CREATE TABLE temp_time(d1 DATETIME,d2 TIMESTAMP);
    
    INSERT INTO temp_time VALUES('2021-9-2 14:45:52','2021-9-2 14:45:52');INSERT INTO temp_time VALUES(NOW(),NOW());
    
    mysql> SELECT * FROM temp_time;+---------------------+---------------------+| d1                  | d2                  |+---------------------+---------------------+| 2021-09-02 14:45:52 | 2021-09-02 14:45:52 || 2021-11-03 17:38:17 | 2021-11-03 17:38:17 |+---------------------+---------------------+2 rows in set (0.00 sec)
    
    #修改当前的时区SET time_zone = '+9:00';
    
    mysql> SELECT * FROM temp_time;+---------------------+---------------------+| d1                  | d2                  |+---------------------+---------------------+| 2021-09-02 14:45:52 | 2021-09-02 15:45:52 || 2021-11-03 17:38:17 | 2021-11-03 18:38:17 |+---------------------+---------------------+2 rows in set (0.00 sec)
    

6.6 开发中经验

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

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

mysql> SELECT UNIX_TIMESTAMP();+------------------+| UNIX_TIMESTAMP() |+------------------+|       1635932762 |+------------------+1 row in set (0.00 sec)

7. 文本字符串类型

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

MySQL中,文本字符串总体上分为CHARVARCHARTINYTEXTTEXTMEDIUMTEXTLONGTEXTENUMSET等类型。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EkaMrlDC-1640752866054)(D:\BaiduNetdiskDownload\01_课件\images\image-20211012003508730.png)]

7.1 CHAR与VARCHAR类型

CHAR和VARCHAR类型都可以存储比较短的字符串。

字符串(文本)类型特点长度长度范围占用的存储空间
CHAR(M)固定长度M0 <= M <= 255M个字节
VARCHAR(M)可变长度M0 <= M <= 65535(实际长度 + 1) 个字节

CHAR类型:

  • CHAR(M) 类型一般需要预先定义字符串长度。如果不指定(M),则表示长度默认是1个字符。
  • 如果保存时,数据的实际长度比CHAR类型声明的长度小,则会在右侧填充空格以达到指定的长度。当MySQL检索CHAR类型的数据时,CHAR类型的字段会去除尾部的空格。
  • 定义CHAR类型字段时,声明的字段长度即为CHAR类型字段所占的存储空间的字节数。
CREATE TABLE test_char1(c1 CHAR,c2 CHAR(5));DESC test_char1;
INSERT INTO test_char1VALUES('a','Tom');SELECT c1,CONCAT(c2,'***') FROM test_char1;
INSERT INTO test_char1(c2)VALUES('a  ');SELECT CHAR_LENGTH(c2)FROM test_char1;

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)  #错误);
CREATE TABLE test_varchar3(NAME VARCHAR(5));INSERT INTO test_varchar3VALUES('尚硅谷'),('尚硅谷教育');#Data too long for column 'NAME' at row 1INSERT INTO test_varchar3VALUES('尚硅谷IT教育');

哪些情况使用 CHAR 或 VARCHAR 更好

类型特点空间上时间上适用场景
CHAR(M)固定长度浪费存储空间效率高存储不大,速度要求高
VARCHAR(M)可变长度节省存储空间效率低非CHAR的情况

情况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和数据存储总量比较好。

7.2 TEXT类型

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

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

每种TEXT类型保存的数据长度和所占用的存储空间不同,如下:

文本字符串类型特点长度长度范围占用的存储空间
TINYTEXT小文本、可变长度L0 <= L <= 255L + 2 个字节
TEXT文本、可变长度L0 <= L <= 65535L + 2 个字节
MEDIUMTEXT中等文本、可变长度L0 <= L <= 16777215L + 3 个字节
LONGTEXT大文本、可变长度L0 <= L<= 4294967295(相当于4GB)L + 4 个字节

由于实际存储的长度不确定,MySQL 不允许 TEXT 类型的字段做主键。遇到这种情况,你只能采用 CHAR(M),或者 VARCHAR(M)。

举例:

创建数据表:

CREATE TABLE test_text(tx TEXT);
INSERT INTO test_textVALUES('atguigu   ');SELECT CHAR_LENGTH(tx)FROM test_text; #10

说明在保存和查询数据时,并没有删除TEXT类型的数据尾部的空格。

开发中经验:

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

8. ENUM类型

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

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

文本字符串类型长度长度范围占用的存储空间
ENUML1 <= L <= 655351或2个字节
  • 当ENUM类型包含1~255个成员时,需要1个字节的存储空间;

  • 当ENUM类型包含256~65535个成员时,需要2个字节的存储空间。

  • ENUM类型的成员个数的上限为65535个。

举例:

创建表如下:

CREATE TABLE test_enum(season ENUM('春','夏','秋','冬','unknow'));

添加数据:

INSERT INTO test_enumVALUES('春'),('秋');# 忽略大小写INSERT INTO test_enumVALUES('UNKNOW');# 允许按照角标的方式获取指定索引位置的枚举值INSERT INTO test_enumVALUES('1'),(3);# Data truncated for column 'season' at row 1INSERT INTO test_enumVALUES('ab');# 当ENUM类型的字段没有声明为NOT NULL时,插入NULL也是有效的INSERT INTO test_enumVALUES(NULL);

9. SET类型

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

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

成员个数范围(L表示实际成员个数)占用的存储空间
1 <= L <= 81个字节
9 <= L <= 162个字节
17 <= L <= 243个字节
25 <= L <= 324个字节
33 <= L <= 648个字节

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

举例:

创建表:

CREATE TABLE test_set(s SET ('A', 'B', 'C'));

向表中插入数据:

INSERT INTO test_set (s) VALUES ('A'), ('A,B');#插入重复的SET类型成员时,MySQL会自动删除重复的成员INSERT INTO test_set (s) VALUES ('A,B,C,A');#向SET类型的字段插入SET成员中不存在的值时,MySQL会抛出错误。INSERT INTO test_set (s) VALUES ('A,B,C,D');SELECT *FROM test_set;

举例:

CREATE TABLE temp_mul(gender ENUM('男','女'),hobby SET('吃饭','睡觉','打豆豆','写代码'));
INSERT INTO temp_mul VALUES('男','睡觉,打豆豆'); #成功# Data truncated for column 'gender' at row 1INSERT INTO temp_mul VALUES('男,女','睡觉,写代码'); #失败# Data truncated for column 'gender' at row 1INSERT INTO temp_mul VALUES('妖','睡觉,写代码');#失败INSERT INTO temp_mul VALUES('男','睡觉,写代码,吃饭'); #成功

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),否则报错。

二进制字符串类型特点值的长度占用空间
BINARY(M)固定长度M (0 <= M <= 255)M个字节
VARBINARY(M)可变长度M(0 <= M <= 65535)M+1个字节

举例:

创建表:

CREATE TABLE test_binary1(f1 BINARY,f2 BINARY(3),# f3 VARBINARY,f4 VARBINARY(10));

添加数据:

INSERT INTO test_binary1(f1,f2)VALUES('a','a');INSERT INTO test_binary1(f1,f2)VALUES('尚','尚');#失败
INSERT INTO test_binary1(f2,f4)VALUES('ab','ab');mysql> SELECT LENGTH(f2),LENGTH(f4)    -> FROM test_binary1;+------------+------------+| LENGTH(f2) | LENGTH(f4) |+------------+------------+|          3 |       NULL ||          3 |          2 |+------------+------------+2 rows in set (0.00 sec)
BLOB类型

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

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

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

二进制字符串类型值的长度长度范围占用空间
TINYBLOBL0 <= L <= 255L + 1 个字节
BLOBL0 <= L <= 65535(相当于64KB)L + 2 个字节
MEDIUMBLOBL0 <= L <= 16777215 (相当于16MB)L + 3 个字节
LONGBLOBL0 <= L <= 4294967295(相当于4GB)L + 4 个字节

举例:

CREATE TABLE test_blob1(id INT,img MEDIUMBLOB);

TEXT和BLOB的使用注意事项:

在使用text和blob字段类型时要注意以下几点,以便更好的发挥数据库的性能。

① BLOB和TEXT值也会引起自己的一些问题,特别是执行了大量的删除或更新操作的时候。删除这种值会在数据表中留下很大的"空洞",以后填入这些"空洞"的记录可能长度不同。为了提高性能,建议定期使用 OPTIMIZE TABLE 功能对这类表进行碎片整理

② 如果需要对大文本字段进行模糊查询,MySQL 提供了前缀索引。但是仍然要在不必要的时候避免检索大型的BLOB或TEXT值。例如,SELECT * 查询就不是很好的想法,除非你能够确定作为约束条件的WHERE子句只会找到所需要的数据行。否则,你可能毫无目的地在网络上传输大量的值。

③ 把BLOB或TEXT列分离到单独的表中。在某些环境中,如果把这些数据列移动到第二张数据表中,可以让你把原数据表中的数据列转换为固定长度的数据行格式,那么它就是有意义的。这会减少主表中的碎片,使你得到固定长度数据行的性能优势。它还使你在主数据表上运行 SELECT * 查询的时候不会通过网络传输大量的BLOB或TEXT值。

11. JSON 类型

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

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

CREATE TABLE test_json(js json);

向表中插入JSON数据。

INSERT INTO test_json (js) VALUES ('{"name":"songhk", "age":18, "address":{"province":"beijing", "city":"beijing"}}');

查询t19表中的数据。

mysql> SELECT *    -> FROM test_json;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v5Qlm4aa-1640752866055)(D:\BaiduNetdiskDownload\01_课件\images\image-20211104192516324.png)]

当需要检索JSON类型的字段中数据的某个具体值时,可以使用“->”和“->>”符号。

mysql> SELECT js -> '$.name' AS NAME,js -> '$.age' AS age ,js -> '$.address.province' AS province, js -> '$.address.city' AS city    -> FROM test_json;+----------+------+-----------+-----------+| NAME     | age  | province  | city      |+----------+------+-----------+-----------+| "songhk" | 18   | "beijing" | "beijing" |+----------+------+-----------+-----------+1 row in set (0.00 sec)

通过“->”和“->>”符号,从JSON字段中正确查询出了指定的JSON数据的值。

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))。

下面展示几种常见的几何图形元素:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UPW6sABR-1640752866055)(D:\BaiduNetdiskDownload\01_课件\images\image-20211104192912988.png)]

  • MultiPoint、MultiLineString、MultiPolygon、GeometryCollection 这4种类型都是集合类,是多个Point、LineString或Polygon组合而成。

下面展示的是多个同类或异类几何图形元素的组合:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-anQSnZON-1640752866056)(D:\BaiduNetdiskDownload\01_课件\images\image-20211104193330204.png)]

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,独立出来一张表,用主键来对应,避免影响其它字段索引效率。

第13章_约束

讲师:尚硅谷-宋红康(江湖人称:康师傅)

官网:http://www.atguigu.com


1. 约束(constraint)概述

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 语句规定约束

1.3 约束的分类

  • **根据约束数据列的限制,**约束可分为:
    • 单列约束:每个约束只约束一列
    • 多列约束:每个约束可约束多列数据
  • 根据约束的作用范围,约束可分为:
    • 列级约束:只能作用在一个列上,跟在列的定义后面
    • 表级约束:可以作用在多个列上,不与列一起,而是单独定义
			位置			支持的约束类型					是否可以起约束名
列级约束:	列的后面		语法都支持,但外键没有效果		不可以
表级约束:	所有列的下面	   默认和非空不支持,其他支持	   可以(主键没有效果)
  • 根据约束起的作用,约束可分为:
    • NOT NULL 非空约束,规定某个字段不能为空
    • UNIQUE 唯一约束规定某个字段在整个表中是唯一的
    • PRIMARY KEY 主键(非空且唯一)约束
    • FOREIGN KEY 外键约束
    • CHECK 检查约束
    • DEFAULT 默认值约束

注意: MySQL不支持check约束,但可以使用check约束,而没有任何效果

  • 查看某个表已有的约束
#information_schema数据库名(系统库)
#table_constraints表名称(专门存储各个表的约束)
SELECT * FROM information_schema.table_constraints 
WHERE table_name = '表名称';

2. 非空约束

2.1 作用

限定某个字段/某列的值不允许为空

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-so3WV1Mi-1640752866056)(D:\BaiduNetdiskDownload\01_课件\images\1555426972098.png)]

2.2 关键字

NOT NULL

2.3 特点

  • 默认,所有的类型的值都可以是NULL,包括INT、FLOAT等数据类型

  • 非空约束只能出现在表对象的列上,只能某个列单独限定非空,不能组合非空

  • 一个表可以有很多列都分别限定了非空

  • 空字符串’'不等于NULL,0也不等于NULL

2.4 添加非空约束

(1)建表时

CREATE TABLE 表名称(
	字段名  数据类型,
    字段名  数据类型 NOT NULL,  
    字段名  数据类型 NOT NULL
);

举例:

CREATE TABLE emp(
id INT(10) NOT NULL,
NAME VARCHAR(20) NOT NULL,
sex CHAR NULL
);
CREATE TABLE student(
	sid int,
    sname varchar(20) not null,
    tel char(11) ,
    cardid char(18) not null
);
insert into student values(1,'张三','13710011002','110222198912032545'); #成功

insert into student values(2,'李四','13710011002',null);#身份证号为空
ERROR 1048 (23000): Column 'cardid' cannot be null

insert into student values(2,'李四',null,'110222198912032546');#成功,tel允许为空

insert into student values(3,null,null,'110222198912032547');#失败
ERROR 1048 (23000): Column 'sname' cannot be null

(2)建表后

alter table 表名称 modify 字段名 数据类型 not null;

举例:

ALTER TABLE emp
MODIFY sex VARCHAR(30) NOT NULL;
alter table student modify sname varchar(20) not null;

2.5 删除非空约束

alter table 表名称 modify 字段名 数据类型 NULL;#去掉not null,相当于修改某个非注解字段,该字段允许为空或 alter table 表名称 modify 字段名 数据类型;#去掉not null,相当于修改某个非注解字段,该字段允许为空

举例:

ALTER TABLE empMODIFY sex VARCHAR(30) NULL;
ALTER TABLE empMODIFY NAME VARCHAR(15) DEFAULT 'abc' NULL;

3. 唯一性约束

3.1 作用

用来限制某个字段/某列的值不能重复。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WLT3XaSB-1640752866057)(D:\BaiduNetdiskDownload\01_课件\images\1555427198811.png)]

3.2 关键字

UNIQUE

3.3 特点

  • 同一个表可以有多个唯一约束。
  • 唯一约束可以是某一个列的值唯一,也可以多个列组合的值唯一。
  • 唯一性约束允许列值为空。
  • 在创建唯一约束的时候,如果不给唯一约束命名,就默认和列名相同。
  • MySQL会给唯一约束的列上默认创建一个唯一索引。

3.4 添加唯一约束

(1)建表时

create table 表名称(	字段名  数据类型,    字段名  数据类型  unique,      字段名  数据类型  unique key,    字段名  数据类型);create table 表名称(	字段名  数据类型,    字段名  数据类型,      字段名  数据类型,    [constraint 约束名] unique key(字段名));

举例:

create table student(	sid int,    sname varchar(20),    tel char(11) unique,    cardid char(18) unique key);
CREATE TABLE t_course(	cid INT UNIQUE,	cname VARCHAR(100) UNIQUE,	description VARCHAR(200));
CREATE TABLE USER( id INT NOT NULL, NAME VARCHAR(25), PASSWORD VARCHAR(16), -- 使用表级约束语法 CONSTRAINT uk_name_pwd UNIQUE(NAME,PASSWORD));

表示用户名和密码组合不能重复

insert into student values(1,'张三','13710011002','101223199012015623');insert into student values(2,'李四','13710011003','101223199012015624');
mysql> select * from student;+-----+-------+-------------+--------------------+| sid | sname | tel         | cardid             |+-----+-------+-------------+--------------------+|   1 | 张三  | 13710011002 | 101223199012015623 ||   2 | 李四  | 13710011003 | 101223199012015624 |+-----+-------+-------------+--------------------+2 rows in set (0.00 sec)
insert into student values(3,'王五','13710011004','101223199012015624'); #身份证号重复ERROR 1062 (23000): Duplicate entry '101223199012015624' for key 'cardid'insert into student values(3,'王五','13710011003','101223199012015625'); ERROR 1062 (23000): Duplicate entry '13710011003' for key 'tel'

(2)建表后指定唯一键约束

#字段列表中如果是一个字段,表示该列的值唯一。如果是两个或更多个字段,那么复合唯一,即多个字段的组合是唯一的#方式1:alter table 表名称 add unique key(字段列表); 
#方式2:alter table 表名称 modify 字段名 字段类型 unique;

举例:

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

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

CREATE TABLE test_json(js json);

向表中插入JSON数据。

INSERT INTO test_json (js) VALUES ('{"name":"songhk", "age":18, "address":{"province":"beijing", "city":"beijing"}}');

查询t19表中的数据。

mysql> SELECT *    -> FROM test_json;

[外链图片转存中…(img-v5Qlm4aa-1640752866055)]

当需要检索JSON类型的字段中数据的某个具体值时,可以使用“->”和“->>”符号。

mysql> SELECT js -> '$.name' AS NAME,js -> '$.age' AS age ,js -> '$.address.province' AS province, js -> '$.address.city' AS city    -> FROM test_json;+----------+------+-----------+-----------+| NAME 
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值