当前位置: 首页 > news >正文

MySQL篇(窗口函数/公用表达式(CTE))(持续更新迭代)

目录

讲解一:窗口函数

一、简介

二、常见操作

1. sum+group by常规的聚合函数操作

2. sum+窗口函数的聚合操作

三、基本语法

1. Function(arg1,..., argn)

1.1. 聚合函数

sum函数:求和

min函数 :最小值

1.2. 排序函数

1.3. 跨行函数

2. OVER [PARTITION BY <...>]

3. [ORDER BY <....>]

4. [window_expression]

四、练习题

1. 建库建表语句

2. 题目

3. 答案解析

计算每日销售额总和(分区按日期)

计算每个区域的总销售额

为每个产品计算其销售排名(按销售额降序)

计算每个区域每天的平均销售额

计算每个产品的销售累计总额

计算每个区域每个产品的销售总额

计算每个区域最近7天的平均销售额

为每个产品的销售记录添加序列号(按日期排序)

计算每个区域每天相对于前一天的销售额变化

计算每个产品的销售占比(按总销售额)

讲解二:公用表达式(CTE)

一、简介

二、语法

三、示例

四、递归 CTE

1. 简介

2. 递归成员限制

3. 示例

4. 使用递归 CTE 遍历分层数据

五、CTE 与 Derived Table

1. 在 5.6 版本中

2. 在 5.7 版本中

3. 在 8.0 版本中


讲解一:窗口函数

一、简介

窗口函数是一种SQL函数,非常适合于数据分析,其最大的特点就是:输入值是从

SELECT语句的结果集中的一行或者多行的"窗口"中获取的,也可以理解为窗口有大有

小(行数有多有少)。

通过OVER子句,窗口函数与其他的SQL函数有所区别,如果函数具有OVER子句,

则它是窗口函数。如果它缺少了OVER子句,则他就是个普通的聚合函数。

窗口函数可以简单地解释为类似于聚合函数的计算函数,但是通过GROUP BY子句组

合的常规聚合会隐去正在聚合的各个行,最终输出称为一行。但是窗口函数聚合完之

后还可以访问当前行的其他数据,并且可以将这些行的某些属性添加到结果当中去。

下面可以通过两个图来区分普通的聚合函数和窗口函数

为了更加直观的反映窗口函数和普通聚合函数的区别,让我们通过代码的形式感受一

下 ,首先让我们先添加测试数据,并查看表。

CREATE DATABASE IF NOT EXISTS EmployeeDB;
USE EmployeeDB;
CREATE TABLE Employees (EmployeeID INT AUTO_INCREMENT PRIMARY KEY,Name VARCHAR(100),DepartmentID INT,Salary DECIMAL(10, 2)
);INSERT INTO Employees (Name, DepartmentID, Salary) VALUES
('Alice', 1, 50000),
('Bob', 1, 55000),
('Charlie', 2, 60000),
('David', 1, 50000),
('Eve', 2, 65000),
('Frank', 3, 45000),
('Grace', 3, 47000),
('Hannah', 3, 48000),
('Ian', 2, 70000),
('Jack', 1, 52000);

26c12b4fbacde37b10f74d3c9036b1af.png

二、常见操作

1. sum+group by常规的聚合函数操作

select DepartmentID, sum(salary) as total
from employees
group by DepartmentID;

31d49fb04599d619e95b9d3ab2f86134.png

我们可以看的出来,常规聚合函数把id进行分组然后把每组的薪资综合计算出来放在

最后面。

2. sum+窗口函数的聚合操作

select *, sum(Salary) over (partition by DepartmentID) total
from employees

1d961c079b8eb4da8aeac289eb59233d.png

我们可以通过这两个例子看出来,聚合函数和窗口聚合函数的区别。就是窗口函数会

进行分组,但不会把行进行合并。对于每一组窗口函数返回出来的结果都会重复的放在最后面。

三、基本语法

Function(arg1,..., argn) OVER ([PARTITION BY <...>] [ORDER BY <....>]

[<window_expression>])

对于以上的窗口函数的语法[ ]中的语法是可以根据自己的需求进行选择(非必须写

入语法),并且此语法严格按照上面的顺序来规定。

Function(arg1,..., argn)是表示函数的分类,可以是下面分类中的任何一组。

  • 聚合函数,例如sum,min,avg,count等函数(常用)
  • 排序函数,例如rank row_number dense_rank()等函数(常用)
  • 跨行函数,lag lead 函数

OVER [PARTITION BY <...>] 类似于group by 用于指定分组

  • 每个分组你可以把它叫做窗口
  • 不分组的情况可以写成partition by null 或者直接不写partition by,所有列为

一个大组

  • 分组的情况下,partition by 后面可以跟多个列,例如partition by cid,cname

[ORDER BY <....>] 用于指定每个分组内的数据排序规则 支持ASC、DESC

