MySQL8新特性

MySQL8新特性 -- pd的MySQL笔记

文章目录

MySQL8是MySQL数据库的一个重要版本,于2018年4月19日正式发布,由Oracle公司主导开发。它是MySQL5.7(2015年发布)之后的一个重大升级版本跳过了6.x和7.x版本号,标志着架构、功能和性能的全面革新。

事务性数据字典

MySQL8 的事务性数据字典是相比 MySQL 5.7 的核心改进之一,彻底改变了元数据(如表结构、视图、索引等)的存储和管理方式。

数据库的元数据包含表结构、视图、索引等等数据,在MySQL 5.7之前,元数据存储在文件系统中,这样的存储方式会导致DDL操作不支持事务,即无法做到批量的DDL操作要么完全执行,要么完全不执行。

示例:5.7没有事务,即使没有t2表 也会删除t1表

sql 复制代码
create database testdb;
use testdb;
create tabel t1(c1 int);
show tables;
drop table t1,t2;

如果是mysql8 那么最后的删除都会失败,t1仍然存在

而MySQL8之后,采用了InnoDB表的方式存储元数据,这种方式支持事务处理,设计目标是为处理大容量数据发挥最大化性能。所以MySQL8的DDL操作是支持事务的。我们再使用MySQL8执行同样的操作:

默认字符集和排序规则

MySQL 8.0 将默认字符集从 MySQL 5.7 的 latin1 更改为 utf8mb4 ,并将默认排序规则从 latin1_swedish_ci 更改为 utf8mb4_0900_ai_ci 。这一变化显著提升了字符集支持的范围和排序精度。

utf8mb4 是 MySQL 对 UTF-8 的实现,支持完整的 Unicode 字符集,适合存储多语言文本、Emoji 和特殊符号。包括多国语言和补充语言,如 Emoji 和罕见汉字。而 latin1 仅支持 256 个字符,无法存储 Emoji 或汉字。

而uft8的排序规则 utf8mb4_0900_ai_ci 基于 Unicode 9.0 标准,取代了 MySQL 5.7 中的uft8排序规则utf8_general_ci ,提供更精确的 Unicode 排序,比较和排序更符合语言习惯(如中文拼音、笔画排序)。

sql 复制代码
create table test_emoji(
    id int primary key,
    content varchar(100) character set utf8mb4 collate utf8mb4_0900_ai_ci
);

insert into test_emoji (id,content) value (1, '😊 你好');
select content from test_emoji where id =1;

窗口函数

窗口函数也叫分析函数,用于将查询的结果进行较为复杂的统计分析,MySQL从8.0开始支持窗口函数。

例如有个销售流水数据表,想统计今年截至当前的累计销售额,之前的mysql较难实现

窗口函数不能过滤查询结果,只能对查询结果进行分析并输出。所以窗口函数是写在SELECT之后,而不是写在WHERE之后。即:

sql 复制代码
SELECT 窗口函数 WHERE 条件;

而窗口函数的基本格式如下:

sql 复制代码
函数名(字段) over(
    partition by <要分组的字段>
    order by <排序的字段>
    rows <窗口范围>
)

如果over后面括号中什么都不写,则意味着窗口包含所有的查询结果

示例:查询每个人的销售额,并统计每个月末,每个人的销售总额(假设这个是表每一行代表一个月的销售额)

sql 复制代码
select *,
sum(s.sales_volume) over(
    partition by name -- 根据名字分组
    order by YEAR(s.statistical_date), MONTH(s.statistical_date) -- 根据月份排序
    rows between unbounded preceding and current row -- 窗口范围 从之前的所有行到当前行
) cum_volume -- 设置别名
from sales s;

接下来我们具体介绍一下rows子句的关键字和写法:

  • preceding:前面行
  • following:后面行
  • current row:当前行
  • unbounded:所有行

例如:

  • rows between 2 preceding and current row :窗口取前面两行到当前行(取近三月的数据)
  • rows between unbounded preceding and current row :窗口取之前所有行到当前行
  • rows between current row and unbounded following: 窗口当前行到之后所有行
  • rows between 3 preceding and 1 following:窗口取当前行的前面三行到后面一行,总共五行

如果子句中只有partition by没有order by和rows,窗口规范默认是所有行

如果子句中有order by没有rows,窗口规范默认是取之前所有行到当前行

聚合函数

窗口函数中的聚合函数,负责在窗口内执行聚合计算,支持窗口内累计或统计。常见的聚合函数有这几种:

  • SUM():计算窗口内值的总和
  • AVG() :计算窗口内值的平均值
  • COUNT():计算窗口内的行数
  • MIN() :找出窗口内的最小值
  • MAX():找出窗口内的最大值

接下来我们用查询语句举例聚合函数的使用:

sql 复制代码
-- 计算每个销售人员的累计销售额
select *,
sum(sales_volumn) over(
    partition by name
    order by statistical_date
) cum_volume
from sales;

-- 计算每个部门的平均销售额
select *,
avg(sales_volumn) over(
    partition by department
) dept_avg_sales
from sales;

