← 返回 技术博客

技术文章

HENGSHI SENSE 30+高级分析函数体系全解:留存计算与窗口函数工程实现

HENGSHI SENSE 30+高级分析函数体系全解:留存计算与窗口函数工程实现

2026/05/22技术博客HENGSHI14 分钟阅读
HENGSHI高级分析函数BI衡石科技
HENGSHI SENSE 30+高级分析函数体系全解:留存计算与窗口函数工程实现

Article body

正文

导读:在数据分析领域,“留存率”与”窗口函数”是衡量用户行为和进行复杂统计的两大核心武器。然而,传统SQL在处理连续留存、时间序列累计、跨维度计算等场景时,往往需要编写大量嵌套子查询,不仅性能堪忧,更让业务分析师望而却步。HENGSHI SENSE 构建了一套覆盖30+高级分析函数的完整体系,将复杂的窗口计算、留存分析、时间序列运算封装为简洁的函数调用。本文将从工程实现角度,逐一拆解这些函数的设计思想、语法规范与实战技巧。


一、为什么需要一套”高级分析函数”体系?

在企业级BI场景中,分析师面对的数据问题远不止”求和”与”计数”。以下几个典型场景,暴露了传统SQL分析能力的不足:

业务场景传统SQL方案痛点
连续7天活跃用户多层嵌套自连接+窗口函数SQL复杂度高,性能随天数指数级下降
周期性累计增长(MTD/YTD)CASE WHEN配合日期截断代码冗长,维度切换需重写
同比/环比分析LAG函数+日期偏移日期边界处理繁琐,易出错
非时间维度留存(如按渠道)无标准方案,需自定义业务逻辑与SQL深度耦合
跨表非关联数据引用子查询或LEFT JOIN维护成本高,语义不清晰

HENGSHI SENSE 的设计哲学是:将高频的分析模式抽象为一等公民(first-class)的函数,让分析师用”声明式”的方式描述”要什么”,而非”怎么算”。这套体系分为四大阵营:

  1. 时间序列函数——处理与时间相关的连续、累计、对比、移动计算
  2. 数据处理函数——实现跨上下文计算、维度操控、数据查找
  3. 聚合函数——从基础统计到高级分布分析的完整工具箱
  4. 图表高级计算——在可视化组件中直接调用的分析能力

接下来,我们逐一深入。


二、时间序列函数:让时间分析从”手写SQL”到”函数调用”

2.1 留存与活跃计算:retention()

留存分析是用户增长领域最核心的指标之一。HENGSHI SENSE 提供了 retention() 函数,支持在时间维度下计算用户的留存与活跃情况。

函数签名:

retention(
  expression,           -- 待计算的表达式(通常为用户ID)
  date expression,      -- 日期字段
  lowExpr,              -- 留存区间的起始偏移
  upperExpr,            -- 留存区间的结束偏移
  includeStartExpr,     -- 是否包含起始日(可选)
  includeEndExpr,       -- 是否包含结束日(可选)
  excludeStartExpr,     -- 是否排除起始日(可选)
  excludeEndExpr        -- 是否排除结束日(可选)
)

实战示例:计算”注册后第1~7天留存”

-- 计算注册日在2024年1月,且在第1到第7天内有活跃行为的用户留存数
retention(
  distinct_count(user_id),
  activity_date,
  1,   -- 从第1天开始
  7,   -- 到第7天结束
  true,  -- 包含第1天
  true   -- 包含第7天
)

该函数的底层实现思路值得深究:它将传统的”自连接+条件筛选”模式,转化为一次性的分区扫描计算。在引擎层,HENGSHI SENSE 会自动识别日期分区,生成最优的执行计划,避免了对原始数据的多次扫描。

2.2 连续留存:continuous_retention()

与普通留存不同,连续留存要求用户在指定区间内每天都活跃,这对实现的要求更高。

函数签名:

continuous_retention(
  expression,       -- 待计算的表达式
  date expression,  -- 日期字段
  lowExpr,          -- 连续区间的起始偏移
  upperExpr         -- 连续区间的结束偏移
)

实战示例:计算连续3天活跃用户数

continuous_retention(
  distinct_count(user_id),
  activity_date,
  1,  -- 从第1天开始
  3   -- 连续到第3天
)

