跳至主要内容

使用 dbt 实现语义层:实践指南

学习如何使用 dbt 构建语义层,将原始数据转化为一致的指标,服务于分析与 BI 工具!
· 8分钟 阅读

使用 AI 探索

许多现代公司依赖语义层来弥合存储在数据仓库(如 Snowflake)中的原始数据与仪表板或报告中的洞见之间的差距。

语义层是一种抽象,用于为分析和报告定义一致的指标、度量和维度。

在本教程中,我们将探索如何使用 dbt(data build tool) 实现语义层,这是一种流行的数据转换与建模框架。本文包含实用示例与要点,帮助您深入掌握这一概念。

什么是语义层?为什么重要?

语义层将原始数据转换为一致、可复用的指标与维度,简化数据分析。它有助于在团队与工具之间保持统一。

语义层的优势

  • 一致的数据定义: 确保营收、流失率、同比增长、平均订单额等业务指标统一定义。这样可消除不同团队使用不一致定义带来的困惑,帮助在整个组织内建立对数据的信任。
  • 增强协作: 促进技术与非技术团队之间更好的沟通与对齐。通过提供统一的数据层,技术团队可专注于数据准确性,而业务团队可依托清晰易用的指标进行战略决策。
  • 加速洞见产出: 减少歧义,加快决策过程。借助语义层,相关方可快速访问并分析可靠指标,减少对账或处理差异所耗费的时间。

为何 dbt 适合构建语义层

让我们看看 dbt 如何成为数据与分析工程师构建语义层的理想工具。

展示使用 dbt 进行建模架构的截图

dbt 语义层概念图,由 dbt Labs 提供

模块化数据建模

dbt 允许将复杂的数据转换拆分为可复用的模块化模型。这些模型可按层次构建,基础数据模型为更高层的分析模型提供输入。

这种方法让数据管道更易维护,并通过将变更隔离到特定模型而不影响整个系统,从而提升团队协作效率。

如需进一步了解 dbt 的基础概念与数据建模最佳实践,请参阅《dbt 教程:数据工程师必知的 7 个概念》。该教程提供的见解与本文策略相辅相成。

集中化指标定义

借助 dbt,可在同一处定义业务逻辑与指标,确保在各类分析工具中的一致性。利用 dbt 的 metrics 功能,您可以一次性定义营收、转化率或流失等 KPI,并在各类仪表板、报告与分析中复用,无需重复定义。这降低了差异并确保数据完整性。

版本控制与文档

dbt 与Git 集成,提供强大的版本控制,便于跟踪与审计历史变更。结合其自动生成的文档,dbt 为所有数据转换与指标创建了一个可访问的动态参考库。这样既提高透明度,也便于新成员通过浏览已记录的数据管道快速上手。

与 BI 工具集成

dbt 可与 Looker、Tableau、Mode 等 BI 平台协作,将语义层集成到可视化工具中。借助 dbt exposures,您可以将 dbt 模型直接关联到 BI 仪表板,确保数据模型的任何更新都能自动反映到分析中,无需手动介入。

设置您的 dbt 项目

通常,dbt 运行在云数据仓库之上,如 Snowflake 或 Google BigQuery。不过,为了简化本教程,我们将在本地连接到一个 DuckDB 数据库。

1. 先决条件

  • 如果尚未安装,请在您的机器上安装 Python。

2. 安装必需工具

使用 pip 安装 dbt CLI:

pip install dbt-core

通过 pip 安装 DuckDB:

pip install duckdb

通过 pip 安装 DuckDB 适配器:

pip install dbt-duckdb

3. 初始化 dbt 项目

创建一个新的 dbt 项目:

dbt init project_name

该命令会在当前目录下创建一个项目,结构如下:

project_name/
├── analyses/
├── logs/
├── macros/
├── models/
├── seeds/
├── snapshots/
├── tests/
├── .gitignore
├── dbt_project.yml

4. 设置 DuckDB 连接

编辑 profiles.yml 文件(通常位于 ~/.dbt/ 文件夹)。如果不存在,请创建一个:

project_name:  # This matches the name in dbt_project.yml file
  target: dev
  outputs:
    dev:
      type: duckdb
      path: path/to/demo_database.duckdb  
      threads: 1

profiles.yml 包含指向 .duckdb 数据库文件位置的配置,您需要在 path 中指定。

您可以新建一个数据库,或从此处下载一个小型示例数据库。在下一节中,我将使用同一来源的 duckdb-demo.duckdb 文件。

要创建一个新的 DuckDB 数据库文件,只需运行以下代码:

import duckdb
duckdb.connect('/path/to/your/duckdb/database.duckdb')

5. 测试连接

现在,运行 dbt debug 测试一切是否正常。运行命令前请确保位于项目文件夹:

cd /path/to/your/project/
dbt debug

如果设置正确,您将看到类似如下的返回结果(为保护隐私,已对路径进行省略):

