Skip to content

计算字段

下载本文档

计算字段允许在查询时动态定义新的列,通过表达式计算得到值,无需修改 TM/QM 模型。

1. 概述

计算字段有两种定义方式:

  1. calculatedFields 参数:在 DSL 请求中定义
  2. 内联表达式:在 columns 中直接写 expression as alias

2. 通过 calculatedFields 定义

2.1 基本格式

json
{
    "param": {
        "calculatedFields": [
            {
                "name": "netAmount",
                "caption": "净销售额",
                "expression": "salesAmount - discountAmount"
            }
        ],
        "columns": ["orderId", "salesAmount", "discountAmount", "netAmount"]
    }
}

2.2 字段说明

字段类型必填说明
namestring字段名,在 columns/slice/orderBy 中引用
captionstring显示名称
expressionstring计算表达式
descriptionstring字段描述
aggstring聚合类型(用于 autoGroupBy 场景)

2.3 表达式中的列引用

表达式可以引用:

  • 模型中的属性名
  • 模型中的度量名
  • 维度列:dimension$caption, dimension$id, dimension$property
  • 其他计算字段名(需在当前字段之前定义)

3. 通过内联表达式定义

在 columns 中直接使用 expression as alias 格式:

json
{
    "param": {
        "columns": [
            "orderId",
            "salesAmount",
            "salesAmount * 1.1 as adjustedAmount",
            "ROUND(salesAmount, 2) as roundedAmount"
        ]
    }
}

3.1 聚合表达式

json
{
    "param": {
        "columns": [
            "product$categoryName",
            "sum(salesAmount) as totalSales",
            "count(*) as orderCount"
        ],
        "groupBy": [
            { "field": "product$categoryName" }
        ]
    }
}

注意:内联表达式的别名不能与模型中已有的字段名相同,否则会报错。


4. 支持的表达式

4.1 算术运算

运算符说明示例
+加法salesAmount + taxAmount
-减法salesAmount - discountAmount
*乘法unitPrice * quantity
/除法profitAmount / salesAmount
%取模quantity % 10

复合表达式

(salesAmount - discountAmount) * 1.13

4.2 数学函数

函数说明示例
ABS(x)绝对值ABS(discountAmount)
ROUND(x, n)四舍五入ROUND(salesAmount, 2)
CEIL(x)向上取整CEIL(quantity / 10)
FLOOR(x)向下取整FLOOR(quantity / 10)
MOD(x, y)取模MOD(quantity, 10)
POWER(x, y)幂运算POWER(2, 3)
SQRT(x)平方根SQRT(variance)

4.3 日期函数

函数说明示例
YEAR(date)提取年份YEAR(salesDate$caption)
MONTH(date)提取月份MONTH(salesDate$caption)
DAY(date)提取日期DAY(salesDate$caption)
DATE(datetime)提取日期部分DATE(orderTime)
NOW()当前时间NOW()
DATE_ADD(date, interval)日期加法DATE_ADD(orderDate, 7)
DATE_SUB(date, interval)日期减法DATE_SUB(orderDate, 7)
DATEDIFF(d1, d2)日期差DATEDIFF(NOW(), orderDate)

4.4 字符串函数

函数说明示例
CONCAT(a, b, ...)字符串连接CONCAT(orderId, '-', orderLineNo)
SUBSTRING(s, start, len)子字符串SUBSTRING(orderId, 1, 4)
UPPER(s)转大写UPPER(status)
LOWER(s)转小写LOWER(email)
TRIM(s)去除空格TRIM(customerName)
LENGTH(s)字符串长度LENGTH(productName)

4.5 空值处理函数

函数说明示例
COALESCE(a, b, ...)返回第一个非空值COALESCE(discountAmount, 0)
NULLIF(a, b)相等则返回 NULLNULLIF(status, 'UNKNOWN')
IFNULL(a, b)为空则返回默认值IFNULL(discountAmount, 0)

4.6 聚合函数