工程上,连续留存的实现比普通留存复杂得多。传统方案通常使用 ROW_NUMBER() + GROUP BY 的”岛屿与间隙(Islands and Gaps)“算法,而 HENGSHI SENSE 在引擎层对其进行了优化,通过一次分组聚合即可完成连续性判定,大幅降低了计算复杂度。

2.3 累计计算:date_accumulate()

累计计算(YTD/MTD/QTD等)是财务报表和经营分析中的高频需求。date_accumulate() 函数可以计算指定时间范围内的累计值。

函数签名:

date_accumulate(
  function1(function2(expression)),  -- 嵌套函数表达式
  date expression1,                  -- 日期字段
  reset period                       -- 重置周期(如 'year', 'quarter', 'month')
)

实战示例:年初至今累计销售额

date_accumulate(
  sum(sales_amount),
  order_date,
  'year'   -- 按年重置,实现YTD累计
)

reset period 设置为 'month' 时,即为MTD(Month to Date)累计;设置为 'quarter' 时,即为QTD累计。这种统一的接口设计,让分析师可以通过一个参数切换不同的累计周期,无需重写任何逻辑。

2.4 同期对比:date_compare()

同比分析(YoY)是衡量业务增长趋势的关键指标。date_compare() 函数直接计算数据相对于指定日期的同期对比值。

函数签名:

date_compare(
  function(expression),  -- 待计算的指标
  date expression1,      -- 当前日期
  date expression2       -- 对比的基准日期
)

实战示例:今年vs去年同期销售额对比

date_compare(
  sum(sales_amount),
  current_date,
  same_period_last_year  --去年同期
)

2.5 移动计算:date_shift()

移动平均(MA)、移动求和等滑动窗口计算在时间序列分析中无处不在。date_shift() 函数专门用于处理这类时间序列移动统计。

函数签名:

date_shift(
  function1(function2(expression)),  -- 嵌套函数表达式
  date expression,                   -- 日期字段
  lowExpr,                           -- 窗口起始偏移
  upperExpr                          -- 窗口结束偏移
)

实战示例:7日移动平均GMV

date_shift(
  avg(gmv),
  trade_date,
  -6,   -- 前6天
  0     -- 当天(共7天窗口)
)

该函数的本质是一个时间维度的滑动窗口,但在语义上比原生SQL的 ROWS BETWEEN 子句更加直观。lowExprupperExpr 的偏移量基于日期(而非行数),避免了因数据稀疏导致的窗口变形问题。

2.6 环比计算:previous()

环比(MoM)用于衡量相邻两个周期之间的变化率。previous() 函数可以直接获取相对于指定日期的环比值。

函数签名:

previous(
  function(expression),  -- 待计算的指标
  period,                -- 周期类型
  delta                  -- 偏移量
)

实战示例:上月销售额及环比增长率

-- 获取上月销售额
previous(sum(sales_amount), 'month', 1)

-- 环比增长率可组合使用
(sum(sales_amount) - previous(sum(sales_amount), 'month', 1))
  / previous(sum(sales_amount), 'month', 1)

2.7 非时间维度版本:static_* 系列函数

在许多业务场景中,留存和活跃的计算不一定基于时间维度。例如,按”渠道”、“产品版本”等维度分析留存。HENGSHI SENSE 提供了三个对应的静态版本:

函数说明
static_retention(expr, dim, low, upper)非时间维度的留存计算
static_continuous_retention(expr, dim, low, upper)非时间维度的连续留存计算
static_retention_rate(expr, dim, low, upper)非时间维度的留存率计算

实战示例:按渠道计算用户留存

static_retention(
  distinct_count(user_id),
  channel_name,   -- 按渠道分组
  1,
  7
)

这套”时间维度/非时间维度”的对称设计,体现了 HENGSHI SENSE 对分析场景的全面覆盖——无论你的维度是日期、渠道还是产品线,都可以使用统一的函数接口。


三、数据处理函数:突破维度与上下文的计算边界

如果说时间序列函数解决的是”何时”的问题,那么数据处理函数解决的就是”如何”的问题——如何突破当前的维度上下文,如何在不同的数据粒度间切换计算。

3.1 上下文操控:calculate() / calculatep() / calculatex()

这三个函数构成了 HENGSHI SENSE 的上下文计算三件套,它们的功能相似但适用场景各有侧重。

函数签名:

calculate(ARG1, [ARG2], ...)   -- 预计算数据并与原始数据一起计算
calculatep(ARG1, [ARG2], ...)  -- 不受小计影响
calculatex(ARG1, [ARG2], ...)  -- 不受维度控制

三个函数的核心区别:

函数小计影响维度控制典型场景
calculate受影响受控制通用预计算
calculatep不受影响受控制需要排除小计干扰的计算
calculatex受影响不受控制需要忽略当前维度上下文的计算

实战示例:计算各品类占全渠道的百分比

-- 使用 calculate 获取全渠道总额
sum(sales_amount) / calculate(sum(sales_amount))

实战示例:跨品类计算(忽略当前品类维度)

-- 使用 calculatex 计算所有品类的总平均值
calculatex(avg(sales_amount))

3.2 跨表数据引用:lookupvalue()

在复杂的数据模型中,有时需要从其他非直接关联的数据表中获取值。lookupvalue() 函数提供了类似Excel VLOOKUP的能力。

函数签名:

lookupvalue(ARG1, ARG2, ARG3)
  • ARG1:目标表中要返回的表达式
  • ARG2:目标表中用于匹配的字段
  • ARG3:当前表中用于匹配的值

实战示例:从地区维度表获取区域经理姓名

lookupvalue(manager_name, region_id, current_region_id)

3.3 部分维度聚合:rollup_value() OVER()

这是 HENGSHI SENSE 6.1.0 版本新增的重要函数,支持基于部分维度计算指标,是处理层级聚合和多粒度分析的利器。

函数签名:

rollup_value(ARG) OVER(
  [ PARTITION BY expr1 [, expr2, ...] ]
)

实战示例:计算各省在其所属大区中的占比

-- 按大区维度进行部分聚合
rollup_value(sum(sales_amount)) OVER(PARTITION BY region)

OVER(PARTITION BY ...) 的语法与标准SQL窗口函数一致,降低了学习成本。在底层实现上,HENGSHI SENSE 的加速引擎会根据数据源类型智能选择执行策略——对于支持窗口函数的数据库(如PostgreSQL、Oracle),直接下推执行;对于不支持的数据库(如MySQL 5.7),则在引擎层进行内存计算。

3.4 Top/Bottom N 过滤

top_n(expression, n)     -- 保留前N个值
bottom_n(expression, n)  -- 保留后N个值

实战示例:只展示销售额排名前10的产品

top_n(sum(sales_amount), 10)

3.5 自定义过滤计算:custom_filters()

custom_filters() 允许在计算表达式中嵌入自定义的过滤条件,实现”条件聚合”的效果。

实战示例:只计算特定品类的销售额

custom_filters(
  sum(sales_amount),
  category = '电子产品'
)

四、聚合函数:从基础统计到高级分布分析

HENGSHI SENSE 提供了完整的聚合函数工具箱,覆盖了从基础统计到高级分析的各个层次。

4.1 数学统计函数

这些是日常分析中使用频率最高的聚合函数:

函数说明示例
avg(expr)算术平均值avg(sales_amount)
count(expr)计数count(order_id)
distinct_count(expr)去重计数distinct_count(user_id)
sum(expr)求和sum(revenue)
min(expr)最小值min(price)
max(expr)最大值max(price)
median(expr)中位数median(income)
percentile(expr, n)百分位数percentile(response_time, 95)
mode(expr)众数mode(page_view)

4.2 统计相关函数

对于需要进行深度统计分析的场景,HENGSHI SENSE 提供了完整的相关性分析和方差分析函数:

函数说明
corr(expr1, expr2)皮尔逊相关系数
covar_pop(expr1, expr2)总体协方差
covar_samp(expr1, expr2)样本协方差
stddev_pop(expr)总体标准差
stddev_samp(expr)样本标准差
var_pop(expr)总体方差
var_samp(expr)样本方差
regr_slope(expr1, expr2)线性回归斜率
regr_intercept(expr1, expr2)线性回归截距
regr_r2(expr1, expr2)R平方值
regr_count(expr1, expr2)回归中非空对的数量

实战示例:分析广告投放金额与转化率的相关性

corr(ad_spend, conversion_rate)

实战示例:构建用户收入与消费金额的线性回归模型参数

regr_slope(consumption, income)       -- 斜率
regr_intercept(consumption, income)   -- 截距
regr_r2(consumption, income)          -- 拟合优度

4.3 位置获取函数