-- 计算每个销售人员的销售记录数量
select *,
count(*) over(
    parition by name
) sales_count
from sales;

-- 找出每个销售人员的最低单月销售额
select *,
min(sales_volume) over(
    partition by name
) min_sales
from sales;

对比之前的聚合函数:

sql 复制代码
-- 找出每个销售人员的最高单月销售额
SELECT name,SUM(sales_volume) FROM sales GROUP BY name;
  • 普通场景下的聚合函数基于GROUP BY子句定义的组进行计算,将多条记录聚合为一条,只能展示分组字段和聚合函数字段;
  • 窗口函数是基于窗口定义进行计算,每条记录都会执行,而且针对于不同的记录可以设置不同的窗口范围,有几条记录执行完还是几条。并且能展示所有字段

排名函数

排名函数可以为数据集中的行分配排名或序号,常用于排序和比较场景。常见的排名函数有这几种:

  • ROW_NUMBER():为结果集中的每一行分配唯一的连续序号(1, 2, 3...),即使值相同也会分配不同序号
  • RANK():为行分配排名,相同值获得相同排名,但会跳过后续排名(如1,1,3)
  • DENSE_RANK():类似RANK()但不跳过排名数字(如1,1,2)

示例: 计算3月销售额排名

sql 复制代码
select *,
row_number() over(order by sales_volume desc) as row_num,
rank() over(order by sales_volume desc) as ran,
dense_rank() over(order by sales_volume desc) as den_ran
from sales
where month(statistical_date) = 3

结合窗口函数与普通聚合函数, 计算全年销售总额排名

sql 复制代码
SELECT user_id,username,
       SUM(sales_volume) as total_sales,
       ROW_NUMBER() OVER(ORDER BY SUM(sales_volume) DESC) as row_num,
       RANK() OVER(ORDER BY SUM(sales_volume) DESC) as ran,
       DENSE_RANK() OVER(ORDER BY SUM(sales_volume) DESC) as den_ran
FROM sales
WHERE YEAR(statistical_date) = YEAR(CURDATE())  -- 或指定年份如 2026
GROUP BY user_id
ORDER BY total_sales DESC;
  1. 使用 YEAR(statistical_date) = YEAR(CURDATE())筛选当前年份数据
  2. 使用 GROUP BY user_id按用户汇总
  3. 使用 SUM(sales_volume)计算全年总销售额
  4. 窗口函数基于汇总后的总销售额进行排名

分析函数

分析函数用于访问窗口内其他行的数据。常见的分析函数有这几种:

  • LAG(column, n, default):返回当前行前 n 行的 column 列的值,若没有则返回当前行中default列的值。
  • LEAD(column, n, default):返回当前行后 n 行的 column 值,若没有则返回当前行中default列的值。
  • FIRST_VALUE(column):返回窗口内第一行的 column 值。
  • LAST_VALUE(column):返回窗口内最后一行的 column 值。
  • NTH_VALUE(column, n):返回窗口内第 n 行的 column 值。

示例:

sql 复制代码
select
    *,
    -- 显示每个人上个月的销售额
    LAG(sales_volume, 1) over(
        partition by name
        order by statistical_date
        rows between unbounded preceding and unbounded following
    ) as prev_month_sales,
    -- 显示每个销售人员下个月的销售额
    LEAD(sales_volume, 1) over(
        partition by name
        order by statistical_date
        rows between unbounded preceding and unbounded following
    ) as next_month_sales,
    -- 显示每个销售人员第一个月的销售额
    FIRST_VALUE(sales_volume) over(
        partition by name
        order by statistical_date
        rows between unbounded preceding and unbounded following
    ) as first_month_sales,
    -- 显示每个销售人员第二个月的销售额
    NTH_VALUE(sales_volume, 2) over(
        partition by name
        order by statistical_date
        rows between unbounded preceding and unbounded following
    ) as second_month_sales,
    --  显示每个销售人员最后一个月的销售额
    LAST_VALUE(sales_volume) over(
        partition by name
        order by statistical_date
        rows between unbounded preceding and unbounded following
    ) as last_month_sales,
from sales;

分布函数

分布函数计算行在窗口内的相对位置或分布。

  • CUME_DIST():返回当前行值在窗口内的累积分布(0 < 值 ≤ 1)。
  • PERCENT_RANK():返回当前行在窗口内的百分比排名(0 ≤ 值 < 1)。

示例:计算1月每个销售人员销售额的百分比排名

sql 复制代码
select
    *,
    percent_rank() over(order by sales_volume desc) as pr,
    cume_dist() over(order by sales_volume desc) as cd
from sales
where month(statistical_date) =1;

计算全年每个销售人员的销售额百分比排名

sql 复制代码
SELECT 
    name,
    SUM(sales_volume) as total_sales,
    PERCENT_RANK() OVER(ORDER BY SUM(sales_volume) DESC) as pr,
    CUME_DIST() OVER(ORDER BY SUM(sales_volume) DESC) as cd
