Mysql-窗口函数二

文章目录

  • [1. 前百分之N的问题 排名 row_number](#1. 前百分之N的问题 排名 row_number)
    • [1.1 需求](#1.1 需求)
    • [1.2 准备工作](#1.2 准备工作)
    • [1.3 分析](#1.3 分析)
    • [1.4 实现](#1.4 实现)
  • [2. 前百分之N的问题 ntile](#2. 前百分之N的问题 ntile)
    • [2.1 介绍](#2.1 介绍)
    • [2.2 语法](#2.2 语法)
      • [2.2.1 示例](#2.2.1 示例)
      • [2.2.2 结果示例](#2.2.2 结果示例)
      • [2.2.3 注意事项](#2.2.3 注意事项)
    • [2.3 需求](#2.3 需求)
    • [2.4 分析](#2.4 分析)
    • [2.5 实现](#2.5 实现)
  • [3. 前百分之N的问题 百分比 PERCENT_RANK](#3. 前百分之N的问题 百分比 PERCENT_RANK)
    • [3.1 语法](#3.1 语法)
      • [3.1.1 示例](#3.1.1 示例)
      • [3.1.2 注意事项](#3.1.2 注意事项)
    • [3.2 需求](#3.2 需求)
    • [3.3 实现](#3.3 实现)
  • [4. 偏移函数: 求环比增长率](#4. 偏移函数: 求环比增长率)
    • [4.1 需求](#4.1 需求)
    • [4.2 语法](#4.2 语法)
    • [4.3 示例](#4.3 示例)
    • [4.4 解释](#4.4 解释)
    • [4.5 分析](#4.5 分析)
    • [4.6 实现](#4.6 实现)
    • [4.6 总结](#4.6 总结)

1. 前百分之N的问题 排名 row_number

1.1 需求

  • 用户访问次数表,包含用户编号、用户类型、访问次数。
  • 要求在剔除访问次数前20%的用户后得到每类用户的平均访问次数。

1.2 准备工作

sql 复制代码
create table user_visits (
    user_id int,
    user_type varchar(32),
    visit_count int
)
;

insert into user_visits
values
(10, 'A', 352),
(6, 'C', 209),
(7, 'C', 110),
(4, 'E', 101),
(2, 'B', 53),
(20, 'A', 53),
(11, 'C', 33),
(1, 'A', 30),
(9, 'E', 29),
(8, 'B', 6)
;

1.3 分析


1.4 实现

sql 复制代码
with t1 as(select user_id, user_type, visit_count,
       row_number() over (order by visit_count desc) as rn
from user_visits)
select distinct user_type,
       round(avg(visit_count) over(partition by user_type),1) as avg_cnt
from t1 where rn>(select count(*) from user_visits) * 0.2;

2. 前百分之N的问题 ntile

2.1 介绍

NTILE 是一个窗口函数,用于将查询结果划分为指定数量的分组,并为每个分组分配一个组号。这在分析数据时非常有用,尤其是需要对数据进行分组或均匀分配时。

2.2 语法

sql 复制代码
NTILE(num_buckets) OVER (PARTITION BY column ORDER BY column)
  • num_buckets:要将数据划分的组数(桶数)。
  • PARTITION BY column:可选,用于按指定列对数据进行分区。
  • ORDER BY column:必需,用于指定每个分区内的排序。

2.2.1 示例

假设我们有一个包含学生成绩的表 student_scores,结构如下:

准备数据

sql 复制代码
-- 创建表
CREATE TABLE students_score (
  student_id INT PRIMARY KEY,
  name VARCHAR(255),
  score INT
);

-- 插入数据
INSERT INTO students_score (student_id, name, score) VALUES
(1, 'Alicia', 85),
(2, 'Robert', 90),
(3, 'Charles', 78),
(4, 'David', 92),
(5, 'Eva', 88);

将这些学生按分数划分为 3 组,并查看每个学生所属的组号。

sql 复制代码
SELECT
    student_id,
    name,
    score,
    NTILE(3) OVER (ORDER BY score DESC) AS group_number
FROM
    students_score;

2.2.2 结果示例

在这个例子中,NTILE(3) 将数据划分为 3 组,并根据分数的降序排序为每个学生分配一个组号。前两个最高分的学生被分配到第一组(组号1),接下来的两个学生被分配到第二组(组号2),而分数最低的学生被分配到第三组(组号3)。

2.2.3 注意事项

  • 如果不能均匀分配组,则较小编号的组可能会多出一行。例如,如果有 10 行数据和 3 组,则前两个组将有 4 行数据,最后一个组将有 2 行数据。
  • NTILE 通常用于数据分析场景,例如分配排名、分层抽样等。

2.3 需求

  • 用户访问次数表,包含用户编号、用户类型、访问次数。
  • 要求在剔除访问次数前20%的用户后得到每类用户的平均访问次数。

2.4 分析

2.5 实现

sql 复制代码
with t1 as (
    select
        user_id, user_type, visit_count,
        ntile(10) over (order by visit_count desc) as nt
    from user_visits
)
select
    user_type,
    round(avg(visit_count), 1) as avg_visit
from t1
where t1.nt>2
group by user_type
order by user_type;

3. 前百分之N的问题 百分比 PERCENT_RANK

3.1 语法

PERCENT_RANK() 是 SQL 中的窗口函数,用于计算某行的百分比排名。这个函数在数据分析中常用于了解某一数据点在整体数据中的相对位置。百分比排名的取值范围是从 0 到 1,表示当前行在分区内的排名相对于分区内其他行的百分比位置。

sql 复制代码
PERCENT_RANK() OVER (PARTITION BY column ORDER BY column)
  • PARTITION BY column:可选,用于按指定列对数据进行分区。
  • ORDER BY column:必需,用于指定每个分区内的排序。
    PERCENT_RANK() 的计算方式是:
sql 复制代码
百分比排名 = (当前行的排名 - 1) / (分区内的行总数 - 1)

3.1.1 示例

假设我们有一个包含学生成绩的表 students_score,结构如下:

计算每个学生的百分比排名。

sql 复制代码
SELECT
    student_id,
    name,
    score,
    PERCENT_RANK() OVER (ORDER BY score DESC) as `percent_rank`
FROM
    students_score;

结果

3.1.2 注意事项

  • 排序顺序:PERCENT_RANK() 的计算依赖于 ORDER BY 子句指定的排序顺序。在上面的例子中,我们按照 score 降序排列,最高分的学生的 percent_rank 是 0。

  • 分区:如果使用了 PARTITION BY 子句,则 PERCENT_RANK() 会在每个分区内计算百分比排名,而不是在整个结果集上。

  • 相同值:在处理相同值时,PERCENT_RANK() 会为相同值分配相同的排名百分比。

  • 第一行和最后一行:第一行的 percent_rank 总是 0,而最后一行的 percent_rank 总是 1。

PERCENT_RANK() 常用于了解数据在分布中的相对位置,对于生成百分比排名或分位数分析非常有用。

3.2 需求

  • 用户访问次数表,包含用户编号、用户类型、访问次数。
  • 要求在剔除访问次数前20%的用户后得到每类用户的平均访问次数。

3.3 实现

sql 复制代码
with t1 as (
    select
        user_id, user_type, visit_count,
        percent_rank() over (order by visit_count desc) as pr
    from user_visits
)
select
    user_type,
    round(avg(visit_count), 1) as avg_visit
from t1
where pr>0.2
group by user_type
order by user_type;

4. 偏移函数: 求环比增长率

4.1 需求

假设有一个销售数据表 sales,其中记录了每个月的销售额,想要计算每个月的销售额与上个月的销售额之间的变化。

4.2 语法

LAG() 是 SQL 中的一个窗口函数,用于从当前行向上偏移指定数量的行,并返回偏移行的值。它对于访问前面的行数据而不使用自连接非常有用,特别是在时间序列数据和累积计算中。

sql 复制代码
LAG(column, offset, default) OVER (PARTITION BY partition_column ORDER BY order_column)

参数解释:

参数解释

  • column: 这是你要获取前值的列。
  • offset: 向上偏移的行数。默认为1,表示返回前一行的值。
  • default: 当偏移行超出窗口范围时返回的默认值。如果没有指定,默认值为 NULL。
  • PARTITION BY partition_column: 可选项,用于指定分区列。在每个分区内独立计算 LAG 值。如果没有指定,整个结果集将视为一个分区。
  • ORDER BY order_column: 必需项,用于指定窗口函数处理数据的顺序。

4.3 示例

假设有一个包含月份销售数据的表 monthly_sales,其结构如下:

获取每个月的销售额,以及与前一个月销售额的差异。

sql 复制代码
SELECT
    month,
    sales,
    LAG(sales, 1) OVER (ORDER BY month) AS previous_sales,
    LAG(sales, 1, 666) OVER (ORDER BY month) AS previous_sales_2
FROM sales_data;

结果:

4.4 解释

  • LAG(sales, 1, 0) OVER (ORDER BY month): 该表达式获取前一个月的销售额。如果没有前一个月的数据(例如对于第一行),则返回默认值 0。
  • sales - previous_sales: 计算当前月与上月的销售额差异。

4.5 分析

4.6 实现

sql 复制代码
drop database if exists db_1;
create database db_1;
use db_1;

-- 创建表
CREATE TABLE sales_data (
    month VARCHAR(7) NOT NULL,
    sales INT NOT NULL,
    PRIMARY KEY (month)
);

-- 插入数据
INSERT INTO sales_data (month, sales) VALUES
('2023-01', 1000),
('2023-02', 1100),
('2023-03', 1050),
('2023-04', 1200),
('2023-05', 1150);

select * from sales_data;

# 目标: 求环比增长率 = (当前月销量 - 上一月销量) / 上一月销量 * 100
select
    month,
    sales,
    lag(sales, 1, 0) over(order by month) as lag_1_sales,
    # sales - (lag(sales, 1) over(order by month)) as diff,
    # (sales - (lag(sales, 1) over(order by month))) / (lag(sales, 1) over(order by month)) * 100 as rate,
    # round((sales - (lag(sales, 1) over(order by month))) / (lag(sales, 1) over(order by month)) * 100, 2) as rate_2,
    concat(round((sales - (lag(sales, 1) over(order by month))) / (lag(sales, 1) over(order by month)) * 100, 1), '%') as rate_3
from sales_data;

4.6 总结

- LAG(字段, [N], [M]):返回分区中当前行前第N行的指定字段的内容,如果没有,默认返回M

- LEAD(字段, [N], [M]):返回分区中当前行后第N行的指定字段的内容,如果没有,默认返回M

  • first_val(...)
  • last_val(...)
  • 注意:M和N可以省略,N默认为1,M默认为NULL。
相关推荐
Yaml44 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
追风林4 小时前
mac 本地docker-mysql主从复制部署
mysql·macos·docker
Hsu_kk6 小时前
MySQL 批量删除海量数据的几种方法
数据库·mysql
编程学无止境6 小时前
第02章 MySQL环境搭建
数据库·mysql
knight-n6 小时前
MYSQL库的操作
数据库·mysql
eternal__day8 小时前
MySQL_聚合函数&分组查询
数据库·mysql
帅得不敢出门8 小时前
安卓设备adb执行AT指令控制电话卡
android·adb·sim卡·at指令·电话卡
咕哧普拉啦8 小时前
乐尚代驾十订单支付seata、rabbitmq异步消息、redisson延迟队列
java·spring boot·mysql·spring·maven·乐尚代驾·java最新项目
春哥的魔法书10 小时前
数据库基础(5) . DCL
数据库·mysql
我又来搬代码了10 小时前
【Android】使用productFlavors构建多个变体
android