在排序场景中,经常需要获取特定位置的数据值:

函数说明示例
first(expr)获取第一个值first(login_time)
last(expr)获取最后一个值last(logout_time)
nth(expr, n)获取第N个值nth(score, 3)
max_by(expr1, expr2)获取使expr2最大的expr1值max_by(product_name, sales)
min_by(expr1, expr2)获取使expr2最小的expr1值min_by(product_name, return_rate)

实战示例:获取每个品类中销量最高的产品名称

max_by(product_name, sales_amount)

这个函数的设计非常巧妙——它将”排序+取值”两个步骤合并为一个原子操作,在语义上更加清晰,在执行上避免了中间结果的物化。

4.4 聚合收集函数

将多个值收集为集合或字符串,适用于标签聚合、列表展示等场景:

函数说明示例
list_collect(expr)收集为列表(保留重复)list_collect(tag)
list_collect_flatten(expr)收集为列表并展平嵌套数组list_collect_flatten(tags_array)
set_collect(expr)收集为集合(去重)set_collect(category)
set_collect_flatten(expr)收集为集合并展平set_collect_flatten(categories)
string_agg(expr, delimiter)字符串聚合string_agg(name, ', ')

4.5 FILTER 子句与 WITHIN GROUP 排序子句

HENGSHI SENSE 支持在聚合函数中使用 FILTER 子句进行条件过滤,以及 WITHIN GROUP 子句进行排序控制。

FILTER 子句示例:

-- 只计算已完成订单的平均金额
avg(order_amount) FILTER(status = 'completed')

-- 只统计VIP用户的去重数
distinct_count(user_id) FILTER(user_level = 'VIP')

WITHIN GROUP 排序子句示例:

-- 获取按金额降序排列的第二个订单
nth(order_id, 2) WITHIN GROUP(ORDER BY order_amount DESC)

-- 获取最早的登录时间
first(login_time) WITHIN GROUP(ORDER BY login_time ASC)

FILTER 子句的引入,使得”条件聚合”不再需要 CASE WHEN 包裹,代码更加简洁。在底层,HENGSHI SENSE 会将其优化为对应的数据库原生语法(如PostgreSQL的 FILTER (WHERE ...)),确保执行效率。


五、图表高级计算:可视化中的分析能力

除了在查询层使用函数,HENGSHI SENSE 还将高级分析能力直接集成到图表组件中。用户无需编写代码,通过配置即可实现以下分析:

计算类型说明适用场景
百分比计算各项占总体的比例市场份额、构成分析
百分位数P50/P75/P90/P95等分位值性能监控、用户分层
同比/环比年同比、月环比等趋势分析、增长追踪
累计YTD/MTD/QTD累计财务报表、目标进度
移动滑动窗口均值/求和数据平滑、趋势识别
重复重复购买/访问计算用户忠诚度分析
留存N日留存率用户生命周期管理
活跃DAU/MAU/WAU产品活跃度监控
窗口计算灵活的分区聚合复杂多维分析

在 6.2 版本中,HENGSHI SENSE 还新增了 KPI条件格式的非色阶方式支持,允许分析师使用自定义的图标、符号等非色阶方式来展示KPI状态,进一步丰富了数据可视化的表达手段。


六、多数据源兼容:一套函数,多种引擎

HENGSHI SENSE 的高级函数体系之所以能在企业级场景中落地,一个关键原因是其广泛的数据源兼容性。以下是官方支持的数据源清单:

数据源支持状态
HENGSHI Engine(自研加速引擎)完整支持
PostgreSQL完整支持
Oracle完整支持
MySQL 8.0+完整支持
SQL Server 2017+完整支持
Amazon Redshift完整支持
Apache Impala完整支持
Presto/Trino完整支持
Greenplum完整支持
Apache Hive完整支持
Spark SQL完整支持
IBM DB2完整支持
MaxCompute完整支持

6.1 智能执行策略

不同数据库对高级分析语法的支持程度差异巨大。HENGSHI SENSE 在此采用了**“能下推就下推,不能下推就内存算”**的策略:

  1. 下推执行:对于原生支持窗口函数、FILTER子句等特性的数据库(如PostgreSQL、Oracle),直接将分析函数翻译为对应数据库的SQL语法,充分利用数据库的计算能力。
  2. 引擎层计算:对于不支持某些特性的数据库,HENGSHI SENSE 的加速引擎会在内存中完成计算。例如,MySQL 5.7不支持窗口函数,此时 HENGSHI SENSE 会将数据拉取到加速引擎层,使用自研的计算引擎完成窗口计算。
  3. 混合执行:部分查询条件下,HENGSHI SENSE 会将能下推的部分下推到数据库执行,将不能下推的部分留在引擎层执行,通过两阶段计算实现最优性能。

