rollup
首发版本:3.00.6
ROLLUP 子句 / rollup 函数用于在普通 GROUP BY 分组结果的基础上,自动生成按分组层级逐级汇总的小计行与总计行,常用于报表、经营分析、统计看板等需要“明细 + 小计 + 总计”同时展示的场景。
ROLLUP 子句支持对分布式表(OLAP、TSDB、PKEY、IOTDB 引擎)进行查询,用法与内存表一致。
语法
DolphinDB 支持以下两种标准 SQL 写法:
语法一:函数形式
SELECT/EXEC group_column(s), aggregate_function(column_name)
FROM table_name
WHERE condition
GROUP BY rollup(group_column_1, group_column_2, ...);
语法二:关键字形式
SELECT/EXEC group_column(s), aggregate_function(column_name)
FROM table_name
WHERE condition
GROUP BY group_column_1, group_column_2, ... WITH ROLLUP;
其中:
-
语法一中的
rollup(...)必须使用小写; -
group_column_1, group_column_2, ...为参与分组的列; -
SELECT/EXEC 子句中,除分组列外,其余列必须使用聚合函数(如
count、sum、avg、max等); -
支持与 WHERE、HAVING、ORDER BY、LIMIT/TOP、JOIN、子查询等常见 SQL 子句组合使用。
执行语义
两种写法语义一致,都会在分组结果基础上,按照从右到左的分组层级逐级汇总。对于 GROUP BY rollup(c1, c2, ..., cn),系统会依次生成以下层级的聚合结果:
-
(c1, c2, ..., cn):最细粒度分组结果;
-
(c1, c2, ..., c(n-1)):去掉最后一个分组列后的一级小计;
-
继续向左逐级汇总;
-
():所有数据的总计。
例如:
-
GROUP BY rollup(region, product)会生成 (region, product) 级别明细、(region) 级别小计,以及总计; -
GROUP BY rollup(year, month, day)会生成 (year, month, day)、(year, month)、(year) 和总计四个层级。
在 ROLLUP 生成的汇总行中,被汇总掉的分组列会显示为 NULL。因此,结果中的 NULL 既可能表示原始数据中的空值,也可能表示某一层级的小计或总计行。解读结果时需要结合分组列的层级判断。
-
ROLLUP 子句 / rollup 函数只能出现在 GROUP BY 子句中。不支持脱离 GROUP BY 单独使用。
-
不支持将 ROLLUP 与 DolphinDB 的
groupby函数混合使用。 -
ROLLUP 会生成额外的小计 / 总计行。如果后续程序依赖结果行数或需要区分明细与汇总,应在业务逻辑中识别这些行。
-
分组列中的 NULL 值需要谨慎解读。原始数据中的空值与 ROLLUP 产生的汇总行都可能表现为 NULL。
-
分组列顺序会影响汇总层级。调整列顺序后,小计的语义也会随之改变。
例子
该示例创建了两张表:物联网告警表 iot_alarm 与金融成交表 trades。后续示例均基于这两张表进行查询。
// 物联网:设备告警明细
drop table if exists iot_alarm;
create table iot_alarm(
site STRING,
devType STRING,
deviceId STRING,
alarmType STRING,
ts TIMESTAMP
);
go;
insert into iot_alarm values ("A","Pump" ,"P-001","HIGH_TEMP", 2026.06.27T10:00:00);
insert into iot_alarm values ("A","Pump" ,"P-001","VIBRATION", 2026.06.27T10:05:00);
insert into iot_alarm values ("A","Pump" ,"P-002","VIBRATION", 2026.06.27T10:00:00);
insert into iot_alarm values ("A","Valve","V-009","LEAK" , 2026.06.27T10:00:00);
insert into iot_alarm values ("B","Pump" ,"P-010","HIGH_TEMP", 2026.06.27T11:00:00);
// 金融:逐笔成交明细
drop table if exists trades;
create table trades(
tradeDate DATE,
exch STRING,
symbol STRING,
qty LONG,
price DOUBLE
);
go;
insert into trades values (2026.06.27, "SSE" , "600000", 100, 10.00);
insert into trades values (2026.06.27, "SSE" , "600000", 200, 10.10);
insert into trades values (2026.06.27, "SSE" , "600519", 10, 1500.00);
insert into trades values (2026.06.27, "SZSE", "000001", 300, 12.00);
insert into trades values (2026.06.27, "SZSE", "000002", 100, 8.00);
例1. 下面的查询统计每台设备的告警次数,并自动生成总计行。
SELECT deviceId, count(*) as alarm_cnt
FROM iot_alarm
GROUP BY rollup(deviceId)
ORDER BY deviceId
| deviceId | alarm_cnt |
|---|---|
| 5 | |
| P-001 | 2 |
| P-002 | 1 |
| P-010 | 1 |
| V-009 | 1 |
结果可理解为两部分:
-
每个 deviceId 的告警次数;
-
deviceId = NULL 的总计行(表中第一行),表示所有设备的告警总次数。
例2. 下面的查询统计场站 A 中,不同设备类型、不同设备的告警次数,并生成设备类型小计和全表总计。
SELECT devType, deviceId, count(*) as alarm_cnt
FROM iot_alarm
WHERE site = "A"
GROUP BY rollup(devType, deviceId)
ORDER BY devType, deviceId
| devType | deviceId | alarm_cnt |
|---|---|---|
| 4 | ||
| Pump | 3 | |
| Pump | P-001 | 2 |
| Pump | P-002 | 1 |
| Valve | 1 | |
| Valve | V-009 | 1 |
该查询会返回三个层级的结果:
-
(devType, deviceId):设备类型-设备维度的明细汇总;
-
(devType, NULL):设备类型小计;
-
(NULL, NULL):总计。
例3. 下面示例按“交易所-证券代码”汇总成交额,并自动生成交易所小计和全市场总计。
SELECT exch, symbol, sum(qty * price) as turnover
FROM trades
WHERE tradeDate = 2026.06.27
GROUP BY exch, symbol WITH ROLLUP
ORDER BY exch, symbol
| exch | symbol | turnover |
|---|---|---|
| 22,420 | ||
| SSE | 18,020 | |
| SSE | 600000 | 3,020 |
| SSE | 600519 | 15,000 |
| SZSE | 4,400 | |
| SZSE | 000001 | 3,600 |
| SZSE | 000002 | 800 |
DolphinDB 支持两种等价写法:
-
GROUP BY col1, col2 WITH ROLLUP:与 MySQL 语法一致,写法更简洁。 -
GROUP BY rollup(col1, col2):与 PostgreSQL / SQL Server / Oracle 语法一致,分组列用函数包裹,可读性更强。
两种写法效果完全相同,可根据个人习惯选择。
例4. ROLLUP 的小计层级依赖分组列顺序。以下两个查询都合法,但生成的小计层级含义不同。
SELECT devType, deviceId, count(*) as alarm_cnt
FROM iot_alarm
WHERE site = "A"
GROUP BY rollup(devType, deviceId)
ORDER BY devType, deviceId
| devType | deviceId | alarm_cnt |
|---|---|---|
| 4 | ||
| Pump | 3 | |
| Pump | P-001 | 2 |
| Pump | P-002 | 1 |
| Valve | 1 | |
| Valve | V-009 | 1 |
SELECT deviceId, devType, count(*) as alarm_cnt
FROM iot_alarm
WHERE site = "A"
GROUP BY rollup(deviceId, devType)
ORDER BY deviceId, devType
| deviceId | devType | alarm_cnt |
|---|---|---|
| 4 | ||
| P-001 | 2 | |
| P-001 | Pump | 2 |
| P-002 | 1 | |
| P-002 | Pump | 1 |
| V-009 | 1 | |
| V-009 | Valve | 1 |
前者生成“按设备类型汇总”的小计((devType, NULL)),后者生成“按设备汇总”的小计((deviceId, NULL))。因此在编写报表类 SQL 时,应先明确希望保留哪个层级作为主要汇总维度,再决定分组列顺序。
例5. 下面示例先按设备类型和设备做层级汇总,再过滤掉告警次数过少的分组,并按结果排序。
SELECT devType, deviceId, count(*) as alarm_cnt
FROM iot_alarm
WHERE site = "A"
GROUP BY devType, deviceId WITH ROLLUP
HAVING count(*) >= 2
ORDER BY devType, deviceId
| devType | deviceId | alarm_cnt |
|---|---|---|
| 4 | ||
| Pump | 3 | |
| Pump | P-001 | 2 |
此写法适用于需要先汇总、再筛选有效统计结果的场景。
例6. 下面示例先按“交易所-证券代码”生成层级汇总结果,再按成交额降序排序,并使用 LIMIT 只返回成交额最高的 3 行。
SELECT exch, symbol, sum(qty * price) as turnover
FROM trades
WHERE tradeDate = 2026.06.27
GROUP BY rollup(exch, symbol)
ORDER BY turnover DESC
LIMIT 3
| exch | symbol | turnover |
|---|---|---|
| 22,420 | ||
| SSE | 18,020 | |
| SSE | 600519 | 15,000 |
也可以使用 TOP 语法实现相同的查询。
SELECT TOP 3 exch, symbol, sum(qty * price) as turnover
FROM trades
WHERE tradeDate = 2026.06.27
GROUP BY rollup(exch, symbol)
ORDER BY turnover DESC
例7. 下面示例将告警明细表与设备维表进行 JOIN,补充设备所在区域后,再按“区域-设备类型”生成明细、小计和总计。
drop table if exists device_info;
create table device_info(
deviceId STRING,
region STRING
);
go;
insert into device_info values ("P-001", "North");
insert into device_info values ("P-002", "North");
insert into device_info values ("V-009", "North");
insert into device_info values ("P-010", "South");
SELECT d.region, a.devType, count(*) as alarm_cnt
FROM iot_alarm a
JOIN device_info d ON a.deviceId = d.deviceId
GROUP BY rollup(d.region, a.devType)
ORDER BY d.region, a.devType
| region | devType | alarm_cnt |
|---|---|---|
| 5 | ||
| North | 4 | |
| North | Pump | 3 |
| North | Valve | 1 |
| South | 1 | |
| South | Pump | 1 |
例8. 下面示例先在子查询中计算每笔成交金额,再在外层查询中按“交易所-证券代码”做 ROLLUP 汇总。
SELECT exch, symbol, sum(amount) as turnover
FROM (
SELECT exch, symbol, qty * price as amount
FROM trades
WHERE tradeDate = 2026.06.27
) t
GROUP BY rollup(exch, symbol)
ORDER BY exch, symbol
| exch | symbol | turnover |
|---|---|---|
| 22,420 | ||
| SSE | 18,020 | |
| SSE | 600000 | 3,020 |
| SSE | 600519 | 15,000 |
| SZSE | 4,400 | |
| SZSE | 000001 | 3,600 |
| SZSE | 000002 | 800 |