15:20:00  Running with dbt=1.9.1
15:20:00  dbt version: 1.9.1
15:20:00  python version: 3.11.5
15:20:00  python path: ...
15:20:00  os info: Windows-10-10.0.22631-SP0
15:20:00  Using profiles dir at ....
15:20:00  Using profiles.yml file at ...
15:20:00  Using dbt_project.yml file at ...
15:20:00  adapter type: duckdb
15:20:00  adapter version: 1.9.1
15:20:00  Configuration:
15:20:00    profiles.yml file [OK found and valid]
15:20:00    dbt_project.yml file [OK found and valid]
15:20:00  Required dependencies:
15:20:00   - git [OK found]

15:20:00  Connection:
15:20:00    database: demo_database
15:20:00    schema: main
15:20:00    path: ...
15:20:00    config_options: None
15:20:00    extensions: None
15:20:00    settings: {}
15:20:00    external_root: .
15:20:00    use_credential_provider: None
15:20:00    attach: None
15:20:00    filesystems: None
15:20:00    remote: None
15:20:00    plugins: None
15:20:00    disable_transactions: False
15:20:00  Registered adapter: duckdb=1.9.1
15:20:01    Connection test: [OK connection ok]

使用 dbt 创建语义层

恭喜您完成以上步骤!您已成功设置了第一个 dbt 项目。

现在,让我们看看如何在数据库之上创建语义层。为此,您可以将自己的 CSV 数据导入 DuckDB,或从这里下载一个小型(5MB)示例数据库文件。

然后,我们可以编写一个简单的 Python 脚本来探索该数据库文件包含的内容:

import duckdb

# Connect to the database
conn = duckdb.connect("duckdb-demo.duckdb")

# Get list of tables
tables = conn.execute(
    "SELECT table_name FROM information_schema.tables WHERE table_schema='main'"
).fetchall()

print(f"\nNumber of tables in database: {len(tables)}")

print("Table names:")
for t in tables:
    print(f"- {t[0]}")

print("\nTables in database:")
for table in tables:
    print(f"- {table[0]}")

    # Get schema for each table
    schema = conn.execute(f"DESCRIBE {table[0]}").fetchall()
    print("\nSchema:")
    for col in schema:
        print(f"  {col[0]}: {col[1]}")

    # Preview data
    data = conn.execute(f"SELECT * FROM {table[0]} LIMIT 5").fetchall()
    print("\nFirst 5 rows:")
    for row in data:
        print(f"  {row}")
    print("\n---")

# Close connection
conn.close()

输出(为简洁起见已截断):

Number of tables in database: 17
Table names:
- bank_failures
- boxplot
- calendar
- ...

Tables in database:
- bank_failures

Schema:
  c1: BIGINT
  Bank: VARCHAR
  City: VARCHAR
  State: VARCHAR
  Date: DATE
  Acquired by: VARCHAR
  Assets ($mil.): DOUBLE

First 5 rows:
  (1, 'Douglass National Bank', 'Kansas City', 'Missouri', datetime.date(2008, 1, 25), 'Liberty Bank and Trust', 58.5)
  (2, 'Hume Bank', 'Hume', 'Missouri', datetime.date(2008, 3, 7), 'Security Bank', 18.7)
...
…

该数据库包含 17 张表。但在本教程中我们仅使用 bank_failures 表。该表包含美国失败银行的历史财务细节。

1. 定义 SQL 模型

SQL 模型是任何 dbt 项目的基础。它们代表将原始数据结构化转换为更适合分析的格式。

在本教程中,我们将在 models/ 文件夹下定义两个 SQL 文件。

在项目的 models 文件夹中新建一个名为 base_bank_failures.sql 的文件:

SELECT *
FROM bank_failures

该文件直接从 bank_failures 表提取原始数据,作为基础模型,后续模型可引用。

随后,新建另一个名为 clean_bank_failures.sql 的文件:

SELECT
    State,
    COUNT(*) AS total_failures,
    SUM("Assets ($mil.)" ) AS total_assets
FROM
    {{ ref('base_bank_failures') }}
GROUP BY
    State

该文件基于 base_bank_failures 模型(使用 ref() 函数)进行聚合与清洗,为分析做准备。它按州汇总失败次数与资产。

2. 创建 metrics.yml 文件

metrics.yml 用于定义集中、可复用的指标,确保在分析与仪表板中的一致性。

在我们的场景中,它将描述如 total_assets 与 avg_assets_per_failure 等指标,使其可被 BI 工具与其他查询直接使用:

version: 2
models:
  - name: clean_bank_failures
    description: "Aggregated data about bank failures."
    columns:
      - name: State
        tests:
          - unique
      - name: "Assets ($mil.)"
    metrics:
      - name: total_assets
        label: Total Assets
        type: sum
        expression: "Assets ($mil.)"
        description: "Total assets of failed banks, aggregated by state."

      - name: avg_assets_per_failure
        label: Average Assets per Failure
        type: average
        expression: "Assets ($mil.)"
        description: "Average assets for each bank failure."

