DHDataHọc
TheoryLesson 1/85 min read+10 XP

Model, source và ref

Bạn sẽ học được

  • dbt giải quyết bài toán gì trong một pipeline dữ liệu.
  • Model là gì và vì sao nó chỉ là một câu SELECT.
  • Khai báo nguồn thô bằng source thay vì gõ tay tên bảng.
  • Dùng ref để dbt tự sắp thứ tự chạy.
  • Đọc được cấu trúc thư mục của một project dbt tối thiểu.

Khái niệm cốt lõi

Trước dbt, phần lớn đội dữ liệu chạy ETL: lấy dữ liệu ra khỏi hệ thống nguồn, biến đổi bằng Python hoặc một công cụ ngoài, rồi ghi lại vào kho dữ liệu. Cách này có hai vấn đề: logic biến đổi nằm rải rác trong code ứng dụng, và không ai biết bảng nào được tạo từ bảng nào.

dbt đảo ngược thứ tự — ELT. Dữ liệu thô được nạp vào kho trước (bằng Fivetran, Airbyte, Airflow hay một script đơn giản), rồi biến đổi chạy ngay trong kho dữ liệu bằng SQL. dbt không chứa dữ liệu, không tự chạy theo lịch; nó là công cụ biên dịch và điều phối các câu SQL đó.

dbt biến một thư mục chứa các file .sql thành một DAG có thứ tự, có kiểm thử, có tài liệu và có lịch sử thay đổi trong Git.

Một model là một file .sql chứa đúng một câu SELECT. Không CREATE TABLE, không INSERT, không dấu chấm phẩy ở cuối. dbt đọc câu SELECT đó và tự bọc thành CREATE VIEW, CREATE TABLE hoặc MERGE tuỳ cấu hình materialization.

code
ten_project/
  dbt_project.yml
  models/
    staging/
      stg_orders.sql
      sources.yml
    marts/
      fct_orders.sql
      dim_customers.sql
  tests/
  macros/
  seeds/
Thư mụcNội dung
models/Các câu SELECT, mỗi file một model
sources.ymlKhai báo bảng thô có sẵn trong kho
tests/Kiểm thử tự viết (singular test)
macros/Hàm Jinja dùng lại
seeds/File CSV nhỏ nạp thành bảng
dbt_project.ymlCấu hình chung: tên project, đường dẫn, materialization mặc định

Vì sao phải dùng source và ref

Trong một project không có dbt, muốn biết bảng fct_orders lấy dữ liệu từ đâu, bạn phải đọc toàn bộ câu SQL và tìm các tên bảng. Khi ai đó đổi tên schema, mọi câu SQL gãy cùng lúc và không có gì báo trước.

dbt thay tên bảng vật lý bằng hai hàm:

code
{{ source('raw', 'orders') }}   -- bảng thô, khai báo trong sources.yml
{{ ref('stg_orders') }}         -- model khác trong cùng project

source nói rằng "bảng này do hệ thống khác tạo, tôi chỉ đọc". ref nói rằng "bảng này do chính project này tạo ra". Từ hai khai báo đó, dbt dựng được đồ thị phụ thuộc và chạy đúng thứ tự — không cần ai nhớ thứ tự bằng tay.

Ví dụ chạy được

Giả sử kho dữ liệu có hai bảng thô: raw.orders và raw.customers.

Bước 1 — khai báo nguồn. Tạo models/staging/sources.yml:

code
version: 2

sources:
  - name: raw
    schema: raw
    tables:
      - name: orders
        loaded_at_field: updated_at
        freshness:
          warn_after: { count: 12, period: hour }
          error_after: { count: 24, period: hour }
      - name: customers

Bước 2 — viết model staging. models/staging/stg_orders.sql:

code
select
    order_id,
    customer_id,
    cast(created_at as date)            as order_date,
    status,
    amount,
    amount - coalesce(discount, 0)      as net_amount
from {{ source('raw', 'orders') }}
where status not in ('test', 'cancelled')

