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
sourcethay 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
.sqlthà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.
ten_project/
dbt_project.yml
models/
staging/
stg_orders.sql
sources.yml
marts/
fct_orders.sql
dim_customers.sql
tests/
macros/
seeds/| Thư mục | Nội dung |
|---|---|
models/ | Các câu SELECT, mỗi file một model |
sources.yml | Khai 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.yml | Cấ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:
{{ source('raw', 'orders') }} -- bảng thô, khai báo trong sources.yml
{{ ref('stg_orders') }} -- model khác trong cùng projectsource 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:
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: customersBước 2 — viết model staging. models/staging/stg_orders.sql:
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:
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, 2Bước 4 — chạy.
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:
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 successfullyTê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:
-- 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_orders | Chỉ một model |
dbt run --select stg_orders+ | Model và mọi thứ phụ thuộc nó |
dbt run --select +fct_orders | Model và mọi thứ nó cần |
dbt run --select +fct_orders+ | Toàn bộ nhánh quanh model |
dbt run --select tag:nightly | Theo tag khai báo trong config |
Thực hành
- Tạo project bằng
dbt init ten_project, mởdbt_project.ymlvà xác địnhname,profile. - Viết
sources.ymlcho hai bảng thô bất kỳ trong kho của bạn. - Viết
stg_đầu tiên chỉ gồmselect * from {{ source(...) }}, chạydbt run. - 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. - Chạy
dbt run --select +ten_modelvà đọc danh sách model được chọn.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
Compilation Error: model not found | Gõ 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 exist | Model nguồn chưa chạy | Chạy bằng --select +model thay vì chọn lẻ |
| Dữ liệu đọc từ production khi đang dev | Gõ cứng tên schema thay vì dùng ref | Thay 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ệu | Chạ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 ref | Thê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. sourcecho bảng thô,refcho model khác — cả hai tạo ra DAG.reftự đổi tên schema theo môi trường, nên không bao giờ gõ cứng tên bảng.--select +modelgiúp chạy đúng nhánh phụ thuộc.
Dùng ← → để chuyển bài