Vue3中状态管理pinia学习(二)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: Vue3中状态管理pinia学习(二)

第06章 多表查询


1.一个案例引发的多表连接


1.1笛卡尔积(或交叉连接)的理解


笛卡尔乘积是一个数学运算。假设我有两个集合 X 和 Y,那么 X 和 Y 的笛卡尔积就是 X 和 Y 的所有可能组合,也就是第一个对象来自于 X,第二个对象来自于 Y 的所有可能。组合的个数即为两个集合中元素个数的乘积数。


SQL92中,笛卡尔积也称为交叉连接,英文是CROSS JOIN。在 SQL99 中也是使用 CROSS JOIN表示交叉连接。它的作用就是可以把任意表进行连接,即使这两张表不相关。


#查询员工姓名和所在部门名称 
SELECT last_name,department_name FROM employees,departments; 
SELECT last_name,department_name FROM employees CROSS JOIN departments;
  • 笛卡尔积的错误会在下面条件下产生
  • 省略多个表的连接条件(或关联条件)
  • 连接条件(或关联条件)无效
  • 所有表中的所有行互相连接
  • 为了避免笛卡尔积, 可以在WHERE加入有效的连接条件。


2.多表查询分类讲解


分类1:等值连接vs非等值连接


拓展1:区分重复的列名


  • 多个表中有相同列时,必须在列名之前加上表名前缀。


拓展2:表的别名


  • 使用别名可以简化查询。
  • 列名前使用表名前缀可以提高查询效率。


需要注意的是,如果我们使用了表的别名,在查询字段中、过滤条件中就只能使用别名进行代替,不能使用原有的表名,否则就会报错。


拓展3:连接多个表


  • 连接n个表,至少需要n-1个连接条件。


#非等值连接的例子
SELECT e.last_name,e.salary,j.grade_level
FROM employees e,job_grades j
#where e.`salary` between j.`lowest_sal` and j.`highest_sal`;
WHERE e.`salary` >= j.`lowest_sal` AND e.`salary` <= j.`highest_sal`;

分类2:自连接vs非自连接


#自连接的例子:
#练习:查询员工id,员工姓名及其管理者的id和姓名
SELECT emp.employee_id,emp.last_name,mgr.employee_id,mgr.last_name
FROM employees emp ,employees mgr
WHERE emp.`manager_id` = mgr.`employee_id`;


分类3:内连接  vs  外连接


  • 内连接: 合并具有同一列的两个以上的表的行, 结果集中不包含一个表与另一个表不匹配的行
  • 外连接: 两个表在连接过程中除了返回满足连接条件的行以外还返回左(或右)表中不满足条件的行 ,这种连接称为左(或右) 外连接。没有匹配的行时, 结果表中相应的列为空(NULL)。
  • 如果是左外连接,则连接条件中左边的表也称为主表,右边的表称为从表
  • 如果是右外连接,则连接条件中右边的表也称为主表,左边的表称为从表


3. SQL99语法实现多表查询


3.1基本语法


  • 使用JOIN...ON子句创建连接的语法结构:


SELECT table1.column, table2.column, table3.column
FROM table1 JOIN table2
ON table1 和 table2 的连接条件
JOIN table3
ON table2 和 table3 的连接条件


3.2内连接(INNER JOIN)的实现


  • 语法


SELECT 字段列表
FROM A表 INNER JOIN B表
ON 关联条件
WHERE 等其他子句;
SELECT last_name,department_name
FROM employees e INNER JOIN departments d
ON e.`department_id` = d.`department_id`;


3.3外连接(OUTER JOIN)的实现


3.3.1左外连接(LEFT OUTER JOIN)


  • 语法:


