MySQL基础,基础select篇【下】

3.5 多行函数

3.5.1 常用聚合函数

  • 所谓聚合函数,也就是多行函数,即多行记录作为参数进行执行来获得结果
  • min(), max():求最大最小,不包含null
  • sum(), avg():求和与平均,不包含null
  • count():计数
    • count(*):返回记录的总数,包含null
    • count(“列名”):返回记录中,某列不为空的记录,不包含null
mysql> select count(*) from employees;	 # 包含null为107行
+----------+
| count(*) |
+----------+
|      107 |
+----------+

mysql> select count(commission_pct) from employees;	# commission_pct 不包含null为35行
+-----------------------+
| count(commission_pct) |
+-----------------------+
|                    35 |
+-----------------------+

# avg不包含null行,其他也一样
mysql> select avg(commission_pct) from employees;
+---------------------+
| avg(commission_pct) |
+---------------------+
|            0.222857 |
+---------------------+

mysql> select avg(commission_pct) from employees where commission_pct is not null;
+---------------------+
| avg(commission_pct) |
+---------------------+
|            0.222857 |
+---------------------+

3.5.2 group by

  • group by关键字,用于数据进行分组,并利用聚合函数求各个组的属性

  • 基本语法

    select 列名1, group_func(列名2)		# 聚合函数
    from 表名
    [where ...]			
    group by 列名1 [with rollup]
    [order by ... ]
    

注意:

  1. 所有在select中出现的、不在聚合函数里面的列名必须出现在group by 的字句中,这是因为一旦分为了多个组,这该列就被分散了。
  2. with rollup关键字:
    • 作用:在所有分组的结尾添加上一行记录,该记录为所有记录的总和,也就是对原表进行聚合函数的结果(见例子)
    • with rollup 不应该与 order by 关键字一起使用,因为with rollup为计算总和应该放在最后面,若利用with rollup,则会改变他的位置,失去了他的作用
  3. 例子:
# 获得不同部门的平均工资以及人数
mysql> select department_id, avg(salary), count(*)
    -> from employees
    -> group by department_id with rollup;	
+---------------+--------------+----------+
| department_id | avg(salary)  | count(*) |
+---------------+--------------+----------+
|          NULL |  7000.000000 |        1 |
|            10 |  4400.000000 |        1 |
|            20 |  9500.000000 |        2 |
|            30 |  4150.000000 |        6 |
|            40 |  6500.000000 |        1 |
|            50 |  3475.555556 |       45 |
|            60 |  5760.000000 |        5 |
|            70 | 10000.000000 |        1 |
|            80 |  8955.882353 |       34 |
|            90 | 19333.333333 |        3 |
|           100 |  8600.000000 |        6 |
|           110 | 10150.000000 |        2 |
|          NULL |  6461.682243 |      107 |		# 最后一行为总和,也就是所有人的平均工资,也是各个部门人数的总和
+---------------+--------------+----------+
13 rows in set (0.00 sec)

3.5.3 having

  • 对数据进行筛选,主要为分组数据

having 与 where

  1. 相同点:都能对数据进行筛选

  2. 区别1:两者可以利用的筛选条件不同

    • having能利用聚合函数作为筛选条件,但where只能利用一行数据作为筛选条件,

    • 因此这导致了当需要对分组数据进行筛选时,只能利用having,

      因此where在group by之前,having在group by之后,

    • 对单行数据进行筛选时,having和where都能使用

  3. 区别2:多表连接时,两者对数据的筛选流程不一样

    • 明确一点:

      having可以利用聚合函数进行筛选,聚合函数是多个行之间的逻辑函数,因此前提需要存在有多个行,having才会发生作用

      where只能对单行数据进行筛选,因此存在单行数据时,where就能发生作用

    • 当对单个表进行筛选时,因为筛选对象都是一行,两者没有什么差别

    • 当需要多个表连接进行筛选时,两者的差异就体现出来了,由于having需要先有多个行才能进行操作,因此其会先连接再进行筛选,而where是筛选再连接,这就导致了where能利用较小的表相连接,效率变高,因此筛选表时用的是where

  4. 小结:

    • having能用聚合函数进行筛选,而where不能,因此分组数据的筛选都用having

    • 由于having为先连接后筛选,而where为先筛选后连接,因此对表进行筛选时,最好用where

    • 综上,where在group by之前,having在group by之后

      select from 
      where			# 先对表进行筛选	
      group by..		# 筛选完毕后进行分组,因此被筛选的数据不包含在分组数据中
      having...		# 对分完的组在进行筛选
      
  5. 例子:

# 在单行数据的查询上结果都一样
mysql> select salary from employees where salary > 3000 limit 2;
+----------+
| salary   |
+----------+
| 24000.00 |
| 17000.00 |
+----------+
mysql> select salary from employees having salary > 3000 limit 2;
+----------+
| salary   |
+----------+
| 24000.00 |
| 17000.00 |
+----------+

# 取各个最少工资少大于5000 的部门的平均值	
mysql> select department_id, AVG(salary) from employees group by department_id having min(salary) > 5000 limit 2;
+---------------+-------------+
| department_id | AVG(salary) |
+---------------+-------------+
|          NULL | 7000.000000 |
|            20 | 9500.000000 |
+---------------+-------------+
2 rows in set (0.00 sec)

# 用where报错
mysql> select department_id, AVG(salary) from employees group by department_id where min(salary) > 5000 limit 2;
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 'where min(salary) > 5000 limit 2' at line 1

3.6 select语句执行过程

3.6.1 关键字顺序结构

select ...	distinct
from t1 		# 多表查询
    join t2
    on ...
    join t3
    on ... 
where ... 			# 不含组筛选
	and \ or ...	# 运算符
group by ...		# 分组
having ...			# 组筛选
order by ... desc	 # 排序
limit n.			# 分页

3.6.2 执行顺序与原理

  • 执行顺序如下,而且每一步骤都会有个临时表,分组就会有多个表,然后select又回到了一个表
from t1 join ...		 # 先指定需要的表
where ... and \ or ...	 #  通过筛选机制获得临时表
group by ...			# 分组
having ...				# 筛选
select ... 				# 临时结果
distinct				# 去重
order by				# 结果排序
limit 					# 显示

3.7 单行函数

  • 所谓单行函数,就是处理的对象都是单行的,以每一行作为参数进行的函数

3.7.1 流程控制函数

  • 流程处理函数可以根据不同的条件,执行不同的处理流程,可以在SQL语句中实现不同的条件选择。 MySQL中的流程处理函数主要包括IF()、IFNULL()和CASE()函数。
  • if(v, v1, v2):当v为正时返回v1,反之返回v2
  • ifnull(v1, v2):若v1为null,返回v2,反之返回v1
  • case when 条件1 then 结果1 … : 相当于python中的if … elif… else
  • case v when 常数 then 结果 … : 相当于 switch v case 常数…
# 对工资进行分类
SELECT employee_id, salary, 
  CASE 
    WHEN salary > 15000 THEN '高薪'
    WHEN salary > 10000 THEN '人生赢家'
    WHEN salary > 5000 THEN '潜力股'
    ELSE '垃圾'
    END '描述'
FROM employees	
;
+-------------+----------+----------+
| employee_id | salary   | 描述     |
+-------------+----------+----------+
|         100 | 24000.00 | 高薪     |
|         101 | 17000.00 | 高薪     |
|         102 | 17000.00 | 高薪     |
|         103 |  9000.00 | 潜力股   |
|         104 |  6000.00 | 潜力股   |

# 对发货状态进分类	# 根据status的值给定不同的结果
SELECT oid,`status`, CASE `status` WHEN 1 THEN '未付款'
WHEN 2 THEN '已付款'
WHEN 3 THEN '已发货'
WHEN 4 THEN '确认收货'
ELSE '无效订单' END
FROM t_order;