[<window_expression>] 用于指定每个窗口中 操作的数据范围 默认是窗口中所有行

1. Function(arg1,..., argn)

通常和partition by分组使用。

当然也可以不分组使用,但也不分组使用通常没有意义。

  • 聚合函数,例如sum,min,avg,count等函数(常用)
  • 排序函数,例如rank row_number dense_rank()等函数(常用)
  • 跨行函数,lag lead 函数

1.1. 聚合函数

我们还通过上文的测试数据进行演示。我们就演示2个函数,其他的聚合类函数都是

相同的用法。

sum函数:求和

select *,sum(Salary) over (partition by DepartmentID) total
from employees

c5404f175fd7a7e1c307ca93fa70485f.png

min函数 :最小值

select *,min(Salary) over (partition by DepartmentID) total
from employees;

8a87edc856846d5989ee0f725950d7f7.png

其他的聚合函数都是同样的用法。

1.2. 排序函数

rank row_number dense_rank()等函数,通常与order by函数一起使用。

row_number()函数:对分组之后按照某些规则从高到低或者从低到高进行排序

(order by),然后打上序号,不考虑并列的情况。

select *,row_number() over (partition by DepartmentID order by Salary desc ) total
from employees;

ae409039e0b6c97aea80a57ecaf897df.png

rank()函数:对分组之后按照某些规则从高到低或者从低到高进行排序(order by),然

后打上序号,考虑并列情况并且跳跃排名,对此我们需要增添一组数据。

INSERT INTO Employees (Name, DepartmentID, Salary) VALUES('css',1,45000);

3ed7f7237fb8245abebc41e216c686fa.png

select *,rank() over (partition by DepartmentID order by Salary desc ) total
from employees;

808da29875c7ec7a9e8f1b7a6d0c37ef.png

dense_rank()函数:

select *,dense_rank() over (partition by DepartmentID order by Salary desc ) total
from employees;

23b05120f6cad51cb52fd593baf3a8f9.png

1.3. 跨行函数

LAG(col,n,DEFAULT) 用于统计窗口内往上第n行值

第一个参数为列名,第二个参数为往上第n行(可选,默认为1),第三个参数为默认

值(当往上第n行为NULL时候,取默认值,如不指定,则为NULL);

select *,lag(Salary,1) over (partition by DepartmentID order by Salary desc ) total
from employees;

31c67980df38ab3cc9007738a136e7c5.png

LEAD(col,n,DEFAULT) 用于统计窗口内往下第n行值

第一个参数为列名,第二个参数为往下第n行(可选,默认为1),第三个参数为默认

值(当往下第n行为NULL时候,取默认值,如不指定,则为NULL);

select *,lead(Salary,1) over (partition by DepartmentID order by Salary desc ) total
from employees;

66b49e9101b1438e09c199db4dec4dc9.png

FIRST_VALUE 取分组内排序后,截止到当前行,第一个值;

select *,first_value(Salary) over (partition by DepartmentID order by Salary desc ) total
from employees;

62563508c876f2933613898f7f4c2dd4.png

LAST_VALUE 取分组内排序后,截止到当前行,最后一个值;

select *,last_value(Salary) over (partition by DepartmentID order by Salary desc  ) total
from employees;

96cd6e7857023a4b17d891cf32d8aeaf.png

从这个数据我们有个疑问,为啥不是去分组内的最后一个值呢?

在这里我给大家解释一下,对于我们分的窗口(比如部门id=1)里面还有个小窗口

row函数

对于我们没有指定小窗口默认是当之前所有行到当前行,这样理解可以很抽象,我们

举个例子。对于部门id=1来说,我们从第一行来看(心里默念从之前所有行到当前

行)从之前所有行到当前行来看确实输出的值应该是55000.00,那么我们看第二行

(心里默念从之前所有行到当前行)那么确实输出的是52000.00。这样我们通过row

函数来改变一下小窗口的范围。更清晰的感受一下这个函数。

select *,last_value(Salary) over (partition by DepartmentID order by Salary descrows between unbounded preceding and unbounded following ) total
from employees;

解释一下设置小窗口的含义:rows between unbounded preceding and

unbounded following

之前所有的行到之后所有的行,那么让我们输出一下。

56312aea47c86612b9a4258268bc9cf6.png

我们可以很清晰的看出来,输出的是每一组里面最后一个的薪资。

2. OVER [PARTITION BY <...>]

over是窗口函数的标志,partition by 用来指定分组,把partition by 后面跟的字段

相同的放在一起

3. [ORDER BY <....>]

用于指定每个分组内的数据排序规则 支持ASC、DESC, 跟group by 中的order by

是一样的用法

4. [window_expression]

用于指定每个窗口中 操作的数据范围 默认是窗口中所有行