FROM sales
WHERE YEAR(statistical_date) = YEAR(CURDATE())  -- 或指定年份如 2026
GROUP BY name
ORDER BY total_sales DESC;

通用表表达式

通用表表达式(Common Table Expressions, CTE)是MySQL 8.0引入的一项重要功能,它极大地增强了SQL查询的可读性和灵活性。

CTE是一个临时的结果集,只在查询执行期间存在,它可以看作是一个临时表,我们经常使用CTE代替复杂的子查询语句,增强SQL的可读性。基本语法为:

sql 复制代码
WITH cte_name AS (
    SELECT ...  -- CTE查询定义
)
SELECT * FROM cte_name;  -- 主查询

示例:找出每月销售额超过该部门当月平均销售额的销售人员

sql 复制代码
-- 子查询
select *
from sales s
join(
    select
        department,
        month(statistical_date) as Month,
        avg(sales_volume) as avg_sales,
    from sales
    group by department, month(statistical_date)
) as dept_avg
on s.department = dept_avg.department and
month(s.statistical_date) = dept_avg.Month
where s.sales_volume > dept_avg.avg_sales;

-- CTE查询形式
with dept_avg as(
    select 
        department,
        month(statistical_date) as Month,
        avg(sales_volume) as avg_sales,
    from sales
    group by department, month(statistical_date)
)

select *
from sales s
join dept_avg
on s.department = dept_avg.department and
month(s.statistical_date) = dept_avg.Month
where s.sales_volume > dept_avg.avg_sales;

CTE的优势:

  1. 分步清晰:将复杂查询分解为逻辑步骤
  2. 可读性强:每个CTE块有明确的业务含义
  3. 易于维护:修改一个步骤不影响其他部分
  4. 避免重复:每个子查询只需定义一次
  5. 调试方便:可以单独测试每个CTE块

示例: 输出每个月每个人的销售额,以及该部门该月的平均销售额,以及每个人的平均销售额

sql 复制代码
with 
dept_avg as(
    select 
        department,
        month(statistical_date) as Month,
        avg(sales_volume) as avg_sales,
    from sales
    group by department, month(statistical_date)
),
emp_avg as(
    select
        name,
        avg(sales_volume) as avg_sales,
    from sales
    group by name
)

select 
    sales.*,
    dept_avg.avg_sales as dept_avg_sales,
    emp_avg.avg_sales as emp_avg_sales
from sales
left join dept_avg
on sales.department = dept_avg.department and month(sales.statistical_date) = dept_avg.Month
left join emp_avg
on sales.name = emp_avg.name

正则表达式函数

  1. REGEXP_LIKE(expr, pattern, [match_type]):检查字符串是否匹配正则表达式。
    • expr:要检查的字符串(列或常量)。
    • pattern:正则表达式。
    • match_type(可选):匹配选项
      • c:大小写敏感(默认)。
      • i:忽略大小写。
  2. REGEXP_REPLACE(expr, pattern, replacement):替换匹配正则表达式的子字符串,将字符串 expr 中匹配 pattern 的部分替换为 replacement。
  3. REGEXP_SUBSTR(expr, pattern):提取 expr 中匹配 pattern 的子字符串。
  4. REGEXP_INSTR(expr, pattern):返回 pattern 在 expr 中匹配的起始位置。
sql 复制代码
-- 筛选有效的邮箱地址
select *
from contacts
where regexp_like(email,'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$','i');

-- 将电话号码替换为同一格式
select *
    REGEXP_REPLACE(phone,'[^0-9]','');
from contacts

^[0-9]表示匹配以数字开头的字符串

\^0-9\]匹配任意单个字符,只要不是0-9

相关推荐
知识即是力量ol2 小时前
口语八股——MySQL 核心原理系列(终篇):SQL优化篇、日志与主从复制篇、高级特性篇、面试回答技巧总结
sql·mysql·面试·核心原理
shalou29012 小时前
Spring 核心技术解析【纯干货版】- Ⅶ:Spring 切面编程模块 Spring-Instrument 模块精讲
前端·数据库·spring
eWidget2 小时前
政务电子证照系统重构:如何解决跨模态数据的“一致性”与“安全合规”难题?
数据库·mongodb·kingbase·数据库平替用金仓·金仓数据库·文档数据库
芝士爱知识a3 小时前
【FinTech前沿】AlphaGBM:重塑期权交易的智能分析引擎——从原理到实践
数据结构·数据库·人工智能·alphagbm·期权
AC赳赳老秦3 小时前
2026主权AI趋势:DeepSeek搭建企业自有可控AI环境,保障数据安全实战
大数据·数据库·人工智能·python·科技·rabbitmq·deepseek
仍然.3 小时前
MYSQL---事务
数据库·mysql
king_harry3 小时前
openGauss 6.0 主备集群备份与恢复实战指南:基于 gs_probackup
数据库·opengauss·gs_probackup
ruxshui3 小时前
MySQL备份核心指南
数据库·mysql