一. 背景与目标在删除或调整全局参数前,管理员需要知道该全局参数是否仍被下游资源使用,以及影响范围在哪里。常见影响对象包括: - 数据集:数据集 SQL、存储过程、参数化查询等引用了全局参数。
- 卡片:卡片使用了带全局参数的数据集,或卡片自身配置里直接引用了全局参数。
- 页面:页面中包含了使用全局参数的卡片。
本文说明如何通过元数据表统计全局参数的使用情况,并判断下游引用是否有效。 核心原则: 先从下游配置中抽取 dpId,再反查 dynamic_parameter 主表。
不要只从 dynamic_parameter 主表往下查。原因是:如果全局参数已经被删除,主表中可能已经没有该参数记录,此时从主表出发无法发现下游残留引用。正确做法是从数据集、卡片等下游配置中先抽取残留的 dpId,再通过 LEFT JOIN dynamic_parameter 判断该引用是否仍有效。 二. 核心标识与判断口径全局参数应通过 ID 关联,而不是通过名称关联。 主关联键: dpId + domId
原因: dpId 是全局参数的稳定标识。name 可能改名、重复,不适合作为关联键。domId 用于避免跨租户或跨域误关联。- 参数删除后,名称可能已无法从
dynamic_parameter 表获取,但下游仍可能残留 dpId。
引用有效性判断: 有效引用:
下游配置中的 dpId 能 LEFT JOIN 到 dynamic_parameter.dpId + domId。
失效引用:
下游配置中仍有 dpId,但 dynamic_parameter 中查不到对应 dpId + domId。
疑似跨域/脏数据:
dpId 能在其他 domId 下查到,但当前 domId 对不上。
冻结参数:
dynamic_parameter 中能查到,但 freeze = 1。
三. 涉及的元数据表3.1 dynamic_parameter全局参数主表。 常用字段: | 字段 | 说明 |
|---|
dpId | 全局参数 ID | domId | 租户/域 | name | 全局参数名称 | value_type | 参数类型 | default_value | 默认值 | freeze | 是否冻结 |
3.2 data_source数据集主表。 常用字段: | 字段 | 说明 |
|---|
ds_id | 数据集 ID | dom_id | 租户/域 | name | 数据集名称 | config | 数据集配置 JSON | is_del | 是否删除 |
全局参数常见存储位置: data_source.config.dynamicParameters
3.3 card卡片主表。 常用字段: | 字段 | 说明 |
|---|
cd_id | 卡片 ID | dom_id | 租户/域 | name | 卡片名称 | ds_id | 卡片主数据集 ID | meta | 卡片图表配置 JSON | settings | 卡片联动、高级配置 JSON | raw_data | 部分卡片扩展配置 | is_del | 是否删除 |
卡片直接使用全局参数的常见位置: card.meta.chartMain.props.dynamicParameters
card.meta 中筛选条件的 dpId
card.settings 中联动目标 targetFields.dpId
card.raw_data 中的 dpId
3.4 page_card_rel页面与卡片关系表。 常用字段: | 字段 | 说明 |
|---|
pg_id | 页面 ID | cd_id | 卡片 ID | dom_id | 租户/域 | is_del | 是否删除 |
3.5 page页面主表。 常用字段: | 字段 | 说明 |
|---|
pg_id | 页面 ID | dom_id | 租户/域 | name | 页面名称 | is_del | 是否删除 |
四. 全局参数使用统计4.1 统计哪些数据集使用了全局参数元数据关系数据集使用全局参数的关系如下: data_source.config.dynamicParameters[*].dpId
-> dynamic_parameter.dpId
data_source.dom_id
-> dynamic_parameter.domId
示例配置: {
"dynamicParameters": [
{
"dpId": "a1a7fc8181f714f119f307eb",
"name": "城市-多值",
"valueType": "STRING",
"defaultValue": "上海市"
}
]
}
SQL示例WITH ds_param_ref AS (
SELECT
ds.dom_id,
ds.ds_id,
ds.name AS ds_name,
dp_ref.dpId AS dp_id
FROM data_source ds
LATERAL VIEW OUTER explode(
from_json(
get_json_object(ds.config, '$.dynamicParameters'),
'array<struct<dpId:string,name:string,valueType:string,defaultValue:string,fdId:string,customize:boolean,multiple:boolean,optionValue:array<string>,inheritParent:boolean>>'
)
) ex AS dp_ref
WHERE coalesce(ds.is_del, 0) = 0
AND ds.config IS NOT NULL
AND get_json_object(ds.config, '$.dynamicParameters') IS NOT NULL
AND get_json_object(ds.config, '$.dynamicParameters') <> '[]'
AND dp_ref.dpId IS NOT NULL
AND trim(dp_ref.dpId) <> ''
)
SELECT
ref.ds_id AS `数据集ID`,
ref.ds_name AS `数据集名称`,
ref.dp_id AS `全局参数ID`,
dp.name AS `全局参数名称`,
CASE
WHEN dp.dpId IS NULL THEN '失效:参数已删除或跨域'
WHEN coalesce(dp.freeze, 0) = 1 THEN '有效:但参数冻结'
ELSE '有效'
END AS `引用状态`
FROM ds_param_ref ref
LEFT JOIN dynamic_parameter dp
ON ref.dp_id = dp.dpId
AND ref.dom_id = dp.domId;
PS:为什么使用 LEFT JOIN这里必须使用 LEFT JOIN,不能使用 INNER JOIN。 原因: INNER JOIN 只能查到仍存在于 dynamic_parameter 表中的参数。
LEFT JOIN 可以保留下游残留的 dpId,从而发现已删除但仍被引用的参数。
4.2 统计哪些卡片使用了全局参数卡片使用全局参数有两类口径: - 卡片使用了带全局参数的数据集。
- 卡片自身配置里直接引用了全局参数。
建议两类都统计,并用 使用场景 字段区分。 4.2.1 卡片使用了带全局参数的数据集元数据关系data_source.config.dynamicParameters[*].dpId
-> data_source.ds_id
-> card.ds_id
即:卡片本身不一定直接配置了全局参数,但它引用的数据集使用了全局参数,因此卡片会受到该参数影响。 SQL 示例WITH ds_param_ref AS (
SELECT
ds.dom_id,
ds.ds_id,
ds.name AS ds_name,
dp_ref.dpId AS dp_id
FROM data_source ds
LATERAL VIEW OUTER explode(
from_json(
get_json_object(ds.config, '$.dynamicParameters'),
'array<struct<dpId:string,name:string,valueType:string,defaultValue:string,fdId:string,customize:boolean,multiple:boolean,optionValue:array<string>,inheritParent:boolean>>'
)
) ex AS dp_ref
WHERE coalesce(ds.is_del, 0) = 0
AND dp_ref.dpId IS NOT NULL
AND trim(dp_ref.dpId) <> ''
)
SELECT DISTINCT
c.cd_id AS `卡片ID`,
c.name AS `卡片名称`,
ref.ds_id AS `数据集ID`,
ref.ds_name AS `数据集名称`,
ref.dp_id AS `全局参数ID`,
dp.name AS `全局参数名称`,
'卡片使用带参数数据集' AS `使用场景`,
CASE
WHEN dp.dpId IS NULL THEN '失效:参数已删除或跨域'
WHEN coalesce(dp.freeze, 0) = 1 THEN '有效:但参数冻结'
ELSE '有效'
END AS `引用状态`
FROM ds_param_ref ref
JOIN card c
ON c.ds_id = ref.ds_id
AND c.dom_id = ref.dom_id
LEFT JOIN dynamic_parameter dp
ON ref.dp_id = dp.dpId
AND ref.dom_id = dp.domId
WHERE coalesce(c.is_del, 0) = 0;
4.2.2 卡片自身配置直接引用全局参数标准配置路径卡片内容中通常会有: card.meta.chartMain.props.dynamicParameters
这里保存了卡片侧的动态参数配置。 SQL 示例:解析标准路径WITH card_direct_dp AS (
SELECT
c.dom_id,
c.cd_id,
c.name AS card_name,
dp_ref.dpId AS dp_id
FROM card c
LATERAL VIEW OUTER explode(
from_json(
get_json_object(c.meta, '$.chartMain.props.dynamicParameters'),
'array<struct<dpId:string,name:string,valueType:string,defaultValue:string,fdId:string,inheritParent:boolean>>'
)
) ex AS dp_ref
WHERE coalesce(c.is_del, 0) = 0
AND dp_ref.dpId IS NOT NULL
AND trim(dp_ref.dpId) <> ''
)
SELECT
ref.cd_id AS `卡片ID`,
ref.card_name AS `卡片名称`,
ref.dp_id AS `全局参数ID`,
dp.name AS `全局参数名称`,
'卡片直接配置' AS `使用场景`,
CASE
WHEN dp.dpId IS NULL THEN '失效:参数已删除或跨域'
WHEN coalesce(dp.freeze, 0) = 1 THEN '有效:但参数冻结'
ELSE '有效'
END AS `引用状态`
FROM card_direct_dp ref
LEFT JOIN dynamic_parameter dp
ON ref.dp_id = dp.dpId
AND ref.dom_id = dp.domId;
兜底扫描卡片 JSON除标准路径外,全局参数还可能出现在: card.meta
card.settings
card.raw_data
比如: "dpId": "xxx"
filterType = "IN_GLOBAL_PARAMS"
filterType = "STARTSWITH_GLOBAL_PARAMS"
targetFields 中存在 dpId
如果只需要发现仍存在的全局参数,可以使用: SELECT
c.cd_id AS `卡片ID`,
c.name AS `卡片名称`,
dp.dpId AS `全局参数ID`,
dp.name AS `全局参数名称`,
'卡片JSON中出现dpId' AS `使用场景`,
'有效' AS `引用状态`
FROM card c
JOIN dynamic_parameter dp
ON c.dom_id = dp.domId
WHERE coalesce(c.is_del, 0) = 0
AND (
c.meta LIKE concat('%', dp.dpId, '%')
OR c.settings LIKE concat('%', dp.dpId, '%')
OR c.raw_data LIKE concat('%', dp.dpId, '%')
);
注意:该兜底方式是从 dynamic_parameter 主表出发,只能发现仍存在的参数。如果参数已经删除,需要从卡片 JSON 中先抽取所有疑似 dpId,再 LEFT JOIN dynamic_parameter。 4.3 统计哪些页面使用了全局参数页面通常不直接使用全局参数,而是因为页面上包含了相关卡片。 元数据关系全局参数
-> 数据集 / 卡片
-> page_card_rel.cd_id
-> page.pg_id
即: card_param_ref.cd_id = page_card_rel.cd_id
page_card_rel.pg_id = page.pg_id
SQL 示例下面 SQL 假设已经有两个中间结果: ds_param_ref:数据集引用全局参数。card_direct_dp:卡片直接配置全局参数。
WITH card_param_ref AS (
SELECT
c.dom_id,
c.cd_id,
c.name AS card_name,
ref.ds_id,
ref.ds_name,
ref.dp_id,
'卡片使用带参数数据集' AS usage_type
FROM ds_param_ref ref
JOIN card c
ON c.ds_id = ref.ds_id
AND c.dom_id = ref.dom_id
WHERE coalesce(c.is_del, 0) = 0
UNION ALL
SELECT
dom_id,
cd_id,
card_name,
NULL AS ds_id,
NULL AS ds_name,
dp_id,
'卡片直接配置' AS usage_type
FROM card_direct_dp
)
SELECT DISTINCT
p.pg_id AS `页面ID`,
p.name AS `页面名称`,
c.cd_id AS `卡片ID`,
c.card_name AS `卡片名称`,
c.ds_id AS `数据集ID`,
c.ds_name AS `数据集名称`,
c.dp_id AS `全局参数ID`,
dp.name AS `全局参数名称`,
c.usage_type AS `使用场景`,
CASE
WHEN dp.dpId IS NULL THEN '失效:参数已删除或跨域'
WHEN coalesce(dp.freeze, 0) = 1 THEN '有效:但参数冻结'
ELSE '有效'
END AS `引用状态`
FROM card_param_ref c
JOIN page_card_rel rel
ON rel.cd_id = c.cd_id
AND rel.dom_id = c.dom_id
JOIN page p
ON p.pg_id = rel.pg_id
AND p.dom_id = rel.dom_id
LEFT JOIN dynamic_parameter dp
ON c.dp_id = dp.dpId
AND c.dom_id = dp.domId
WHERE coalesce(rel.is_del, 0) = 0
AND coalesce(p.is_del, 0) = 0;
⑤. 推荐统一统计 SQL 框架实际落地时,建议统一输出一张明细表,包含数据集、卡片、页面三层信息。 推荐字段: | 字段 | 说明 |
|---|
全局参数ID | 下游配置中抽取到的 dpId | 全局参数名称 | 从 dynamic_parameter 反查得到 | 引用状态 | 有效、失效、冻结等 | 使用层级 | 数据集、卡片、页面 | 使用场景 | 数据集配置、卡片使用带参数数据集、卡片直接配置、页面包含相关卡片 | 数据集ID | 相关数据集 ID | 数据集名称 | 相关数据集名称 | 卡片ID | 相关卡片 ID | 卡片名称 | 相关卡片名称 | 页面ID | 相关页面 ID | 页面名称 | 相关页面名称 |
示例统一结构: WITH ds_param_ref AS (
-- 数据集中的全局参数引用
),
card_direct_dp AS (
-- 卡片自身配置中的全局参数引用
),
card_param_ref AS (
-- 卡片使用带参数数据集
SELECT
ref.dom_id,
ref.ds_id,
ref.ds_name,
c.cd_id,
c.name AS card_name,
ref.dp_id,
'卡片' AS usage_level,
'卡片使用带参数数据集' AS usage_type
FROM ds_param_ref ref
JOIN card c
ON c.ds_id = ref.ds_id
AND c.dom_id = ref.dom_id
WHERE coalesce(c.is_del, 0) = 0
UNION ALL
-- 卡片直接配置
SELECT
dom_id,
NULL AS ds_id,
NULL AS ds_name,
cd_id,
card_name,
dp_id,
'卡片' AS usage_level,
'卡片直接配置' AS usage_type
FROM card_direct_dp
),
page_param_ref AS (
SELECT
c.dom_id,
c.ds_id,
c.ds_name,
c.cd_id,
c.card_name,
p.pg_id,
p.name AS page_name,
c.dp_id,
'页面' AS usage_level,
concat('页面包含相关卡片:', c.usage_type) AS usage_type
FROM card_param_ref c
JOIN page_card_rel rel
ON rel.cd_id = c.cd_id
AND rel.dom_id = c.dom_id
JOIN page p
ON p.pg_id = rel.pg_id
AND p.dom_id = rel.dom_id
WHERE coalesce(rel.is_del, 0) = 0
AND coalesce(p.is_del, 0) = 0
),
all_refs AS (
SELECT
dom_id,
ds_id,
ds_name,
NULL AS cd_id,
NULL AS card_name,
NULL AS pg_id,
NULL AS page_name,
dp_id,
'数据集' AS usage_level,
'数据集配置' AS usage_type
FROM ds_param_ref
UNION ALL
SELECT
dom_id,
ds_id,
ds_name,
cd_id,
card_name,
NULL AS pg_id,
NULL AS page_name,
dp_id,
usage_level,
usage_type
FROM card_param_ref
UNION ALL
SELECT
dom_id,
ds_id,
ds_name,
cd_id,
card_name,
pg_id,
page_name,
dp_id,
usage_level,
usage_type
FROM page_param_ref
)
SELECT DISTINCT
ref.dp_id AS `全局参数ID`,
dp.name AS `全局参数名称`,
CASE
WHEN dp.dpId IS NULL THEN '失效:参数已删除或跨域'
WHEN coalesce(dp.freeze, 0) = 1 THEN '有效:但参数冻结'
ELSE '有效'
END AS `引用状态`,
ref.usage_level AS `使用层级`,
ref.usage_type AS `使用场景`,
ref.ds_id AS `数据集ID`,
ref.ds_name AS `数据集名称`,
ref.cd_id AS `卡片ID`,
ref.card_name AS `卡片名称`,
ref.pg_id AS `页面ID`,
ref.page_name AS `页面名称`
FROM all_refs ref
LEFT JOIN dynamic_parameter dp
ON ref.dp_id = dp.dpId
AND ref.dom_id = dp.domId;
六. 数据集 SQL 中涉及的函数语法6.1 get_json_objectget_json_object(ds.config, '$.dynamicParameters')
作用:从 JSON 字符串中取出指定路径的值。 在本场景中,用于从数据集配置中取出 dynamicParameters 数组。 6.2 from_jsonfrom_json(
get_json_object(ds.config, '$.dynamicParameters'),
'array<struct<dpId:string,name:string,valueType:string,defaultValue:string>>'
)
作用:把 JSON 字符串转成 Spark SQL 可识别的结构化数组。 注意:需要显式声明 schema,否则后续无法访问 dp_ref.dpId。 6.3 explodeexplode(array_col)
作用:把数组拆成多行。 例如一个数据集引用了 3 个全局参数,explode 后会产生 3 行。 6.4 LATERAL VIEW OUTER explodeLATERAL VIEW OUTER explode(...) ex AS dp_ref
作用:在 Spark/Hive SQL 中展开数组字段。 OUTER 的作用是:即使数组为空或为 null,也保留原始行。但如果后面加了:
dp_ref.dpId IS NOT NULL
最终仍只会保留真实引用了全局参数的记录。 6.5 coalescecoalesce(ds.is_del, 0) = 0
作用:兼容删除标识为空的情况。 如果 is_del 是字符串类型,可以改成: coalesce(ds.is_del, '0') = '0'
具体使用哪种写法,需要根据元数据字段类型确认。 6.6 trimtrim(dp_ref.dpId) <> ''
作用:过滤空字符串,避免无效引用进入统计结果。 6.7 concat如果在 ETL 常规 Join 算子中不方便配置多字段关联,可以生成拼接键: concat(dp_ref.dpId, '@@', ds.dom_id) AS dp_join_key
全局参数主表中也生成: concat(dpId, '@@', domId) AS dp_join_key
然后在 ETL 中用单字段关联: dp_join_key = dp_join_key
这样既保留了 dpId + domId 的关联语义,又能避免部分前端 ETL Join 面板对多字段 predicate 支持不稳定的问题。 七. 实操建议7.1 查询口径建议推荐分三层统计: 数据集层:data_source.config.dynamicParameters
卡片层:card.ds_id 关联数据集引用 + card.meta/settings/raw_data 直接引用
页面层:page_card_rel 关联包含相关卡片的页面
9.2 删除前检查建议删除某个全局参数前,应检查: 1. 是否仍被数据集引用
2. 是否仍被卡片直接配置引用
3. 是否影响页面上的卡片
4. 是否存在已删除参数的残留引用
9.3 失效引用治理建议如果发现: 下游存在 dpId,但 dynamic_parameter 中查不到
说明存在失效引用。建议治理方式: 1. 定位具体数据集、卡片、页面。
2. 确认该全局参数是否已删除。
3. 如果下游仍需要参数能力,重新绑定新的全局参数。
4. 如果下游不再需要,清理数据集或卡片配置中的残留 dpId。
10. 总结全局参数影响范围统计的核心不是“查参数表”,而是“查下游资源中残留的参数引用”。 推荐实现链路: 1. 从 data_source.config.dynamicParameters 抽取数据集引用。
2. 从 card.meta/settings/raw_data 抽取卡片直接引用。
3. 通过 card.ds_id 找到使用带参数数据集的卡片。
4. 通过 page_card_rel 找到页面。
5. 最后 LEFT JOIN dynamic_parameter 判断引用是否有效。
最终即可形成: 全局参数 -> 数据集 -> 卡片 -> 页面
以及: 有效 / 失效 / 冻结 / 跨域异常
的完整治理视图。 |