Bước 3 — viết model mart. models/marts/fct_orders.sql:

code
select
    o.order_date,
    c.segment,
    count(*)                as orders,
    sum(o.net_amount)       as revenue
from {{ ref('stg_orders') }} o
join {{ ref('dim_customers') }} c using (customer_id)
group by 1, 2

Bước 4 — chạy.

code
dbt run --select fct_orders+

Dấu + phía sau nghĩa là "và mọi thứ phụ thuộc vào nó". dbt đọc ref bên trong fct_orders, thấy cần stg_orders và dim_customers, tự chạy hai model đó trước rồi mới chạy fct_orders. Bạn không cần biết thứ tự.

Bước 5 — xem kết quả. dbt in ra tên model và thời gian:

code
1 of 3 OK created sql view model analytics.stg_orders ......... [CREATE VIEW in 0.42s]
2 of 3 OK created sql view model analytics.dim_customers ...... [CREATE VIEW in 0.31s]
3 of 3 OK created sql view model analytics.fct_orders ......... [CREATE VIEW in 0.55s]
Completed successfully

Tên schema analytics lấy từ target trong profiles.yml, không ghi cứng trong SQL. Cùng một project chạy trên máy cá nhân và trên production sẽ ghi vào hai schema khác nhau.

ref hoạt động thế nào

Khi biên dịch, dbt thay {{ ref('stg_orders') }} bằng tên đầy đủ theo target hiện tại:

code
-- trước khi biên dịch
from {{ ref('stg_orders') }}

-- sau khi biên dịch với target dev
from "dev"."analytics"."stg_orders"

Đây là lý do dbt an toàn khi chạy song song nhiều môi trường. Nếu bạn gõ tay from analytics.stg_orders, model sẽ đọc nhầm dữ liệu production khi chạy ở nhánh phát triển.

Cùng cơ chế đó, ref cho phép chọn theo đồ thị:

LệnhÝ nghĩa
dbt run --select stg_ordersChỉ một model
dbt run --select stg_orders+Model và mọi thứ phụ thuộc nó
dbt run --select +fct_ordersModel và mọi thứ nó cần
dbt run --select +fct_orders+Toàn bộ nhánh quanh model
dbt run --select tag:nightlyTheo tag khai báo trong config

Thực hành

  1. Tạo project bằng dbt init ten_project, mở dbt_project.yml và xác định name, profile.
  2. Viết sources.yml cho hai bảng thô bất kỳ trong kho của bạn.
  3. Viết stg_ đầu tiên chỉ gồm select * from {{ source(...) }}, chạy dbt run.
  4. Thêm một cột tính toán, chạy lại và kiểm tra bằng select * from analytics.stg_ten_bang limit 10.
  5. Chạy dbt run --select +ten_model và đọc danh sách model được chọn.

Lỗi thường gặp

Triệu chứngNguyên nhânCách sửa
Compilation Error: model not foundGõ sai tên trong ref hoặc file chưa nằm trong models/Kiểm tra tên file khớp tên ref, chạy dbt parse
relation does not existModel nguồn chưa chạyChạy bằng --select +model thay vì chọn lẻ
Dữ liệu đọc từ production khi đang devGõ cứng tên schema thay vì dùng refThay mọi tên bảng bằng ref/source
dbt run thành công nhưng bảng rỗngĐiều kiện where lọc hết dữ liệuChạy câu SELECT trực tiếp trên kho để kiểm tra
Model chạy sai thứ tựKhông khai báo quan hệ qua refThêm ref thay vì trỏ thẳng tên bảng

Tóm tắt

  • dbt biến đổi dữ liệu trong kho, không di chuyển dữ liệu.
  • Một model là một câu SELECT; dbt tự bọc thành view hoặc table.
  • source cho bảng thô, ref cho model khác — cả hai tạo ra DAG.
  • ref tự đổi tên schema theo môi trường, nên không bao giờ gõ cứng tên bảng.
  • --select +model giúp chạy đúng nhánh phụ thuộc.

Use ← → to move between lessons