Title: 虚拟属性 Locale: zh URL: https://sensorswave.com/docs/data-center/data-dictionary/virtual-properties/ Description: 了解如何通过 SQL 表达式创建虚拟属性,在不修改埋点的情况下派生新的事件属性和用户属性 虚拟属性允许您通过 SQL 表达式基于已有属性派生出新的属性字段,无需修改任何埋点代码。虚拟属性不会修改原始数据,而是在查询时动态计算,天然支持回溯所有历史数据,可直接用于所有分析模型的筛选和分组。 ## 适用场景 虚拟属性适用于以下典型场景: - **合并不一致的埋点属性**:历史版本的埋点字段名不同(如 `platform_v1` 和 `platform`),通过 `COALESCE` 合并为统一属性,一次性分析跨越埋点变更前后的完整数据 - **渠道来源归类**:将 UTM source 参数归类为标准渠道分类(如 google/baidu → 「搜索引擎」,facebook/weibo → 「社交媒体」),按渠道大类分析流量来源 - **计算派生指标**:基于已有数值属性进行四则运算,如「实付金额」= 原价 - 折扣金额,「客单价」= 订单金额 / 商品数量 - **从 URL 提取信息**:从页面地址中提取搜索关键词、域名、路径等参数,用于分析用户搜索行为和流量来源 - **用户生命周期分段**:基于注册时间计算账号年龄,并分段为「新用户」「成长期」「成熟用户」等标签 ## 两种类型 虚拟属性分为两种类型: | 类型 | 附属对象 | 可引用的属性 | 典型用途 | |------|---------|------------|---------| | **虚拟事件属性** | 事件 | 事件属性 `{event.xxx}` + 用户属性 `{user.xxx}` | 计算订单客单价、提取 URL 参数、归类渠道来源 | | **虚拟用户属性** | 用户 | 仅用户属性 `{user.xxx}` | 计算用户年龄、生成注册时长标签、拼接地域信息 | > **如何选择?** 如果派生属性与某次事件行为相关(如订单金额计算、URL 参数提取),使用虚拟事件属性。如果派生属性是用户的固有特征(如年龄、注册天数),使用虚拟用户属性。 虚拟事件属性对所有事件全局可用。当引用的埋点属性在某个事件上不存在时,表达式会返回 NULL,您可以通过 `COALESCE`/`IFNULL` 等函数处理空值。 ## 核心概念 ### SQL 表达式 虚拟属性通过基于 [Apache Doris SQL 语法](https://doris.apache.org/zh-CN/docs/3.x/sql-manual/sql-functions/scalar-functions/) 的表达式定义计算逻辑。表达式支持算术运算、字符串处理、日期计算、条件判断等操作,但仅支持标量函数(不支持 `SUM`、`COUNT` 等聚合函数)。表达式最长 2048 字符。 如果您需要使用本文档未列出的函数,可参考 [Doris 标量函数文档](https://doris.apache.org/zh-CN/docs/3.x/sql-manual/sql-functions/scalar-functions/) 查看完整的函数列表。 ### 属性引用语法 在表达式中引用已有属性使用大括号语法: | 语法 | 含义 | 适用范围 | 示例 | |------|------|---------|------| | `{event.属性名}` | 引用事件属性 | 仅虚拟事件属性 | `{event.total_amount}` | | `{user.属性名}` | 引用用户属性(跟随查询开关) | 两种类型均可 | `{user.city}` | | `{user.属性名@latest}` | 强制引用用户最新属性值 | 两种类型均可 | `{user.vip_level@latest}` | | `{user.属性名@snapshot}` | 强制引用事件发生时的用户属性快照 | 仅虚拟事件属性 | `{user.vip_level@snapshot}` | **关于 `{user.属性名}` 不加后缀时的行为**:取值方式取决于查询中「使用最新用户属性」开关的状态: - **开关关闭(默认)**:`{user.属性名}` 等价于 `{user.属性名@snapshot}`,取事件发生时的用户属性值 - **开关开启**:`{user.属性名}` 等价于 `{user.属性名@latest}`,取用户属性的最新值 > **注意**:`@latest` 在查询时需要将事件表与用户表进行关联(JOIN),会导致查询变慢。请仅在确实需要最新值的场景下使用,例如需要用户当前的 VIP 等级而非下单时的等级。 ### 数据类型 虚拟属性支持以下数据类型: | 数据类型 | 说明 | 适用场景 | |---------|------|---------| | STRING | 字符串 | 文本拼接、分类标签 | | NUMBER | 数值 | 计算结果、比率 | | BOOL | 布尔值 | 条件判断结果 | | DATETIME | 日期时间 | 时间计算结果 | ## 虚拟事件属性 虚拟事件属性附属于事件,可以同时引用事件属性和用户属性,适用于需要基于事件上下文进行计算的场景。 ### 创建虚拟事件属性 1. 进入 **数据中心** > **数据字典** 2. 切换到 **虚拟事件属性** 标签页 3. 点击 **新建虚拟事件属性** 4. 填写基本信息: - **属性名称**:唯一标识,创建后不可修改。系统会自动添加 `$virtual/` 前缀 - **显示名**:在分析界面中展示的名称 - **数据类型**:选择表达式返回值的类型 5. 编写 SQL 表达式: - 在表达式编辑器中输入计算逻辑 - 通过属性选择器插入属性引用,避免手动拼写 6. 点击 **验证** 检查表达式语法和属性引用是否正确 7. 验证通过后,可复制生成的示例 SQL 或点击 **跳转到自定义 SQL** 直接查询分析,验证计算结果是否符合预期 8. 点击 **保存** ### 示例 1:合并不一致的埋点属性 **场景**:埋点方案升级后,平台属性从 `platform_v1` 改名为 `platform`。需要一个统一的属性来分析跨越变更前后的完整数据。 ```sql COALESCE({event.platform}, {event.platform_v1}) ``` 数据类型选择 STRING。`COALESCE` 会返回第一个非空值——新版本数据取 `platform`,旧版本数据取 `platform_v1`。 ### 示例 2:UTM 来源渠道归类 **场景**:将 UTM source 参数归类为标准渠道分类,按大类分析用户来源分布。 ```sql CASE WHEN {event.utm_source} IN ('google', 'baidu', 'bing') THEN '搜索引擎' WHEN {event.utm_source} IN ('facebook', 'weibo', 'wechat') THEN '社交媒体' WHEN {event.utm_source} IN ('email', 'newsletter') THEN '邮件营销' WHEN {event.utm_source} IS NULL THEN '直接访问' ELSE '其他' END ``` 数据类型选择 STRING。 ### 示例 3:计算实付金额 **场景**:订单事件中有 `original_price`(原价)和 `discount_amount`(折扣金额),需要派生「实付金额」用于分析。 | 字段 | 值 | |------|------| | 属性名称 | actual_payment | | 显示名 | 实付金额 | | 数据类型 | NUMBER | | SQL 表达式 | `{event.original_price} - IFNULL({event.discount_amount}, 0)` | 使用 `IFNULL` 处理没有折扣的订单,避免 NULL 导致计算结果为空。 ### 示例 4:从 URL 提取搜索关键词 **场景**:从页面 URL 中提取搜索关键词参数(如 `?q=手机`),用于分析用户搜索行为和关键词分布。 ```sql EXTRACT_URL_PARAMETER({event.page_url}, 'q') ``` 数据类型选择 STRING。按 [Doris 文档](https://doris.apache.org/zh-CN/docs/3.x/sql-manual/sql-functions/scalar-functions/string-functions/extract-url-parameter/),`EXTRACT_URL_PARAMETER` 用于从 URL 的查询串中按参数名取值;`PARSE_URL` 仅用于提取协议、主机、路径等组成部分(两参数形式),见下文「URL 解析」。 ## 虚拟用户属性 虚拟用户属性附属于用户,只能引用用户属性,适用于基于用户特征进行派生计算的场景。 ### 创建虚拟用户属性 1. 进入 **数据中心** > **数据字典** 2. 切换到 **虚拟用户属性** 标签页 3. 点击 **新建虚拟用户属性** 4. 填写基本信息: - **属性名称**:唯一标识,创建后不可修改。系统会自动添加 `$virtual/` 前缀 - **显示名**:在分析界面中展示的名称 - **数据类型**:选择表达式返回值的类型 5. 编写 SQL 表达式: - 仅可引用用户属性 `{user.xxx}`,不可引用事件属性 6. 点击 **验证** 检查表达式语法和属性引用 7. 验证通过后,可复制生成的示例 SQL 或点击 **跳转到自定义 SQL** 查询验证 8. 点击 **保存** ### 示例:用户生命周期分段 **场景**:基于注册时间计算账号年龄并分段为不同生命周期阶段,用于按用户阶段对比分析。 ```sql CASE WHEN DATEDIFF(NOW(), {user.register_time}) 0, '收入', '支出')` | | `COALESCE(a, b, ...)` | 返回第一个非空值 | `COALESCE({event.platform}, {event.platform_v1})` | | `IFNULL(a, default)` | 空值替换 | `IFNULL({event.channel}, '直接访问')` | | `NULLIF(a, b)` | 相等时返回 NULL | `NULLIF({event.status}, '')` | ### 日期函数 | 函数 | 说明 | 示例 | |------|------|------| | `DATE_FORMAT(dt, fmt)` | 格式化日期 | `DATE_FORMAT({event.create_time}, '%Y-%m')` | | `DATEDIFF(expr1, expr2)` | 返回 `expr1` 与 `expr2` 相差的天数(`expr1` − `expr2`,见 [Doris 文档](https://doris.apache.org/zh-CN/docs/3.x/sql-manual/sql-functions/scalar-functions/date-time-functions/datediff/)) | `DATEDIFF(NOW(), {user.register_time})` | | `NOW()` | 当前时间 | `NOW()` | | `UNIX_TIMESTAMP(dt)` | 转为时间戳 | `UNIX_TIMESTAMP({event.time})` | ### 类型转换 | 函数 | 说明 | 示例 | |------|------|------| | `CAST(x AS type)` | 类型转换 | `CAST({event.price} AS BIGINT)` | ### URL 解析 | 函数 | 说明 | 示例 | |------|------|------| | `PARSE_URL(url, part)` | 提取 URL 组成部分(`part` 不区分大小写),语法见 [PARSE_URL](https://doris.apache.org/zh-CN/docs/3.x/sql-manual/sql-functions/scalar-functions/string-functions/parse-url/) | `PARSE_URL({event.url}, 'HOST')` | | `EXTRACT_URL_PARAMETER(url, name)` | 从查询串中取名为 `name` 的参数值(同名列取第一个),语法见 [EXTRACT_URL_PARAMETER](https://doris.apache.org/zh-CN/docs/3.x/sql-manual/sql-functions/scalar-functions/string-functions/extract-url-parameter/) | `EXTRACT_URL_PARAMETER({event.url}, 'q')` | `part` 常用取值包括:`PROTOCOL`、`HOST`、`PATH`、`QUERY`、`REF`、`FILE`、`PORT` 等。若只需某个查询参数的值,应使用 `EXTRACT_URL_PARAMETER`,而不是三参数的 Hive 风格写法。 ## 验证和预览 ### 表达式验证 点击 **验证** 按钮,系统会检查: - SQL 语法是否正确 - 引用的属性是否存在 - 属性的数据类型是否与表达式兼容 - 是否包含不允许的函数(如聚合函数) - 虚拟用户属性中是否错误引用了事件属性 如果验证不通过,会显示具体的错误信息和定位。 ### 在自定义 SQL 中验证 验证通过后,系统会生成一条包含该虚拟属性表达式的示例 SQL。您可以: - **复制 SQL**:将生成的 SQL 复制到剪贴板,在其他工具中执行 - **跳转到自定义 SQL**:点击按钮直接跳转到自定义 SQL 查询页面,用真实数据验证虚拟属性的计算结果是否符合预期 这是在保存虚拟属性之前确认表达式逻辑正确性的推荐做法。 ## 编辑和删除 ### 编辑 1. 在虚拟属性列表中找到目标属性 2. 点击 **编辑** 3. 可修改:显示名、数据类型、SQL 表达式 4. 不可修改:属性名称、属性类型(事件/用户) 5. 修改后点击 **保存** > **注意**:修改表达式后,所有使用该虚拟属性的分析结果会立即按新逻辑计算,包括历史数据。 ### 删除 1. 点击 **删除** 2. 系统会检查是否有图表、概览或分群引用该属性 - 如果没有引用,确认后即可删除 - 如果存在引用,会列出引用资产。您可以选择取消删除或强制删除 3. 强制删除后,引用该属性的资产将失效 ## 在分析模型中使用 虚拟属性在分析界面中带有 `[虚拟]` 标识,使用方式与普通属性一致: - **作为筛选条件**:筛选满足特定条件的事件或用户 - **作为分组维度**:按虚拟属性的值分组查看分析结果 - **适用范围**:事件分析、漏斗分析、留存分析、用户列表、用户行为序列、用户分群 ## 使用限制 | 限制项 | 说明 | |--------|------| | 聚合函数 | 不支持 `SUM`、`COUNT`、`AVG`、`MAX`、`MIN` 等聚合函数 | | 子查询 | 不支持 `SELECT` 子查询 | | 嵌套引用 | 不支持在虚拟属性中引用另一个虚拟属性 | | 虚拟用户属性的引用范围 | 仅可引用用户属性 `{user.xxx}`,不可引用事件属性 | | 表达式长度 | 最长 2048 字符 | | 安全限制 | 表达式经过安全检查,禁止 DDL/DML 语句、子查询和系统函数 | ## 常见问题 ### `@latest` 和 `@snapshot` 有什么区别? - `@snapshot`:取事件发生时用户属性的值。例如用户下单时的 VIP 等级 - `@latest`:取用户属性的最新值。例如用户当前的 VIP 等级。查询时需要 JOIN 用户表,性能较慢 不加后缀时,取值方式跟随查询中「使用最新用户属性」开关。开关关闭(默认)时等价于 `@snapshot`,开启时等价于 `@latest`。`@snapshot` 仅在虚拟事件属性中可用。 ### 虚拟用户属性的值是实时计算的吗? 是的。每次查询时都会根据当前的用户属性值重新计算。例如「用户年龄」会随时间自动变化,「注册天数」每天都会增加。 ### 表达式验证报错「属性不存在」怎么办? 检查引用的属性名是否拼写正确。建议通过属性选择器插入属性引用,避免手动输入导致的拼写错误。 ### 修改虚拟属性后查询结果为什么变了? 虚拟属性是查询时动态计算的,修改表达式后,所有时间范围的查询都会按新逻辑计算。这是预期行为,不是数据异常。 ### 为什么虚拟用户属性创建时选不到事件属性? 这是预期行为。虚拟用户属性仅支持引用用户属性 `{user.xxx}`。如果您需要基于事件属性创建派生字段,请创建虚拟事件属性。 --- **最后更新时间**:2026 年 4 月 7 日