窗口子句操作的数据范围:

  1. 起始行:N preceding/unbounded preceding
  2. 当前行:currentrow
  3. 终止行:N following/unbounded following

举例:

  • rows between unbounded preceding and current row

从之前所有的行到当前行

  • rows between 2 preceding and current row

从前面两行到当前行

  • rows between current row and unbounded following

从当前行到之后所有的行

  • rows between current row and 1following

从当前行到后面一行

注意:

排序子句后面缺少窗口子句,窗口规范默认是

rows between unbounded preceding and current row

排序子句和窗口子句都缺失,窗口规范默认是

rows between unbounded preceding and unbounded following

总体流程:

  1. 通过partition by和 order by 子句确定大窗口(定义出上界unbounded preceding和下界unbounded following)
  2. 通过row 子句针对每一行数据确定小窗口(滑动窗口)
  3. 对每行的小窗口内的数据执行函数并生成新的列

四、练习题

1. 建库建表语句

CREATE DATABASE IF NOT EXISTS sales_db;
USE sales_db;CREATE TABLE IF NOT EXISTS sales (id INT AUTO_INCREMENT PRIMARY KEY,product_id INT comment '商品id',sale_date DATE comment '销售日期',amount DECIMAL(10, 2)comment '销售额',region VARCHAR(50) comment '地区'
)comment '销售';-- 插入一些示例数据
INSERT INTO sales (product_id, sale_date, amount, region) VALUES
(1, '2023-01-01', 100.00, 'East'),
(2, '2023-01-01', 150.00, 'West'),
(1, '2023-01-02', 200.00, 'East'),
(3, '2023-01-02', 120.00, 'South'),
(2, '2023-01-03', 180.00, 'West'),
(1, '2023-01-03', 220.00, 'East'),
(3, '2023-01-04', 140.00, 'South'),
(4, '2023-01-04', 300.00, 'North'),
(2, '2023-01-05', 250.00, 'West'),
(1, '2023-01-05', 280.00, 'East');
insert into sales(product_id, sale_date, amount, region) values
(1,'2023-01-01',200.00,'East');

77964503976e6063377f8c5e9a2acd80.png

2. 题目

  1. 计算每日销售额总和(分区按日期)
  2. 计算每个区域的总销售额
  3. 为每个产品计算其销售排名(按销售额降序)
  4. 计算每个区域每天的平均销售额
  5. 计算每个产品的销售累计总额
  6. 计算每个区域每个产品的销售总额
  7. 计算每个区域最近7天的平均销售额
  8. 为每个产品的销售记录添加序列号(按日期排序)
  9. 计算每个区域每天相对于前一天的销售额变化
  10. 计算每个产品的销售占比(销售额/总销售额)

自己可以先把这些最基础的窗口函数做完之后,再看下面的解析

3. 答案解析

计算每日销售额总和(分区按日期)

# 计算每日销售额总和(分区按日期)
SELECT *,sale_date, SUM(amount) OVER (PARTITION BY sale_date order by sale_date) AS total_daily_sales
FROM sales;

32a308ecf242c0946df76f88805855b5.png

计算每个区域的总销售额

# 计算每个区域的总销售额
SELECT region, SUM(amount) OVER (PARTITION BY region) AS total_regional_sales
FROM sales;

88d75881f9bc0851af30619ab6f656e8.png

为每个产品计算其销售排名(按销售额降序)

# 为每个产品计算其销售排名(按销售额降序)
select product_id,rank() over (order by sum(amount) desc )as '销售排名' from sales group by product_id ;

83d4c49e334dc9dea8a5e282294be5f5.png

计算每个区域每天的平均销售额

# 计算每个区域每天的平均销售额
select *,avg(amount)over (partition by region,sale_date rows between unbounded preceding and unbounded following) from sales;

031918e662870d9b1230cb3234cda3b0.png

计算每个产品的销售累计总额

# 计算每个产品的销售累计总额
select *,sum(amount)over (partition by product_id) from sales;

3bbe06979585e418d613c44f3e184b05.png

计算每个区域每个产品的销售总额

# 计算每个区域每个产品的销售总额
select *,sum(amount)over (partition by product_id,region)from sales;

cb546e729b38a256a5aa422d5d1c2d79.png

计算每个区域最近7天的平均销售额

# 计算每个区域最近7天的平均销售额
with t1 as ( select *,dense_rank() over(partition by region order by sale_date)as ttime from sales )
select *,avg(amount)over(partition by region) from t1 where ttime<7;
;

6daf252942b79c7a0a5d3432ff2b74c7.png

为每个产品的销售记录添加序列号(按日期排序)

# 为每个产品的销售记录添加序列号(按日期排序)
select *,dense_rank() over (partition by product_id order by sale_date)from sales;

8b57837caafeb441fcb52df45d70806b.png

计算每个区域每天相对于前一天的销售额变化