3. 创建 exposures.yml 文件

exposures.yml 将您的 dbt 模型关联到 Tableau 或 Looker 等外部工具。它记录这些连接,确保可追溯与一致性。

该文件指定 clean_bank_failures 模型被一个 Tableau 仪表板使用,并提供负责人与依赖的详情:

exposures:
  - name: tableau_dashboard
    type: dashboard
    url: https://tableau.yourcompany.com/workbook
    owner:
      name: Data Team
      email: datateam@yourcompany.com
    depends_on:
      - ref('clean_bank_failures')

构建与测试语义层

要成功验证并构建您的 dbt 项目,可使用以下常用命令:

1. 构建模型: 运行 dbt build 以实体化所有模型、执行测试,并运行 seeds 与 snapshots。该命令合并了 dbt run 与 dbt test 的功能:

dbt build

2. 测试模型: 如果只想在构建后用测试验证数据,请使用 dbt test。它会对模型应用所有数据质量测试(例如唯一性与非空检查)。

dbt test

3. 运行特定模型或测试: 若只需运行特定模型或测试,可将其名称作为参数。例如,仅运行某个模型:

dbt run --select clean_bank_failures

测试某个特定列:

dbt test --select clean_bank_failures.State

将语义层与 BI 工具集成

要将 dbt 连接到 Looker 或 Tableau 等 BI 工具,请遵循以下步骤,有效暴露语义层并确保 BI 工具可直接引用 dbt 模型与指标:

1. 在 dbt 中启用 exposures

在 dbt 项目的 models 文件夹下创建 exposures.yml 文件,内容如下:

exposures:
  - name: tableau_dashboard
    type: dashboard
    url: https://tableau.yourcompany.com/workbook
    owner:
      name: Your Name
      email: email@yourcompany.com
    depends_on:
      - ref('clean_bank_failures')

2. 准备数据库连接

  • 确保转换后的数据模型已在您的数据库(如 Snowflake、BigQuery、DuckDB)中可用。
  • 运行 dbt build 将 dbt 模型实体化到数据库中。

3. 将 Tableau 连接到 dbt 模型

  • 打开 Tableau Desktop,创建到数据库的新连接。
  • 选择存放 dbt 模型的架构(schema)。
  • 将 dbt 模型(例如 clean_bank_failures)拖放到 Tableau 工作区。

4. 在 Tableau 中定义指标

  • 在 Tableau 中直接使用 dbt 定义的指标(如 total_assets 或 avg_assets_per_failure)进行可视化,以确保一致性。
  • 如有需要,可创建计算字段以增强可视化效果。

5. 将 Looker 连接到 dbt 模型

  • 在 Looker 中进入管理面板,设置到数据库的连接。
  • 为每个 dbt 模型创建一个 LookML 视图,导入其架构与字段。
  • 使用 dbt 中定义的 total_assets 或 avg_assets_per_failure 指标,构建一致的 Looker Explore 与仪表板。

使用 dbt 实现语义层的最佳实践

既然您已了解用 dbt 构建语义层的流程,让我们回顾一些最佳实践。

集中业务逻辑

在 dbt 中定义所有业务规则,以确保一致性与可维护性。通过集中业务逻辑,您可消除跨工具的冗余定义,打造一个所有相关方都能轻松更新与理解的单一真实来源。

模块化模型

将转换拆分为逻辑清晰、可复用的组件。例如,为原始数据创建基础模型,为清洗与转换后的数据创建中间模型,为可直接用于分析的数据创建最终模型。

这种模块化结构便于排错、促进代码复用,并让团队成员能够在不干扰整个管道的情况下各自处理组件。

全面记录

利用 dbt 的自动文档,让您的语义层始终井然有序、透明可查。

在 YAML 文件中为模型、指标与列添加清晰的描述。

定期更新文档以反映变化,并使用 dbt 自动生成的血缘图来可视化模型之间的关系。这样可确保语义层对所有用户都易于访问与导航。

像 dbt-utils 包 这样的工具可显著简化您的 dbt 工作流,提供预构建的宏与工具,帮助简化模型开发并确保完善的文档。

结语

使用 dbt 实现语义层,是弥合原始数据与可行动洞见之间差距的有力方式。dbt 的模块化数据建模、集中化指标定义、稳健的版本控制以及与 BI 工具的集成,能够帮助您为分析与报告提供一致、可靠且易用的数据。

本教程带您完成了设置 dbt 项目、创建 SQL 模型、定义可复用指标,以及将语义层与 Tableau、Looker 等可视化工具集成的全过程。

如需进一步了解 dbt,请查看优秀的 《Introduction to dbt》课程!

Moez Ali

作者

Moez Ali

数据科学家,PyCaret 的创始人兼作者

主题