函数说明示例
SUM(x)求和SUM(salesAmount)
AVG(x)平均值AVG(unitPrice)
COUNT(*)计数COUNT(*)
MAX(x)最大值MAX(salesAmount)
MIN(x)最小值MIN(salesAmount)

5. 完整示例

5.1 简单算术表达式

json
{
    "param": {
        "calculatedFields": [
            {
                "name": "netAmount",
                "caption": "净销售额",
                "expression": "salesAmount - discountAmount"
            }
        ],
        "columns": ["orderId", "salesAmount", "discountAmount", "netAmount"]
    }
}

生成的 SQL

sql
SELECT
    order_id AS orderId,
    sales_amount AS salesAmount,
    discount_amount AS discountAmount,
    (sales_amount - discount_amount) AS netAmount
FROM fact_sales

5.2 利润率计算

json
{
    "param": {
        "calculatedFields": [
            {
                "name": "profitRate",
                "caption": "利润率(%)",
                "expression": "profitAmount * 100.0 / salesAmount"
            }
        ],
        "columns": ["orderId", "salesAmount", "profitAmount", "profitRate"],
        "slice": [
            { "field": "salesAmount", "op": ">", "value": 0 }
        ]
    }
}

5.3 链式计算字段

计算字段可以引用其他计算字段:

json
{
    "param": {
        "calculatedFields": [
            {
                "name": "netAmount",
                "caption": "净销售额",
                "expression": "salesAmount - discountAmount"
            },
            {
                "name": "taxIncluded",
                "caption": "含税金额",
                "expression": "netAmount * 1.13"
            }
        ],
        "columns": ["orderId", "salesAmount", "netAmount", "taxIncluded"]
    }
}

5.4 引用维度列

json
{
    "param": {
        "calculatedFields": [
            {
                "name": "yearMonth",
                "caption": "年月",
                "expression": "CONCAT(YEAR(salesDate$caption), '-', MONTH(salesDate$caption))"
            }
        ],
        "columns": ["orderId", "salesDate$caption", "yearMonth", "salesAmount"]
    }
}

5.5 计算字段作为过滤条件

json
{
    "param": {
        "calculatedFields": [
            {
                "name": "profitRate",
                "caption": "利润率",
                "expression": "profitAmount * 100.0 / salesAmount"
            }
        ],
        "columns": ["orderId", "salesAmount", "profitAmount", "profitRate"],
        "slice": [
            { "field": "salesAmount", "op": ">", "value": 0 },
            { "field": "profitRate", "op": ">", "value": 10 }
        ]
    }
}

5.6 分组汇总中的计算字段

json
{
    "param": {
        "columns": [
            "product$categoryName",
            "sum(salesAmount) as totalSales",
            "sum(profitAmount) as totalProfit",
            "sum(profitAmount) * 100.0 / sum(salesAmount) as profitRate"
        ],
        "groupBy": [
            { "field": "product$categoryName" }
        ],
        "orderBy": [
            { "field": "totalSales", "order": "desc" }
        ]
    }
}

6. 安全性

6.1 禁止的函数

以下函数会被拦截并抛出 SecurityException

  • EXECEXECUTE
  • DROPDELETEUPDATEINSERT
  • CREATEALTERTRUNCATE
  • 其他未在白名单中的函数

6.2 错误处理

错误类型说明
引用不存在的列抛出异常,提示列名不存在
重复的计算字段名抛出异常,提示名称已存在
别名与已有字段冲突抛出异常,提示字段名冲突
使用禁止的函数抛出 SecurityException

7. 最佳实践

  1. 避免除零错误:除法运算前添加 salesAmount > 0 条件
  2. 使用 COALESCE:处理可能为 NULL 的字段
  3. 命名规范:计算字段使用有意义的名称,避免与已有字段冲突
  4. 链式依赖:被引用的计算字段需在引用者之前定义
  5. 性能考虑:复杂的计算字段可能影响查询性能

下一步