6.2 版本演进

HENGSHI SENSE 的高级函数体系在持续演进中:

  • 6.1.0 版本:新增 ROLLUP_VALUE() 函数和 json_array_length() 函数,进一步丰富了维度计算和JSON数据处理能力。ROLLUP_VALUE() 的引入,填补了”部分维度聚合”场景的功能空白。
  • 6.2 版本:KPI条件格式支持非色阶方式,让分析结果的展示更加灵活。

七、最佳实践与性能调优

7.1 函数选择指南

面对众多的分析函数,如何选择最合适的方案?

场景决策树:

需要分析用户留存?
├── 基于时间维度 → retention() / continuous_retention()
└── 基于其他维度 → static_retention() / static_continuous_retention()

需要做时间对比?
├── 同期对比(如去年同期) → date_compare()
├── 环比(如上月) → previous()
└── 滑动窗口 → date_shift()

需要突破当前维度计算?
├── 全局占比 → calculate()
├── 忽略小计影响 → calculatep()
└── 忽略所有维度 → calculatex()

需要获取特定位置的值?
├── 排序后的第一个/最后一个 → first() / last()
├── 排序后取最值对应的字段 → max_by() / min_by()
└── 按特定排名取值 → nth()

7.2 性能优化建议

  1. 优先使用内置函数而非自定义表达式:HENGSHI SENSE 的内置函数经过引擎层优化,通常比等效的SQL表达式性能更好。
  2. 合理设置时间窗口date_shift()retention() 中的窗口参数直接影响扫描的数据量。在满足业务需求的前提下,尽量缩小窗口范围。
  3. 利用 FILTER 子句替代 CASE WHENFILTER 子句在支持它的数据库上能生成更优的执行计划。
  4. 注意聚合粒度rollup_value() OVER(PARTITION BY ...) 中的分区字段不宜过多,否则会导致分区碎片化,影响性能。
  5. 善用加速引擎:对于复杂的多表关联+窗口计算场景,建议使用 HENGSHI SENSE 的加速引擎,避免在源数据库上执行过重的查询。

7.3 常见陷阱

陷阱说明解决方案
日期格式不一致retention() 等函数要求日期字段格式统一在数据接入层进行日期格式标准化
空值处理部分聚合函数遇到NULL会跳过,部分会返回NULL使用 coalesce() 进行空值替换
大偏移量性能下降date_shift() 的窗口过大时性能下降评估是否真的需要如此大的窗口
维度冲突calculatex() 会忽略所有维度,可能导致意外结果仔细确认是否真的需要忽略所有维度

八、总结

HENGSHI SENSE 的高级分析函数体系,本质上是对企业级数据分析中高频场景的系统化抽象。从本文的解析可以看出:

  1. 覆盖全面:30+函数覆盖了留存分析、时间序列、上下文计算、统计分布等核心分析场景,形成了一套完整的分析工具箱。
  2. 设计对称:时间维度与非时间维度的函数对称设计(如 retention vs static_retention),降低了学习成本,提升了API的一致性。
  3. 多源兼容:通过智能执行策略,同一套函数可以在15+种数据源上运行,屏蔽了底层的技术差异。
  4. 持续演进:从6.1.0的 ROLLUP_VALUE() 到6.2的KPI条件格式增强,这套体系在持续扩展边界。
  5. 工程导向:每个函数的设计都考虑了执行效率,通过下推策略、内存计算、混合执行等手段,确保分析性能不会成为瓶颈。

对于数据分析团队而言,掌握这套函数体系意味着:可以用更少的代码、更清晰的逻辑、更好的性能,解决更复杂的业务问题。这不仅是效率的提升,更是分析思维的升级——当你不再被”怎么写SQL”所困扰,就能将更多精力投入到”数据背后的业务洞察”之中。

HENGSHI SENSE

丰富的资源 完整的生态

邀您成为衡石伙伴

立即加入

企业级部署、产品集成与试用咨询均可快速响应