SELECT 字段列表
FROM A表 LEFT JOIN B表
ON 关联条件
WHERE 等其他子句;
SELECT last_name,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`;
3.3.2右外连接(RIGHT OUTER JOIN)


  • 语法:


SELECT 字段列表 
FROM A表 RIGHT JOIN B表
ON 关联条件
WHERE 等其他子句;
SELECT last_name,department_name
FROM employees e RIGHT OUTER JOIN departments d
ON e.`department_id` = d.`department_id`;


4. UNION的使用


UNION操作符


UNION 操作符返回两个查询的结果集的并集,去除重复记录。


UNION ALL操作符


UNION ALL操作符返回两个查询的结果集的并集。对于两个结果集的重复部分,不去重。


如果明确知道合并数据后的结果数据不存在重复数据,或者不需要去除重复的数据,则尽量使用UNION ALL语句,以提高数据查询的效率。


5. 7种SQL JOINS的实现


5.1 举例


# 中图:内连接
SELECT employee_id,department_name
FROM employees e JOIN departments d
ON e.`department_id` = d.`department_id`;
# 左上图:左外连接
SELECT employee_id,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`;
# 右上图:右外连接
SELECT employee_id,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`;
# 左中图:
SELECT employee_id,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE d.`department_id` IS NULL;
# 右中图:
SELECT employee_id,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE e.`department_id` IS NULL;
# 左下图:满外连接
# 方式1:左上图 UNION ALL 右中图
SELECT employee_id,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`
UNION ALL
SELECT employee_id,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE e.`department_id` IS NULL;
# 方式2:左中图 UNION ALL 右上图
SELECT employee_id,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE d.`department_id` IS NULL
UNION ALL
SELECT employee_id,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`;
# 右下图:左中图  UNION ALL 右中图
SELECT employee_id,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE d.`department_id` IS NULL
UNION ALL
SELECT employee_id,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE e.`department_id` IS NULL;
5.2 语法格式小结


  • 左中图


#实现A - A∩B
select 字段列表
from A表 left join B表
on 关联条件
where 从表关联字段 is null and 等其他子句;
  • 右中图


#实现B - A∩B
select 字段列表
from A表 right join B表
on 关联条件
where 从表关联字段 is null and 等其他子句;


  • 左下图


#实现查询结果是A∪B
#用左外的A,union 右外的B
select 字段列表
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 等其他子句
union
select 字段列表
from A表 right join B表
on 关联条件
where 从表关联字段 is null and 等其他子句


6. SQL99语法新特性


6.1自然连接


SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d
ON e.`department_id` = d.`department_id`
AND e.`manager_id` = d.`manager_id`;
# NATURAL JOIN : 它会帮你自动查询两张连接表中`所有相同的字段`,然后进行`等值连接`。
SELECT employee_id,last_name,department_name
FROM employees e NATURAL JOIN departments d;


6.2 USING连接


SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d
ON e.department_id = d.department_id;
# 指定数据表里的同名字段进行等值连接,只能配合JOIN一起使用。
SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d
USING (department_id);


【强制】超过三个表禁止 join。需要 join 的字段,数据类型保持绝对一致;多表关联查询时,保证被关联的字段需要有索引。


说明:即使双表 join 也要注意表索引、SQL 性能。


第07章 单行函数


1.数值函数


1.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


1.2角度与弧度

函数

用法

RADIANS(x)

将角度转化为弧度,其中,参数x为角度值

DEGREES(x)

将弧度转化为角度,其中,参数x为弧度值


1.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为弧度值


1.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


1.5进制间的转换

函数

用法

BIN(x)

返回x的二进制编码

HEX(x)

返回x的十六进制编码

OCT(x)

返回x的八进制编码

CONV(x,f1,f2)

返回f1进制数变成f2进制数


2.字符串函数

函数

用法

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开始的。


3.日期和时间函数


3.1获取日期、时间

函数

用法

CURDATE() ,CURRENT_DATE()

返回当前日期,只包含年、月、日

CURTIME() , CURRENT_TIME()

返回当前时间,只包含时、分、秒

NOW() / SYSDATE() / CURRENT_TIMESTAMP() / LOCALTIME() / LOCALTIMESTAMP()

返回当前系统日期和时间

UTC_DATE()

返回UTC(世界标准时间)日期

UTC_TIME()

返回UTC(世界标准时间)时间


3.2日期与时间戳的转换

函数

用法

UNIX_TIMESTAMP()

以UNIX时间戳的形式返回当前时间。SELECT UNIX_TIMESTAMP() ->1634348884

UNIX_TIMESTAMP(date)

将时间date以UNIX时间戳的形式返回。

FROM_UNIXTIME(timestamp)

将UNIX时间戳的时间转换为普通格式的时间


3.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


3.4日期的操作函数

函数

用法

EXTRACT(type FROM date)

返回指定日期中特定的部分,type指定返回的值


EXTRACT(type FROM date)函数中type的取值与含义:

type取值

含义

MICROSECOND

返回毫秒数

SECOND

返回秒数

MINUTE

返回分钟数

HOUR

返回小时数

DAY

返回天数

WEEK

返回日期在一年中的第几个星期

MONTH

返回日期在一年中的第几个月

QUARTER

返回日期在一年中的第几个季度

YEAR

返回日期的年份

SECOND_MICROSECOND

返回秒和毫秒值

MINUTE_MICROSECOND

返回分钟和毫秒值

MINUTE_SECOND

返回分钟和秒值

HOUR_MICROSECOND

返回小时和毫秒值

HOUR_SECOND

返回小时和秒值

HOUR_MINUTE

返回小时和分钟值

DAY_MICROSECOND

返回天和毫秒值

DAY_SECOND

返回天和秒值

DAY_MINUTE

返回天和分钟值

DAY_HOUR

返回天和小时

YEAR_MONTH

返回年和月


3.5时间和秒钟转换的函数

函数

用法

TIME_TO_SEC(time)

将 time 转化为秒并返回结果值。转化的公式为: 小时*3600+分钟*60+秒

SEC_TO_TIME(seconds)

将 seconds 描述转化为包含小时、分钟和秒的时间


3.6计算日期和时间的函数

函数

用法

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的取值:

间隔类型

含义

HOUR

小时

MINUTE

分钟

SECOND

YEAR

MONTH

DAY

YEAR_MONTH

年和月

DAY_HOUR

日和小时

DAY_MINUTE

日和分钟

DAY_SECOND

日和秒

HOUR_MINUTE

小时和分钟

HOUR_SECOND

小时和秒

MINUTE_SECOND

分钟和秒

函数

用法

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后的时间


3.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参数常用的格式符:

格式符

说明

格式符

说明

%Y

4位数字表示年份

%y

表示两位数字表示年份

%M

月名表示月份(January,....)

%m

两位数字表示月份(01,02,03。。。)

%b

缩写的月名(Jan.,Feb.,....)

%c

数字表示月份(1,2,3,...)

%D

英文后缀表示月中的天数(1st,2nd,3rd,...)

%d

两位数字表示月中的天数(01,02...)

%e

数字形式表示月中的天数(1,2,3,4,5.....)

%H

两位数字表示小数,24小时制(01,02..)

%h和%I

两位数字表示小时,12小时制(01,02..)

%k

数字形式的小时,24小时制(1,2,3)

%l

数字形式表示小时,12小时制(1,2,3,4....)

%i

两位数字表示分钟(00,01,02)

%S和%s

两位数字表示秒(00,01,02...)

%W

一周中的星期名称(Sunday...)

%a

一周中的星期缩写(Sun.,Mon.,Tues.,..)

%w

以数字表示周中的天数(0=Sunday,1=Monday....)

%j

以3位数字表示年中的天数(001,002...)

%U

以数字表示年中的第几周,(1,2,3。。)其中Sunday为周中第一天

%u

以数字表示年中的第几周,(1,2,3。。)其中Monday为周中第一天

%T

24小时制

%r

12小时制

%p

AM或PM

%%

表示%


GET_FORMAT函数中date_type和format_type参数取值如下:

日期类型

格式化类型

返回的格式化字符串

DATE

USA

%m.%d.%Y

DATE

JIS

%Y-%m-%d

DATE

ISO

%Y-%m-%d

DATE

EUR

%d.%m.%Y

DATE

INTERNAL

%Y%m%d

TIME

USA

%h:%i:%s %P

TIME

JIS

%H:%i:%s

TIME

ISO

%H:%i:%s

TIME

EUR

%H.%i.%s

TIME

INTERNAL

%H%i%s

DATETIME

USA

%Y-%m-%d %H.%i.%s

DATETIME

JIS

%Y-%m-%d %H:%i:%s

DATETIME

ISO

%Y-%m-%d %H:%i:%s

DATETIME

EUR

%Y-%m-%d %H.%i.%s

DATETIME

INTERNAL

%Y%m%d%H%i%s


4.流程控制函数

函数

用法

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...


5.加密与解密函数

函数

用法

PASSWORD(str)

返回字符串str的加密版本,41位长的字符串。加密结果不可逆,常用于用户的密码加密。在mysql8.0中已弃用。

MD5(str)

返回字符串str的md5加密后的值,也是一种加密方式。若参数为NULL,则会返回NULL

SHA(str)

从原明文密码str计算并返回加密后的密码字符串,当参数为NULL时,返回NULL。 SHA加密算法比MD5更加安全 。

ENCODE(value,password_seed)

返回使用password_seed作为加密密码加密value,在mysql8.0中已弃用。

DECODE(value,password_seed)

返回使用password_seed作为加密密码解密value,在mysql8.0中已弃用。


6. MySQL信息函数

函数

用法

VERSION()

返回当前MySQL的版本号

CONNECTION_ID()

返回当前MySQL服务器的连接数

DATABASE(),SCHEMA()

返回MySQL命令行当前所在的数据库

USER(),CURRENT_USER()、SYSTEM_USER(),SESSION_USER()

返回当前连接MySQL的用户名,返回结果格式为“主机名@用户名”

CHARSET(value)

返回字符串value自变量的字符集

COLLATION(value)

返回字符串value的比较规则


7.其他函数

函数

用法

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


第08章 聚合函数


1.聚合函数介绍


1.1 AVG和SUM函数


可以对数值型数据使用AVG 和 SUM 函数。


mysql> SELECT AVG(salary), MAX(salary),MIN(salary), SUM(salary)
    -> FROM employees
    -> WHERE job_id LIKE '%REP%';
+-------------+-------------+-------------+-------------+
| AVG(salary) | MAX(salary) | MIN(salary) | SUM(salary) |
+-------------+-------------+-------------+-------------+
| 8272.727273 |    11500.00 |     6000.00 |   273000.00 |
+-------------+-------------+-------------+-------------+


1.2 MIN和MAX函数


可以对任意数据类型的数据使用 MIN 和 MAX 函数。


mysql> SELECT MIN(hire_date), MAX(hire_date)
    -> FROM employees;
+----------------+----------------+
| MIN(hire_date) | MAX(hire_date) |
+----------------+----------------+
| 1987-06-17     | 2000-04-21     |
+----------------+----------------+
1.3 COUNT函数


  • COUNT(*)返回表中记录总数,适用于任意数据类型


mysql> SELECT COUNT(*)
    -> FROM employees
    -> WHERE department_id = 50;
+----------+
| COUNT(*) |
+----------+
|       45 |
+----------+
  • COUNT(expr) 返回expr不为NULL的记录总数。


mysql> SELECT COUNT(commission_pct)
    -> FROM employees
    -> WHERE department_id = 50;
+-----------------------+
| COUNT(commission_pct) |
+-----------------------+
|                     0 |
+-----------------------+


  • 问题:用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基本使用


可以使用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;

包含在 GROUP BY 子句中的列不必包含在SELECT 列表中

SELECT AVG(salary) 
FROM employees
GROUP BY department_id;

2.2使用多个列分组


SELECT department_id dept_id, job_id, SUM(salary)
FROM employees
GROUP BY department_id, job_id;


2.3 GROUP BY中使用WITH ROLLUP


使用WITH ROLLUP关键字之后,在所有查询出的分组记录之后增加一条记录,该记录计算查询出的所有记录的总和,即统计记录数量。


SELECT department_id,AVG(salary)
FROM employees
WHERE department_id > 80
GROUP BY department_id WITH ROLLUP;

注意:


当使用ROLLUP时,不能同时使用ORDER BY子句进行结果排序,即ROLLUP和ORDER BY是互相排斥的。


3. HAVING


3.1基本使用


SELECT department_id, MAX(salary)
FROM employees
GROUP BY department_id
HAVING MAX(salary)>10000;
  • 非法使用聚合函数 : 不能在WHERE子句中使用聚合函数。


mysql> SELECT department_id, AVG(salary)
    -> FROM employees
    -> WHERE AVG(salary) > 8000
    -> GROUP BY department_id;
ERROR 1111 (HY000): Invalid use of group function


结论:


当过滤条件中有聚合函数时,则此过滤条件必须声明在HAVING中。


当过滤条件中没有聚合函数时,则此过滤条件声明在WHERE中或HAVING中都可以。但是,建议大家声明在WHERE中。


3.2 WHERE和HAVING的对比

优点

缺点

WHERE

先筛选数据再关联,执行效率高

不能使用分组中的计算函数进行筛选

HAVING

可以使用分组中的计算函数

在最后的结果集中进行筛选,执行效率较低


4. SELECT的执行过程


4.1查询的结构


#方式1: 
SELECT ...,....,...
FROM ...,...,....
WHERE 多表的连接条件
AND 不包含组函数的过滤条件
GROUP BY ...,...
HAVING 包含组函数的过滤条件
ORDER BY ... ASC/DESC
LIMIT ...,... 
#方式2:
SELECT ...,....,...
FROM ... JOIN ...
ON 多表的连接条件
JOIN ...
ON ...
WHERE 不包含组函数的过滤条件
AND/OR 不包含组函数的过滤条件
GROUP BY ...,...
HAVING 包含组函数的过滤条件
ORDER BY ... ASC/DESC
LIMIT ...,...
#其中: 
#(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 ...,...-> ON -> (LEFT/RIGNT  JOIN) -> WHERE -> GROUP BY -> HAVING -> SELECT -> DISTINCT -> ORDER BY -> LIMIT
SELECT DISTINCT player_id, player_name, count(*) as num # 顺序 5
FROM player JOIN team ON player.team_id = team.team_id # 顺序 1
WHERE height > 1.80 # 顺序 2
GROUP BY player.team_id # 顺序 3
HAVING num > 2 # 顺序 4 
ORDER BY num DESC # 顺序 6
LIMIT 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


然后进入第三步和第四步,也就是GROUPHAVING阶段 。在这个阶段中,实际上是在虚拟表 vt2 的基础上进行分组和分组过滤,得到中间的虚拟表vt3vt4


当我们完成了条件筛选部分之后,就可以筛选表中提取的字段,也就是进入到SELECTDISTINCT阶段 。


首先在 SELECT 阶段会提取想要的字段,然后在 DISTINCT 阶段过滤掉重复的行,分别得到中间的虚拟表vt5-1vt5-2


当我们提取了想要的字段数据之后,就可以按照指定的字段进行排序,也就是ORDER BY阶段 ,得到虚拟表vt6


最后在 vt6 的基础上,取出指定行的记录,也就是LIMIT阶段 ,得到最终的结果,对应的是虚拟表vt7


当然我们在写 SELECT 语句的时候,不一定存在所有的关键字,相应的阶段就会省略。


同时因为 SQL 是一门类似英语的结构化查询语言,所以我们在写 SELECT 语句的时候,还要注意相应的关键字顺序,所谓底层运行的原理,就是我们刚才讲到的执行顺序。


第09章 子查询


1.需求分析与问题解决


1.1实际问题


题目:谁的工资比Abel高?


#方式一:
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' 
);


1.2子查询的基本使用


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


1.3子查询的分类


分类方式1:


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


分类方式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代码示例


题目:返回公司工资最少的员工的last_name,job_id和salary


SELECT last_name, job_id, salary 
FROM employees
WHERE salary = (
    SELECT MIN(salary)
    FROM employees
);
2.3 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中的子查询


题目:显示员工的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子查询中的空值问题


mysql> SELECT last_name, job_id
    -> FROM employees
    -> WHERE job_id = (
    ->     SELECT job_id
    ->     FROM employees
    ->     WHERE last_name = 'Haas'
    -> );
Empty set (0.01 sec)


子查询不返回任何行


2.6非法使用子查询


mysql> SELECT employee_id, last_name
    -> FROM employees
    -> WHERE salary = ( # 多行子查询使用单行比较符
    ->     SELECT MIN(salary)
    ->     FROM employees
    ->     GROUP BY department_id
    -> );
ERROR 1242 (21000): Subquery returns more than 1 row


3.多行子查询


3.1多行比较操作符

操作符

含义

IN

等于列表中的任意一个

ANY

需要和单行比较操作符一起使用,和子查询返回的某一个值比较

ALL

需要和单行比较操作符一起使用,和子查询返回的所有值比较

SOME

实际上是ANY的别名,作用相同,一般常使用ANY


3.2代码示例


题目:查询平均工资最低的部门id


#方式1:
SELECT department_id 
FROM employees
GROUP BY department_id
HAVING AVG(salary) = (
    SELECT MIN(avg_sal) 
    FROM (
        SELECT AVG(salary) avg_sal 
        FROM employees 
        GROUP BY department_id 
    ) dept_avg_sal 
);
#方式2:
SELECT department_id 
FROM employees 
GROUP BY department_id
HAVING AVG(salary) <= ALL (
    SELECT AVG(salary) avg_sal
    FROM employees
    GROUP BY department_id 
);


MySQL中聚合函数是不能嵌套使用的。


3.3空值问题


mysql> SELECT last_name
    -> FROM employees
    -> WHERE employee_id NOT IN (
    ->     SELECT manager_id
    ->     FROM employees
    -> );
Empty set (0.01 sec)

4.相关子查询


4.1相关子查询执行流程


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


4.2代码示例


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


方式一:相关子查询


SELECT last_name, salary, department_id
FROM employees e
WHERE salary > (
 SELECT AVG(salary)
    FROM employees
    WHERE department_id = e.department_id
);

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


SELECT last_name,salary,e1.department_id
FROM employees e1,(
    SELECT department_id,AVG(salary) dept_avg_sal
    FROM employees
    GROUP BY department_id
) e2 
WHERE e1.`department_id` = e2.department_id 
AND e2.dept_avg_sal < e1.`salary`;
在ORDER BY 中使用子查询:


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


SELECT employee_id,salary
FROM employees e
ORDER BY (
    SELECT department_name 
    FROM departments d
    WHERE e.`department_id` = d.`department_id` 
);

结论:在SELECT中,除了GROUP BY 和 LIMIT之外,其他位置都可以声明子查询!


4.3 EXISTS与NOT EXISTS关键字


  • 关联子查询通常也会和 EXISTS操作符一起来使用,用来检查在子查询中是否存在满足条件的行。
  • 如果在子查询中不存在满足条件的行:
  • 条件返回 FALSE
  • 继续在子查询中查找
  • 如果在子查询中存在满足条件的行:
  • 不在子查询中继续查找
  • 条件返回 TRUE
  • NOT EXISTS关键字表示如果不存在某种条件,则返回TRUE,否则返回FALSE。


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


方式一:


SELECT employee_id, last_name, job_id, department_id
FROM employees e1
WHERE EXISTS ( 
    SELECT * 
    FROM employees e2 
    WHERE e2.manager_id = e1.employee_id
);

方式二:自连接


SELECT DISTINCT e1.employee_id, e1.last_name, e1.job_id, e1.department_id
FROM employees e1 JOIN employees e2 
WHERE e1.employee_id = e2.manager_id;


方式三:


SELECT employee_id,last_name,job_id,department_id
FROM employees
WHERE employee_id IN ( 
    SELECT DISTINCT manager_id 
    FROM employees 
);

题目:查询departments表中,不存在于employees表中的部门的department_id和department_name


SELECT department_id,department_name
FROM departments d
WHERE NOT EXISTS (
  SELECT *
  FROM employees e
  WHERE d.`department_id` = e.`department_id`
);


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

目录
相关文章
|
1天前
|
存储 JavaScript 前端开发
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
【10月更文挑战第21天】 vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
|
1天前
|
前端开发 JavaScript
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
|
1天前
Vue3 项目的 setup 函数
【10月更文挑战第23天】setup` 函数是 Vue3 中非常重要的一个概念,掌握它的使用方法对于开发高效、灵活的 Vue3 组件至关重要。通过不断的实践和探索,你将能够更好地利用 `setup` 函数来构建优秀的 Vue3 项目。
|
5天前
|
JavaScript Java API
vue3知识点:setup
vue3知识点:setup
18 5
|
5天前
|
API
vue3知识点:reactive对比ref
vue3知识点:reactive对比ref
15 3
|
5天前
|
JavaScript API
vue3知识点:ref函数
vue3知识点:ref函数
15 2
|
5天前
|
JavaScript API
Vue3快速上手简介
Vue3快速上手简介
18 2
|
5天前
|
API
vue3知识点:reactive函数
vue3知识点:reactive函数
13 1
|
5天前
|
JavaScript 前端开发 API
vue3知识点:Vue3.0中的响应式原理和 vue2.x的响应式
vue3知识点:Vue3.0中的响应式原理和 vue2.x的响应式
11 0
|
2天前
|
数据采集 监控 JavaScript
在 Vue 项目中使用预渲染技术
【10月更文挑战第23天】在 Vue 项目中使用预渲染技术是提升 SEO 效果的有效途径之一。通过选择合适的预渲染工具,正确配置和运行预渲染操作,结合其他 SEO 策略,可以实现更好的搜索引擎优化效果。同时,需要不断地监控和优化预渲染效果,以适应不断变化的搜索引擎环境和用户需求。