3.7.2 数值函数

01) 基本函数
函数用法
abs(x)取绝对值
sign(x)或者x的符号,正数返回1,负数返回-1,0返回0
pi()获得圆周率
ceil(x),ceiling(x)向上取整,返回大于或等于x的最小整数
floor(x)向下取整,返回小于或等于x的最小整数
least(x1,x2…xn)返回列表中的最小值
greatest(x1,x2,…xn)返回列表中的最大值
mod(x, y)求X / y的余数
rand([x])返回0~1之间的随机数,x为随机种子
round(x, [n])四舍五入,保留n位,n默认为0sq
truncate(x, y)返回数字x截止至y位小数的结果
sqrt(x)开根号,若小于0,结果为null
  • 例子
mysql> select truncate(1.23433, 3);
+----------------------+
| truncate(1.23433, 3) |
+----------------------+
|                1.234 |
+----------------------+
1 row in set (0.00 sec)
02) 指数与对数函数
函数用法
pow(x,n),power(x,n)返回x的n次方
exp(n)返回e的n次方
ln(x), log(x)以e为底,x的对数
log10(x)以10为底,x的对数
log2(x)以2为底,x的对数

3.7.3 字符串函数

函数用法
char_length(S)返回S的字符个数
length(S)返回S的字字节个数,根据字符集的不同而不同
concat(s1, s2…sn)连接字符串
concat_ws(x, s1,s2,…sn)连接字符串,并以x为间隔
insert(s, index, len, s1)将S中第index个开始,len个长度的子串替代为S1(下标从1开始)
nullif(S1, S2)比较两个字符串,若相等则返回null,若不相等,返回S1
# 字符数为1,字节数为2(utf8)
mysql> select char_length("陈"), length("陈");
+-------------------+--------------+
| char_length("陈") | length("陈") |
+-------------------+--------------+
|                 1 |            2 |
+-------------------+--------------+

# 间隔为 “ ”
mysql> select concat_ws(" ", "陈", last_name) from employees limit 2;
+---------------------------------+
| concat_ws(" ", "陈", last_name) |
+---------------------------------+
| 陈 King                         |
| 陈 Kochhar                      |
+---------------------------------+

# 字符串不区分大小写
mysql> select nullif("陈", "陈"), nullif("chen", "CHEN"), nullif("chen","chen1");
+--------------------+------------------------+------------------------+
| nullif("陈", "陈") | nullif("chen", "CHEN") | nullif("chen","chen1") |
+--------------------+------------------------+------------------------+
| NULL               | NULL                   | chen                   |
+--------------------+------------------------+------------------------+

3.7.4 日期与时间函数

01) 获取日期与时间
函数用法
curdate(),current_date()获得当前日期(系统)
curtime()获得当前时间(系统)
now(),localtime()返回当前系统的日期和时间
utc_date()返回UTC日期(世界标准日期)
utc_time()返回UTC时间(世界标准时间)
current_timestamp(3)精确到3位毫秒数
# 世界标准时间和系统时间是不一样的
mysql> select curdate(),curtime(), now(), utc_date(), utc_time();
+------------+-----------+---------------------+------------+------------+
| curdate()  | curtime() | now()               | utc_date() | utc_time() |
+------------+-----------+---------------------+------------+------------+
| 2023-04-08 | 10:10:42  | 2023-04-08 10:10:42 | 2023-04-08 | 02:10:42   |
+------------+-----------+---------------------+------------+------------+
1 row in set (0.00 sec)
02) 获得月份,星期,天数等函数
函数(参数都为date)用法
year(),month(),day()获得年份、月份、日期
hour(),minute(),second()获得小时,分钟、秒
monthname()获得月份名称:January…
dayname()返回星期几:Monday…
quarter()返回季度:1~4
weekofyear()一年中的第几周
dayofyear()一年中的第几天
dayofmonth(), day()一月中的第几天
dayofweek()一周中的第几天
# 返回具体时间
mysql> select now(), year(now()), month(now()), day(now()), hour(now()), minute(now()), second(now());
+---------------------+-------------+--------------+------------+-------------+---------------+---------------+
| now()               | year(now()) | month(now()) | day(now()) | hour(now()) | minute(now()) | second(now()) |
+---------------------+-------------+--------------+------------+-------------+---------------+---------------+
| 2023-04-08 10:36:18 |        2023 |            4 |          8 |          10 |            36 |            18 |
+---------------------+-------------+--------------+------------+-------------+---------------+---------------+