# 计算每个区域每天相对于前一天的销售额变化
SELECTa.sale_date,a.region,a.amount,a.amount - LAG(a.amount) OVER (PARTITION BY a.region ORDER BY a.sale_date) AS daily_change
FROM sales a;

8a4c20f302a9c12f0f3f0089ba8a3063.png

计算每个产品的销售占比(按总销售额)

# 计算每个产品的销售占比(按总销售额)
select *,sales.amount/sum(amount)over (partition by product_id)from sales;

47e0e054619fc56324dac510b1fcd649.png

讲解二:公用表达式(CTE)

一、简介

官网:MySQL :: MySQL 8.0 Reference Manual :: 15.2.20 WITH (Common Table Expressions)

MySQL 从 8.0 开始支持 WITH 语法,即:Common Table Expressions - CTE,公用表表达式。

CTE 是一个命名的临时结果集合,仅在单个 SQL 语句(select、insert、update 或 delete)的执行范

围内存在。

与派生表类似的是:CTE 不作为对象存储,仅在查询执行期间持续。

与派生表不同的是:CTE 可以是自引用(递归CTE),也可以在同一查询中多次引用。

此外,与派生表相比,CTE 提供了更好的可读性和性能。

二、语法

CTE 的结构包括:名称、可选列列表和定义 CTE 的查询。

定义 CTE 后,可以像 select、insert、update、delete 或 create view 语句中的视图一样使用它。

with cte_name (column_list) as (query)
select * from cte_name;

查询中的列数必须与 column_list 中的列数相同。

如果省略 column_list,CTE 将使用定义 CTE 的查询的列列表。

三、示例

初始化数据:

-- create table
create table department
(id        bigint auto_increment comment '主键ID'primary key,dept_name varchar(32)      not null comment '部门名称',parent_id bigint default 0 not null comment '父级id'
);-- insert values
insert into `department` values (null, '总部', 0);
insert into `department` values (null, '研发部', 1);
insert into `department` values (null, '测试部', 1);
insert into `department` values (null, '产品部', 1);
insert into `department` values (null, 'Java组', 2);
insert into `department` values (null, 'Python组', 2);
insert into `department` values (null, '前端组', 2);
insert into `department` values (null, '供应链测试组', 3);
insert into `department` values (null, '商城测试组', 3);
insert into `department` values (null, '供应链产品组', 4);
insert into `department` values (null, '商城产品组', 4);
insert into `department` values (null, 'Java1组', 5);
insert into `department` values (null, 'Java2组', 5);

(1)最基本的CTE语法

mysql> with cte1 as (select * from `department` where id in (1, 2)),->      cte2 as (select * from `department` where id in (2, 3))-> select *-> from cte1->          join cte2-> where cte1.id = cte2.id;
+----+-----------+-----------+----+-----------+-----------+
| id | dept_name | parent_id | id | dept_name | parent_id |
+----+-----------+-----------+----+-----------+-----------+
|  2 | 研发部     |         1 |  2 | 研发部     |         1 |
+----+-----------+-----------+----+-----------+-----------+
1 row in set (0.00 sec)

(2)一个 CTE 引用另一个 CTE

mysql> with cte1 as (select * from `department` where id = 1),->      cte2 as (select * from cte1)-> select *-> from cte2;
+----+-----------+-----------+
| id | dept_name | parent_id |
+----+-----------+-----------+
|  1 | 总部       |         0 |
+----+-----------+-----------+
1 row in set (0.00 sec)

四、递归 CTE

1. 简介

递归 CTE 是一个具有引用 CTE 名称本身的子查询的 CTE。递归 CTE 的语法为:

with recursive cte_name as (initial_query  -- anchor member
union all
recursive_query -- recursive member that references to the cte name
)
select * from cte_name;

递归 CTE 由三个主要部分组成:

  • 形成 CTE 结构的基本结果集的初始查询(initial_query),初始查询部分被称为锚成员。
  • 递归查询部分是引用 CTE 名称的查询,因此称为递归成员。递归成员由一个 union all 或 union distinct 运算符与锚成员相连。
  • 终止条件是当递归成员没有返回任何行时,确保递归停止。

递归 CTE 的执行顺序如下:

  1. 首先,将成员分为两个:锚点和递归成员。
  2. 接下来,执行锚成员形成基本结果集(R0),并使用该基本结果集进行下一次迭代。
  3. 然后,将 Ri 结果集作为输入执行递归成员,并将 Ri + 1 作为输出。
  4. 之后,重复第三步,直到递归成员返回一个空结果集,换句话说,满足终止条件。
  5. 最后,使用 union all 运算符将结果集从 R0 到 Rn 组合。

2. 递归成员限制

递归成功不能包含以下结构:

  • 聚合函数,如 max、min、sum、avg、count 等。
  • group by 子句
  • order by 子句
  • limit 子句
  • distinct

