分析函数
OVER 子句
语法
<analytic_function> OVER (
PARTITION BY <column>
ORDER BY <column> ASC|DESC
<window_frame>) <window_column_alias>
PARTITION BY、ORDER BY 和 window_frame 都是可选的。
PARTITION BY
create table t(
month MONTH,
city STRING,
sold INT
);
go
insert into t values(2023.06M, "A", 1200);
insert into t values(2023.07M, "A", 1500);
insert into t values(2023.08M, "B", 1200);
insert into t values(2023.07M, "B", 1300);
insert into t values(2023.07M, "C", 1300);
insert into t values(2023.06M, "C", 1400);
insert into t values(2023.08M, "A", 1400);
insert into t values(2023.06M, "B", 1000);
insert into t values(2023.08M, "C", 1100);
SELECT
month,
city,
sum(sold) OVER (PARTITION BY city) AS sum
FROM t;
当对分布式表应用分析函数时,若 PARTITION BY 后的列是分区列,则在每个分区内部执行分析函数;否则,需要把整个表数据读取到内存中重新分区后执行分析函数。此时若分区表的数据量比较大,性能会比较差。
ORDER BY
ORDER BY 子句指定应用分析函数的每个分组中的行顺序。如果没有指定 ORDER BY 子句,则分组内的行是无序的。如果指定了 ORDER BY 子句,分组中的数据会按照 ORDER BY 子句指定的列排序后再应用分析函数。
SELECT month, city, rank() OVER (PARTITION BY city ORDER BY month) AS rank FROM t;
根据上面两个例子的结果,我们可以观察到,无论是否指定 ORDER BY,DolphinDB 输出的结果都会保持原表的顺序。
窗口帧(<window_frame>)
窗口帧定义
DolphinDB 支持通过 ROWS 和 RANGE 定义窗口帧。
ROWS|RANGE BETWEEN lower_bound AND upper_bound
BETWEEN ... AND ... 指在... 之间,下限(lower bound)必须位于上限(upper bound)之前。边界可以是5个选项中的任意一个:
-
UNBOUNDED PRECEDING:当前行之前所有行
-
n PRECEDING:当前行之前的 n 行。n 填入具体数字,如 5 PRECEDING
-
CURRENT ROW:仅当前行
-
n FOLLOWING:当前行之后的 n 行。n 填入具体数字,如 5 FOLLOWING
-
UNBOUNDED FOLLOWING:当前行之后所有行
其中,选项中的 n 必须是整数,不能是 duration。
ROWS 根据行号来确定窗口的范围。例如,ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING 表示当前行的前1行和后1行,共3行。
在定义窗口帧的边界时,支持使用以下缩写:
-
ROWS | RANGE UNBOUNDED PRECEDING等价于ROWS | RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW -
ROWS | RANGE n PRECEDING等价于ROWS | RANGE BETWEEN n PRECEDING AND CURRENT ROW -
ROWS | RANGE CURRENT ROW等价于ROWS | RANGE BETWEEN CURRENT ROW AND CURRENT ROW
默认窗口帧
如果没有指定 <window_frame> 子句,则默认窗口帧取决于是否使用 ORDER BY 子句。
-
如果使用 ORDER BY,则默认窗口帧为
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW -
如果不使用 ORDER BY,则默认窗口帧为
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
由于默认窗口帧根据是否使用 ORDER BY 子句而有所不同,因此是否使用 ORDER BY 子句也可能影响到查询结果。例如,sum 的计算结果可能会改变。因此建议在查询语句中明确指定窗口帧。
-
RANGE 搭配 ORDER BY 时,ORDER BY 后面的列字段只能是整型或时间类型。
分析函数(<analytic_function>)
DolphinDB 支持以下几类分析函数,需要注意的是,在 DolphinDB 中,应用分析函数后的输出结果的顺序与原表的顺序保持一致。
|
函数名 |
说明 |
| sum | 行的总和。 |
| avg | 行的平均值。 |
| std | 计算标准差。 |
| stdp | 计算总体标准差。 |
| var | 计算方差。 |
| varp | 计算总体方差。 |
| count | 行的计数。 |
| min | 最小值。 |
| max | 最大值。 |
| skew | 倾斜度。 |
| kurtosis | 峰度。 |
| med | 中值。 |
| row_number | 分组内每行的唯一编号,从1开始。 |
| rank | 某个值在分组内的排名,从1开始。 |
| dense_rank | 某个值在分组内的密集排名,从1开始。 |
| percent_rank | 某行的百分位排名,取值范围为[0,1]。 |
| cume_dist | 某个值在分组中的累积分布。如果按升序排列,则统计行值小于等于当前行值的行数除以总行数。如果按降序排列,则统计行值大于等于当前值的行数除以总行数。取值范围为(0,1]。 |
| lead(expr, offset=1, default=NULL) | 当前行之后的行偏移值。offset 和 default 可选。不支持窗口帧。 |
| lag(expr, offset=1, default=NULL) | 当前行之前的行偏移值。offset 和 default 可选。不支持窗口帧。 |
| ntile | 将分组内的行尽可能均匀地分为 n 组,并为每行分配组号。当不使用 ORDER BY 子句时,输出表字段随机。不支持窗口帧。 |
| first_value | 第一行的值。 |
| last_value | 最后一行的值。 |
| nth_value | 第 N 行的值。 |
例子
create table t(
id INT,
sym SYMBOL,
volume INT
);
insert into t values(1, `R, 200);
insert into t values(2, `P, 500);
insert into t values(1, `P, 100);
insert into t values(1, `P, 300);
insert into t values(2, `R, 300);
insert into t values(2, `P, 400);
insert into t values(3, `R, 400);
select * from t;
id sym volume
1 R 200
2 P 500
1 P 100
1 P 300
2 R 300
2 P 400
3 R 400
按 sym 分组后,获取每组内由当前行的前1行和后2行确定的窗口帧内的 volume 的总和。
select
*,
sum(volume) over (
partition by sym
rows between 1 preceding and 2 following)
from
t;
id sym volume sum
1 R 200 900
2 P 500 900
1 P 100 1,300
1 P 300 800
2 R 300 900
2 P 400 700
3 R 400 700
按 sym 分组并按 volume 升序排序后,获取每组内当前行的百分位排名。
select
*,
percent_rank() over (
partition by sym
order by volume)
from
t;
id sym volume percent_rank
1 R 200 0
2 P 500 1
1 P 100 0
1 P 300 0.3333
2 R 300 0.5
2 P 400 0.6667
3 R 400 1
获取分组内每行根据 volume 的排名。如果不使用 order by 子句,则所有排名一样。如果指定 order by 子句,则会根据 order by 后的列进行排名,默认是升序。
// 不使用 order by
select
*,
rank() over (
partition by sym)
from
t;
id sym volume rank
1 R 200 1
2 P 500 1
1 P 100 1
1 P 300 1
2 R 300 1
2 P 400 1
3 R 400 1
// 使用 order by
select
*,
rank() over (
partition by sym
order by volume)
from
t;
id sym volume rank
1 R 200 1
2 P 500 4
1 P 100 1
1 P 300 2
2 R 300 2
2 P 400 3
3 R 400 3
按 sym 分组按 id 降序排序后,在由当前行的前2行到当前行组成的窗口内,获取第一行的值。
select
*,
first_value(volume) OVER(
partition by sym
ORDER BY id DESC
rows 2 preceding)
from
t;
id sym volume first_value
1 R 200 400
2 P 500 500
1 P 100 500
1 P 300 400
2 R 300 400
2 P 400 500
3 R 400 400
对分布式表应用分析函数。
t = table(1 2 1 1 2 2 3 as id, `R`P`L`P`R`L`R as sym, 200 500 100 300 300 400 400 as volume) db_name = "dfs://window_function" if (existsDatabase(db_name)) { dropDatabase(db_name) } db = database(db_name, HASH, [INT, 2], , 'TSDB') pt = db.createPartitionedTable(t, "pt", "id", ,"volume") pt.append!(t) // 按 sym 列分组后,按 id 按升序排序后,获取 volume 当前行后面第1行的值,若没有对应行,则填充-1。 select *, lead(volume, 1, -1) OVER( PARTITION BY sym ORDER BY id) from t; id sym volume lead 1 R 200 300 2 P 500 -1 1 L 100 400 1 P 300 500 2 R 300 400 2 L 400 -1 3 R 400 -1