# 返回日期
mysql> select monthname(now()), dayname(now());
+------------------+----------------+
| monthname(now()) | dayname(now()) |
+------------------+----------------+
| April            | Saturday       |
+------------------+----------------+
1 row in set (0.00 sec)

mysql> select quarter(now()), weekofyear(now()), dayofyear(now()), dayofmonth(now()), dayofweek(now());
+----------------+-------------------+------------------+-------------------+------------------+
| quarter(now()) | weekofyear(now()) | dayofyear(now()) | dayofmonth(now()) | dayofweek(now()) |
+----------------+-------------------+------------------+-------------------+------------------+
|              2 |                14 |               98 |                 8 |                7 |
+----------------+-------------------+------------------+-------------------+------------------+

3.8 子查询

  • 所谓子查询,就是在一个查询中嵌套这一个查询语句,也就内查询

  • 根据子查询的结果返回记录的条数可以分为:单行子查询、多行子查询

    根据子查询执行的次数可以分为:相关子查询、不相关子查询

  • 内查询的结果往往用于外查询的做条件判断,因此与条件判断相关的关键字都可以进行子查询:case [v] when,where, having

3.8.1 单行子查询

  • 主要操作符有: =,>,>=,<,<=,<>也就是比较运算符
01) where 中的子查询
  • 最常用的子查询位置
  • 例子:返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资
# 子查询方式
select last_name, job_id, salary
from employees
where job_id = (
	select job_id from employees
    where employees_id = 141	# 号数为141号
) and
salary > (
	select salary from employees
    where employees_id = 143
);

# 自连接方式
select e1.last_name, e1.job_id, e1.salary
from employees as e1 join employees as e2
on e2.employee_id in (141, 143) 
and e1.job_id = e2.job_id 
and e1.salary > e2.salary2;
02) having中的子查询
#  查询最低工资大于50号部门最低工资的部门id和其最低工资
sel

3.8.2 多行子查询

  • 主要操作符号有:in、any,all,some(也就是any)
  • 例子:查看平均工资最少的部门id
# 思路:先获得各个部门的平均工资,再获得工资中的最小值
SELECT MIN(e.avg_salary)
FROM 
	(SELECT AVG(salary) AS avg_salary
	FROM employees
	GROUP BY department_id
	) AS e;

3.8.3 相关子查询

  • 如果子查询的执行依赖于外部查询,通常情况下都是因为子查询中的表用到了外部的表,并进行了条件 关联,因此每执行一次外部查询,子查询都要重新计算一次,这样的子查询就称之为 关联子查询 。
01) 在where中进行子查询
  • 例子:查询员工中工资大于本部门平均工资的员工的last_name,salary和其department_id
select last_name, salary, department_id
from employees out_table
where out_table.salary > 
(select avg(salary)
from employees in_table
where out_table.employee_id =  in_table.employee_id 	# 调用了外部表
group by department_id 
)
# 相关子查询的方法比较慢
SELECT last_name, salary, department_id, avg_sal
FROM 
(SELECT last_name, salary, department_id ,
	AVG(salary) over(PARTITION BY department_id) AS 'avg_sal'	# 利用了窗口函数使查询变为了非相关子查询
FROM employees) AS new_table
WHERE salary > avg_sal;
02) 在from中使用子查询
  • 例子:查询员工中工资大于本部门平均工资的员工的last_name,salary和其department_id
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`;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值