上述约束不适用于锚点成员。 另外,只有在使用 union 运算符时,要禁止 distinct 才适用。

如果使用 union distinct 运算符,则允许使用 distinct。

另外,递归成员只能在其子句中引用 CTE 名称,而不是引用任何子查询。

3. 示例

with recursive cte_count (n)as (select 1union allselect n + 1from cte_countwhere n < 3)
select n from cte_count;	

在此示例中,以下查询:

select 1

是作为基本结果集返回 1 的锚成员。

以下查询:

select n + 1
from cte_count
where n < 3

是递归成员,因为它引用了 cte_count 的 CTE 名称。递归成员中的表达式 < 3 是终止条件。

当 n 等于 3,递归成员将返回一个空集合,将停止递归。

下图显示了上述 CTE 的元素:

20230724024159.png?origin_url=images%5C106.png&pos_id=fMBiOcoP

ccc045b7c9fcbccf7a4a3265852c400c.png

递归 CTE 返回以下输出:

+------+
|    n |
+------+
|    1 |
|    2 |
|    3 |
+------+

递归 CTE 的执行步骤如下:

  1. 首先,分离锚和递归成员。
  2. 接下来,锚定成员形成初始行 select 1,因此第一次迭代在 n = 1 时产生 1 + 1 = 2。
  3. 然后,第二次迭代对第一次迭代的输出 2 进行操作,并且在 n = 2 时产生 2 + 1 = 3。
  4. 之后,在第三次操作 n = 3 之前,满足终止条件 n <3 ,因此查询停止。
  5. 最后,使用 union all 运算符组合所有结果集 1,2 和 3。

4. 使用递归 CTE 遍历分层数据

查部门 id = 2 的所有下级部门和本级:

mysql> with recursive cte_tab as (select id, dept_name, parent_id, 1 as level->                            from department->                            where id = 2->                            union all->                            select d.id, d.dept_name, d.parent_id, level + 1->                            from cte_tab c->                                     inner join department d on c.id = d.parent_id-> )-> select *-> from cte_tab;
+------+-----------+-----------+-------+
| id   | dept_name | parent_id | level |
+------+-----------+-----------+-------+
|    2 | 研发部     |         1 |     1 |
|    5 | Java组    |         2 |     2 |
|    6 | Python组  |         2 |     2 |
|    7 | 前端组     |         2 |     2 |
|   12 | Java1组   |         5 |     3 |
|   13 | Java2组   |         5 |     3 |
+------+-----------+-----------+-------+
6 rows in set (0.00 sec)

五、CTE 与 Derived Table

针对 from 子句里面的 subquery,MySQL 在不同版本中,是做过一系列的优化,接下来我们就来看看。

1. 在 5.6 版本中

MySQL 会对每一个 Derived Table 进行物化,生成一个临时表保存 Derived Table 的结果,然后利用临时表来完

成父查询的操作,具体如下:

mysql> explain-> select * from (select * from department where id <= 1000) t1 join (select * from department where id >= 990) t2 on t1.id = t2.id;
+----+-------------+------------+------------+--------+---------------+---------+---------+----------------------------+------+----------+-------------+
| id | select_type | table      | partitions | type   | possible_keys | key     | key_len | ref                        | rows | filtered | Extra       |
+----+-------------+------------+------------+--------+---------------+---------+---------+----------------------------+------+----------+-------------+
|  1 | SIMPLE      | department | NULL       | range  | PRIMARY       | PRIMARY | 8       | NULL                       |    1 |   100.00 | Using where |
|  1 | SIMPLE      | department | NULL       | eq_ref | PRIMARY       | PRIMARY | 8       | pointer_mall.department.id |    1 |   100.00 | NULL        |
+----+-------------+------------+------------+--------+---------------+---------+---------+----------------------------+------+----------+-------------+
2 rows in set, 1 warning (0.01 sec)

2. 在 5.7 版本中

MySQL 引入了 Derived Merge 新特性,允许符合条件的 Derived Table 中的子表与父查询的表进行合并,具体如下:

mysql> explain-> select * from (select * from department where id <= 1000) t1 join (select * from department where id >= 990) t2 on t1.id = t2.id;
+----+-------------+------------+------------+-------+---------------+-------------+---------+-------+---------+----------+-------------+
| id | select_type | table      | partitions | type  | possible_keys | key         | key_len | ref   | rows    | filtered | Extra       |
+----+-------------+------------+------------+-------+---------------+-------------+---------+-------+---------+----------+-------------+
|  1 | PRIMARY     | <derived2> | NULL       | ALL   | NULL          | NULL        | NULL    | NULL  |    1900 |   100.00 | NULL        |
|  1 | PRIMARY     | <derived3> | NULL       | ref   | <auto_key0>   | <auto_key0> | 8       | t1.id |    2563 |   100.00 | NULL        |
|  3 | DERIVED     | department | NULL       | range | PRIMARY       | PRIMARY     | 8       | NULL  | 4870486 |   100.00 | Using where |
|  2 | DERIVED     | department | NULL       | range | PRIMARY       | PRIMARY     | 8       | NULL  |    1900 |   100.00 | Using where |
+----+-------------+------------+------------+-------+---------------+-------------+---------+-------+---------+----------+-------------+
4 rows in set, 1 warning (0.00 sec)

