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 子句中,除分组列外,其余列必须使用聚合函数(如 countsumavgmax 等);

  • 支持与 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