Metabase 表变量(Table Variables):在 SQL 编辑器中动态指定查询表
【免费下载链接】metabaseThe easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:项目地址: https://gitcode.com/GitHub_Trending/me/metabase
导读
表变量(Table variables)是 Metabase 原生 SQL 编辑器提供的一种特殊模板标签:它允许你在 SQL 查询中用{{变量名}}占位符替代真实表名,运行查询时由 Metabase 自动替换为所选表的 schema 与表名。本文基于 docs/questions/native-editor/table-variables.md 完整讲解表变量的添加方式、类型配置、"Emit table alias"(变量名别名)两种引用模式、与 SQL 片段(snippets)的组合玩法及已知限制,并结合当前仓库源码说明其底层实现原理,帮助你写出一次编写、多表复用的通用 SQL 模板。
表变量是什么
表变量(Table variable)是 SQL 参数(SQL parameters)的一种变量类型。在原生 SQL 查询中,你可以在通常书写表名的地方使用双花括号占位符,例如{{table}}。当运行查询时,Metabase 会把该占位符替换为你在变量侧栏中选定的表的 schema 与表名。
这一点与普通的文本/数字变量有本质区别:普通变量替换的是"值"(如某个筛选条件的具体取值),而表变量替换的是"表"本身,也就是查询所作用的数据源。
从源码结构看,表变量在 Metabase 的模板标签(template tag)体系中被建模为一种独立的标签类型:table。在 src/metabase/lib/schema/template_tag.cljc 中,模板标签的合法:type枚举为:snippet :card :dimension :number :text :date :boolean :temporal-unit :table,其中:table就是表变量对应的类型。而该文件对表变量标签(::source-table)的定义明确包含三个关键字段:
:table-id:映射到的目标表 ID(必填);:emit-alias(可选布尔值):是否"发出"变量名作为别名;:source-filters(可选):针对表变量的源过滤条件。
表变量 vs 其他 SQL 变量
| 变量类型 | 占位符示例 | 作用 | 文档 |
|---|---|---|---|
| 字段筛选变量 | {{created_at}} | 生成"智能"筛选器(日期选择、下拉等),需映射到查询中的字段 | field-filters.md |
| 基础变量 | {{category}} | 文本、数字、日期等简单值输入 | basic-sql-parameters.md |
| 时间分组参数 | {{unit}} | 让用户切换按日/周/月/年分组 | time-grouping-parameters.md |
| 表变量 | {{table}} | 选择要查询哪张表,替换表名 | 本文 |
向查询中添加表变量
第一步:写出占位符
在原生 SQL 编辑器中,凡是写表名的地方都可以使用双花括号占位符:
SELECT COUNT(*) FROM {{table}}表变量可以出现在任何表名可出现的位置,包括FROM子句与JOIN子句。例如在下面的查询中,{{table}}和{{products_table}}分别替换两张表:
SELECT t.*, p.title FROM {{table}} AS t JOIN {{products_table}} AS p ON t.product_id = p.id第二步:将变量类型设置为 Table
添加{{变量名}}占位符之后,Variables(变量)侧栏会自动出现,此时需要完成以下配置:
- 打开Variables侧栏(添加变量后会自动弹出)。
- 将变量类型改为Table。
- 在Table to map to(要映射到的表)下,从表选择器中选择一张表(必填项,未选择前会显示 required 标记)。
- 根据你希望在查询中引用该表变量的方式,切换Emit table alias开关(具体见下文"引用表变量的两种方式")。
运行查询时,Metabase 会把{{table}}替换为所选表的 schema 与表名。如果想预览 Metabase 实际执行的完整 SQL,可以点击编辑器上方的眼睛(eye)图标——该预览会展示变量替换后的真实语句,便于排查替换是否符合预期。
从源码看,"表映射"与"别名开关"正是前端 TableMappingSelect.tsx 组件负责渲染的界面:它展示了Table to map to选择器(SchemaAndTableDataSelector,缺省时标红显示(required)),并提供一个 Switch 开关,开关默认值为tag["emit-alias"] ?? true,其文案为"Use variable name as alias(将变量名用作别名)",提示语为"你可以在查询的其他部分用变量名引用此表"。由此可见,Emit table alias 在默认情况下是开启的。
引用表变量的两种方式
表变量被替换进 SQL 后,查询中其他部分要引用这张表,有两种写法,区别在于Emit table alias开关的状态。
方式一:使用变量名引用(Emit table alias 开启)
如果你希望在查询的其余部分直接使用变量名来引用该表,需要将Emit table alias切换为开启(on)。此时 Metabase 会以变量名作为表别名"发出",查询形如:
SELECT var_name.id, p.title FROM {{var_name}} JOIN products as p on var_name.product_id = p.id这里的{{var_name}}被替换为schema.table_name AS var_name(等价语义),因此后续var_name.id、var_name.product_id都能正确解析。
方式二:自行指定别名(Emit table alias 关闭)
如果你希望自己手动指定别名,则需要将Emit table alias切换为关闭(off),并在查询中手动添加别名。适合的场景是:你已经有一条带既有别名体系的长查询,只想把其中一张表换成表变量,而不想改动其余引用。此时查询形如:
SELECT o.id, p.title FROM {{var_name}} as o JOIN products as p on o.product_id = p.id此时{{var_name}}仅被替换为schema.table_name,不带任何别名,因此你需要手动书写as o,并在后续用o.id、o.product_id引用它。
两种方式的对比
| Emit table alias 开启 | Emit table alias 关闭 | |
|---|---|---|
| 替换结果 | 表名 + 变量名别名 | 仅表名 |
| 后续引用 | 直接用变量名,如var_name.id | 手动别名,如o.id |
| 典型场景 | 新查询、希望少写别名 | 已有长查询、只想换表 |
这一行为在后端有明确的代码依据:在 src/metabase/query_processor/parameters/values.clj 中,:table标签的解析实现会读取:table-id、:source-filters、:emit-alias与:name,并调用lib/parsed-referenced-table-query-param:第一个参数是目标表 ID,第二个参数是源过滤条件,第三个参数则是在emit-alias为真时传入变量名((when emit-alias name))——这正是"开启别名即发出变量名"这一行为的实现来源。
表变量 × SQL 片段(Snippets):一次编写,多表复用
表变量最有价值的用法是与 SQL 片段(snippets)组合使用:你可以把一段通用的 SQL 写成片段,然后在多个问题(question)中复用,每个问题把其中的表变量映射到不同的表。
具体操作如下。
1. 创建包含表变量的片段
假设创建一个名为 "row count" 的 SQL 片段,内容为:
SELECT COUNT(*) FROM {{table}}创建方式:在原生编辑器中选中代码,右键选择Save as snippet;或打开 Snippet 侧栏新建并命名(片段名必须唯一,即使已归档的片段也占用名称,参见 snippets.md)。
2. 在问题中插入片段
在另一个 SQL 问题中插入该片段引用:
{{snippet: row count}}注意 Metabase 对片段引用的空白敏感:{{snippet:与snippet之间不能有空格,而冒号与片段名之间需要有一个空格。
3. 为每个问题映射不同的表
在每个问题中打开 Variables 侧栏,将{{table}}映射到不同的数据库表即可。同一个 "row count" 片段,可以在问题 A 中统计Products表的行数,在问题 B 中统计Orders表的行数,无需重写 SQL。
背后的机制是:片段参数的设置由问题(question)决定,而非片段本身决定(参见 snippets.md 中的 "Values for snippet parameters are defined by the question, not the snippet")。因此同一片段在不同问题中可以映射到完全不同的表,各问题的映射互不干扰。
从模板标签的建模上,片段引用{{snippet: row count}}本身是一种:snippet类型的模板标签(见 template_tag.cljc,含:snippet-name与:snippet-id),而片段内部展开后出现的{{table}}则会作为独立的:table标签参与解析。两者组合,就实现了"通用片段 + 每问题换表"的复用能力。
表变量的限制
根据官方文档与当前仓库实现,表变量目前存在以下限制,规划使用方式时需要特别注意:
- 不能作为仪表板筛选参数:表变量无法连接到仪表板筛选器(dashboard filter)组件。表变量必须直接在每个问题上单独设置。这一点与字段筛选变量形成鲜明对比——字段筛选变量可以连接到仪表板筛选器(见 sql-parameters.md 中的"将 SQL 问题连接到仪表板筛选器")。
- 仅限 SQL 查询:表变量只在原生 SQL 查询中可用,不适用于查询构建器(query builder)。
- 没有输入组件:不存在让用户自行输入表名的输入框。你必须在变量侧栏中通过表选择器选定表,也就是说,表变量面向的是"由提问者预先固定表"的模板场景,而非终端用户自由选表。
- 暂不支持 transforms:表变量还不能用于 Data Studio 的 transforms(转换)。
另外可以补充一个与安全模型相关的注意点:表变量在映射时通过:table-id指向具体表(见 template_tag.cljc 的::source-table定义),并且支持可选的:source-filters(允许的操作符限定为:> :>= :< :<= := :!=,见 allowed-source-filter-ops),后端解析时会校验这些操作符的合法性(见 values.clj)。
实战小结
要把表变量用好,记住以下要点:
- 占位符写在哪:写在表名出现的位置(
FROM、JOIN),使用双花括号{{名称}}。 - 配置三件事:类型选 Table、映射目标表(必填)、决定 Emit table alias 开关。
- 引用方式二选一:开 Emit table alias 用变量名引用;关掉则手动写别名。
- 复用靠片段:把通用 SQL 存成片段,用
{{snippet: 名称}}插入,每个问题分别映射不同表。 - 预览验证:用编辑器上方的眼睛图标预览变量替换后的完整 SQL,确认 schema、表名与别名正确。
- 牢记限制:不能接仪表板筛选器、仅限 SQL 查询、无输入框、暂不支持 transforms。
进一步阅读
- SQL parameters(SQL 参数总览)
- Snippets(SQL 片段)
- Field filters(字段筛选变量)
- SQL troubleshooting guide(SQL 故障排查指南)
【免费下载链接】metabaseThe easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:项目地址: https://gitcode.com/GitHub_Trending/me/metabase
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考