3. 在 8.0 版本中

我们可以使用 CTE 实现,其执行计划也是和 Derived Table 一样

mysql> explain-> with t1 as (select * from department where id <= 1000),->      t2 as (select * from department where id >= 990)-> select * from t1 join t2 on t1.id = t2.id;
+----+-------------+------------+------------+--------+---------------+---------+---------+----------------------------+------+----------+-------------+
| id | select_type | table      | partitions | type   | possible_keys | key     | key_len | ref                        | rows | filtered | Extra       |
+----+-------------+------------+------------+--------+---------------+---------+---------+----------------------------+------+----------+-------------+
|  1 | SIMPLE      | department | NULL       | range  | PRIMARY       | PRIMARY | 8       | NULL                       |    1 |   100.00 | Using where |
|  1 | SIMPLE      | department | NULL       | eq_ref | PRIMARY       | PRIMARY | 8       | pointer_mall.department.id |    1 |   100.00 | NULL        |
+----+-------------+------------+------------+--------+---------------+---------+---------+----------------------------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

从测试结果来看,CTE 似乎是 Derived Table 的一个替代品?

其实不是的,虽然 CTE 内部优化流程与 Derived Table 类似,但是两者还是区别的,具体如下:

  1. 一个 CTE 可以引用另一个 CTE
  2. CTE 可以自引用
  3. CTE 在语句级别生成临时表,多次调用只需要执行一次,提高性能

从上面介绍可以知道,CTE 一方面可以非常方便进行 SQL 开发,另一方面也可以提升 SQL 执行效率。

相关文章:

MySQL篇(窗口函数/公用表达式(CTE))(持续更新迭代)

目录 讲解一&#xff1a;窗口函数 一、简介 二、常见操作 1. sumgroup by常规的聚合函数操作 2. sum窗口函数的聚合操作 三、基本语法 1. Function(arg1,..., argn) 1.1. 聚合函数 sum函数&#xff1a;求和 min函数 &#xff1a;最小值 1.2. 排序函数 1.3. 跨行函数…...

Jira Cloud涨价5%-20%,钉钉项目Teambition成优选替代

近日&#xff0c;Jira再次宣布涨价&#xff0c;Cloud版涨幅达到5%-20%&#xff0c;这一消息来源于Atlassian官方面向合作伙伴发布的2024年最新涨价通知。 Atlassian旗下核心产品&#xff0c;包括Jira、Confluence、JiraServiceManagement等的Cloud版本价格将有所提高&#xff…...

Python语言基础教程(下)4.0

✨博客主页&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客内容》&#xff1a;.NET、Java.测试开发、Python、Android、Go、Node、Android前端小程序等相关领域知识 &#x1f4e2;博客专栏&#xff1a; https://blog.csdn.net/m0_63815035/cat…...

【HTTP】构造HTTP请求和状态码

状态码 用于响应中&#xff0c;表示响应的结果如何 正确&#xff1f;错误&#xff1f;什么原因&#xff1f; HTTP 中的状态码都是标准约定好的 200 OK 成功了&#xff0c;一切顺利 在抓包到的响应中 404 Not Found 访问的资源&#xff08;URL 中的路径&#xff09;没找…...

Delta Lake如何使用

1. 安装 Java 确保你的系统上安装了 Java 8 或更高版本。可以通过以下命令检查 Java 是否已安装&#xff1a; java -version2. 安装 Apache Spark 下载 Spark&#xff1a; 从 Apache Spark 官方网站 下载适合的版本&#xff0c;建议下载预编译的版本&#xff08;例如&#xf…...

面试题 - parallelStream() 有什么缺点 - ForkJoinPool,它和传统的线程池(如 ThreadPoolExecutor)的区别

底层使用 ForkJoinPool &#xff0c;不同与线程池适用于连续的内存分布的数据结构&#xff0c;如数组和ArrayList()&#xff0c;并不适用于链表适用于 cpu 密集的工作&#xff0c;cpu 的核数多效率高&#xff0c;并行流能否真正提高性能&#xff0c;很大程度上取决于系统的可用…...

切换淘宝最新镜像源npm详细讲解

​ 大家好&#xff0c;我是程序员小羊&#xff01; 前言&#xff1a; 在中国大陆&#xff0c;npm&#xff08;Node Package Manager&#xff09;的默认源由于网络限制&#xff0c;速度可能较慢。为了解决这个问题&#xff0c;淘宝提供了一个镜像源&#xff0c;它同步了 npm 的…...

STM32F407单片机编程入门(十二) FreeRTOS实时操作系统详解及实战含源码

文章目录 一.概要二.什么是实时操作系统三.FreeRTOS的特性四.FreeRTOS的任务详解1.任务函数定义2.任务的创建3.任务的调度原理 五.CubeMX配置一个FreeRTOS例程1.硬件准备2.创建工程3.调试FreeRTOS任务调度 六.CubeMX工程源代码下载七.小结 一.概要 FreeRTOS是一个迷你的实时操…...

网络安全-利用 Apache Mod CGI

目录 一、环境 二、开始操作 三、总结 一、环境 蚁剑官网拉取 二、开始操作 蚁剑连接 一样终端命令不能执行 可以看到putenv已经禁用 我们开始一下&#xff0c;跳入一个新终端且可以执行命令 我们具体看一下干了什么事情 上传了一个htaccess这个文件的作用是让以后所有ant文…...

ACE之ACE_Reactor_Notify

简介 ACE_Reactor_Notify作为Reactor的实现类ACE_Reactor_Impl内部通知来唤醒eventloop 抽象 #mermaid-svg-9UguTLk5S9joDMfi {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-9UguTLk5S9joDMfi .error-icon{fill:#…...

【小沐学GIS】blender导入OpenStreetMap城市建筑(blender-osm、blosm)

文章目录 1、简介1.1 blender1.2 osm地图 2、OpenStreetMap下载方式2.1 Simple2.2 Overpass API2.3 OSM星球2.4 Geofabrik下载2.5 其他方法2.6 BBBike 3、blender-osm插件3.1 简介3.2 操作 结语 1、简介 1.1 blender https://www.blender.org/ Blender 是一款免费的开源 3D …...

数字IC设计\FPGA 职位经典笔试面试整理--语法篇 Verilog System Verilog(部分)

注&#xff1a; 资料都是基于网上一些博客分享和自己学习整理而成的 Verilog 1. 数据类型 Verilog一共有19种数据类型 基础四种数据类型&#xff1a;reg型&#xff0c;wire型&#xff0c;integer型&#xff0c;parameter型 reg型   reg类型是寄存器数据类型的关键字。寄存…...

【EtherCAT】CiA402简介

目录 1、CiA402是CANopen协议的子协议 2、CiA402是 用于驱动和运动控制的CANopen设备配置文件 3、 CiA402主要由三部分组成 4、CiA介绍 4.1、操作模式 4.2、对象字典 5、一般对象字定义 6、详细对象字定义 7、Profile position mode 8、Homing mode 9、 Position co…...

嵌入式Linux:模块化编程

目录 内核模块 模块特点 最简单的模块 内核模块的程序结构 模块加载函数 模块卸载函数 模块参数 导出符号 作者简介 内核模块 linux内核整体结构非常庞大,其包含的组件也非常多。 怎么把需要的部分包含在内核中呢? 一种办法是把所有的需要的功能都编译到内核中。…...

【两方演化博弈代码复现】:双方演化博弈的原理、概率博弈仿真、相位图、单个参数灵敏度演化

目录-基于MatLab2016b实现 一、演化博弈的原理1. 基本概念2. 参与者的策略3.演化过程 二、MATLAB 代码解读&#xff08;博弈参与主体&#xff08;双方&#xff09;策略选择的动态演化讨程&#xff09;三、MATLAB 代码解读&#xff08;博弈主体随着时间策略选择的动态演化讨程&a…...

Selenium打开浏览器后闪退问题解决

笔者这两天在做一个自动化方案&#xff0c;用来优化数据统计。其中一部分数据需要通过云上堡垒机跳转访问&#xff0c;而这个堡垒机在笔者日常使用的火狐浏览器上运行不是很正常&#xff08;表现在有些复制粘贴按钮显示不太灵敏&#xff09;。 但在Edge浏览器上基本正常&#…...

【图论】最短路应用

1135. 新年好 题目 提交记录 讨论 题解 视频讲解 MarkDown视图Copy 重庆城里有 nn 个车站&#xff0c;mm 条 双向 公路连接其中的某些车站。 每两个车站最多用一条公路连接&#xff0c;从任何一个车站出发都可以经过一条或者多条公路到达其他车站&#xff0c;但不同的…...

Spring Boot实战:使用策略模式优化商品推荐系统

在现代电子商务平台中&#xff0c;个性化的商品推荐系统是提升用户体验和增加销售额的关键。本文将通过一个Spring Boot实战项目&#xff0c;展示如何利用Java的设计模式——策略模式&#xff0c;来优化商品推荐系统。同时&#xff0c;我们将探讨Spring Boot中的一个重要特性&a…...

Navicat导入Sql文件至Mysql数据库,事务失效

Mysql 版本&#xff1a;8.0.39 Navicat 版本&#xff1a;17.x、16.x 结论&#xff1a; Navicat 导入sql文件&#xff0c;事务不会生效&#xff0c;无论怎么设置 mysql.exe 导入sql文件&#xff0c;事务生效 测试 准备一张表 name约束不能为空&#xff0c;用于测试事务失败…...

篮球运动场景物体检测系统源码分享

篮球运动场景物体检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Comp…...

从 JDK 8 到 JDK 17:Swagger 升级迁移指南

点击上方“程序猿技术大咖”&#xff0c;关注并选择“设为星标” 回复“加群”获取入群讨论资格&#xff01; 随着 Java 生态向 JDK 17 及 Jakarta EE 的演进&#xff0c;许多项目面临从 JDK 8 升级的挑战&#xff0c;其中 Swagger&#xff08;API 文档工具&#xff09;的兼容性…...

交易所系统攻坚:高并发撮合引擎与合规化金融架构设计

交易所系统攻坚&#xff1a;高并发撮合引擎与合规化金融架构设计 ——2025年数字资产交易平台的性能与合规双轮驱动 一、高并发撮合引擎&#xff1a;从微秒级延迟到百万TPS 核心架构设计 订单簿优化&#xff1a;数据结构创新&#xff1a;基于红黑树与链表混合存储&#xff0c…...

uniapp 对接腾讯云IM群公告功能

UniApp 实战&#xff1a;腾讯云IM群公告功能 一、前言 在即时通讯场景中&#xff0c;群公告是信息同步的重要渠道。本文将基于uniapp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群公告的发布、修改、历史记录查询等核心功能。 群公告的数据结构设计权限校…...

GPU集群故障分析:大型AI训练中的硬件问题与影响

GPU集群故障分析&#xff1a;大型AI训练中的硬件问题与影响 核心问题 在大型AI计算集群&#xff08;如使用上千块GPU卡训练大模型&#xff09;中&#xff1a; GPU硬件会出哪些毛病&#xff1f;这些问题发生的频率、严重程度如何&#xff1f;最终对AI训练任务有什么影响&#…...

camera功能真的那么难用吗

背景 Android开发工作过程中&#xff0c;经常需要用到camera相关能力&#xff0c;比如&#xff1a;人脸识别&#xff0c;ai识别&#xff0c;拍照预览&#xff0c;摄像头录制等等需求。都需要使用到camera&#xff0c;且需要拿到camera的预览数据。但是每次开发这块代码都比较繁…...

Global Security Market知识点总结:主经纪商业务

在全球证券市场的复杂体系中&#xff0c;主经纪商业务&#xff08;Prime Brokerage&#xff09;占据着独特且关键的位置。这一业务为大型机构投资者提供了一系列至关重要的服务&#xff0c;极大地影响着金融市场的运作与发展。 一、主经纪商业务的定义 主经纪商业务是投资银行…...

【原神 × 二叉树】角色天赋树、任务分支和圣遗物强化路径的算法秘密!

【原神 二叉树】角色天赋树、任务分支和圣遗物强化路径的算法秘密! 作者:星之辰 标签:#原神 #二叉树 #天赋树 #任务分支 #圣遗物强化 #算法科普 发布时间:2025年6月 总字数:6000+ 一、引子:提瓦特大陆的“树型奥秘” 你是否曾留意过《原神》角色面板的天赋树? 升级技能…...

Neovim - 打造一款属于自己的编辑器(一)

文章目录 前言&#xff08;劝退&#xff09;neovim 安装neovim 配置配置文件位置第一个 hello world 代码拆分 neovim 配置正式配置 neovim基础配置自定义键位Lazy 插件管理器配置tokyonight 插件配置BufferLine 插件配置自动补全括号 / 引号 插件配置 前言&#xff08;劝退&am…...

Shell 命令及运行原理 + 权限的概念(7)

文章目录 Shell 命令以及运行原理&#xff08;4-1.22.08&#xff09;Linux权限的概念1. 什么是权限2. 认识人&#xff08;普通用户&#xff0c;root用户&#xff09;以及两种用户的切换认识普通用户和root用户两种用户之间的切换指令提权 3. 文件的属性解析 权限属性指令ll显示…...

Amazing晶焱科技:电子系统产品在多次静电放电测试后的退化案例

在我们的电子设计世界里&#xff0c;ESD&#xff08;静电放电&#xff09;问题总是让人头疼。尤其是当客户面临系统失效的困境时&#xff0c;寻找一个能够彻底解决问题的方案就变得格外重要。这一次&#xff0c;我们要谈的是一个经典案例&#xff1a;电子系统产品在多次静电放电…...