Skip to main content

Command Palette

Search for a command to run...

การจัดการโมเดลใน dbt และการทดสอบ

Updated
3 min read
K

I'm a data architect from Thailand. :)

ก่อนหน้านี้ผมเขียนไป 2 บทความเกี่ยวกับ dbt ถ้าใครยังไม่ได้อ่าน ขอให้ลองย้อนกลับไปอ่านก่อนนะครับ 😉

เนื้อหาในบทความนี้อยากจะกล่าวถึง 2 ส่วนหลักๆ คือ การจัดการโมเดล (เบื้องต้น) ใน dbt และการทดสอบ

การจัดการโมเดล

ซึ่งจากบทความก่อนหน้าเราได้สร้างโมเดลหน้าตาประมาณนี้ไป

-- models/customer_orders.sql

select
    dbt_tutorial.raw_customers.id as customer_id,
    dbt_tutorial.raw_customers.first_name,
    dbt_tutorial.raw_customers.last_name,
    dbt_tutorial.raw_orders.id as order_id,
    dbt_tutorial.raw_orders.order_date,
    dbt_tutorial.raw_orders.status

from dbt_tutorial.raw_customers
left join dbt_tutorial.raw_orders
    on dbt_tutorial.raw_customers.id = dbt_tutorial.raw_orders.user_id

ซึ่งจริงๆ ก็สามารถนำเอาไปใช้ได้ปกติแหละ ไม่ได้มีปัญหาอะไร แต่ในระยะยาวแล้ว ถ้าต่างคนต่างทำ Ad-Hoc Query แบบนี้ไปเรื่อยๆ การดูแลรักษาโค้ดของเราได้ลำบากมากขึ้นเรื่อยๆ เรามักจะเขียน SQL ในลักษณะที่เป็น Modular ประมาณนี้แทน 👇🏻 โดยเอา WITH clause เข้ามาช่วย

-- models/another_customer_orders.sql

with customers as (

    select
        id as customer_id,
        first_name,
        last_name

    from dbt_tutorial.raw_customers

),

orders as (

    select
        id as order_id,
        user_id as customer_id,
        order_date,
        status

    from dbt_tutorial.raw_orders

),

final as (

    select
        customers.customer_id,
        customers.first_name,
        customers.last_name,
        orders.order_id,
        orders.order_date,
        orders.status

    from customers

    left join orders using (customer_id)

)

select * from final

โค้ด SQL จะดูยาวไปไหน.. 😅 แต่ๆๆ ถ้าเรามองดูถึงความแบ่งแยกเป็นส่วนๆ เราจะเห็นได้ว่า โค้ดด้านบนเรา Query จากตาราง dbt_tutorial.raw_customers เสร็จแล้วเราก็ Query จากตาราง dbt_tutorial.raw_orders และสุดท้ายเราเอา 2 ตารางนี้มา Join กันด้วย Key ร่วม customer_id กลายเป็นตารางที่ชื่อ final

การที่เราทำแบบนี้ บวกกับความสามารถของ dbt แล้ว มันทำให้เราสามารถ Reuse Query ที่เคยเขียนไปแล้วได้ครับ เราสามารถแบ่งไฟล์ another_customer_orders.sql ออกมาเป็น 3 ไฟล์ได้ตามนี้

-- models/customers.sql

with customers as (

    select
        id as customer_id,
        first_name,
        last_name

    from dbt_tutorial.raw_customers

)

select * from customers
-- models/orders.sql

with orders as (

    select
        id as order_id,
        user_id as customer_id,
        order_date,
        status

    from dbt_tutorial.raw_orders

)

select * from orders

และไฟล์สุดท้าย เราจะใช้ ฟังก์ชั่น ref ซึ่งเป็นฟังก์ชั่นที่สำคัญมากแทบจะที่สุดแล้วของ dbt ที่เราจะเอาไว้ Reference ไปยังโมเดลอื่นๆ ตรงนี้ก็หมายความว่าเราสามารถ Reuse ตัวโมเดล customers กับ orders ได้แล้ว! 🤩

-- models/final.sql

with customers as (

    select * from {{ ref('customers') }}

),

orders as (

    select * from {{ ref('orders') }}

),

final as (

    select
        customers.customer_id,
        customers.first_name,
        customers.last_name,
        orders.order_id,
        orders.order_date,
        orders.status

    from customers

    left join orders using (customer_id)

)

select * from final

เวลาเราจะรัน dbt เนื่องจากเรามี 3 โมเดล เราจะสั่งตามนี้

dbt run --model final customers orders

เราก็จะได้ผลลัพธ์ตามนี้

dbt-dependency-handling

สังเกตว่า dbt เค้าจัดการ Dependency ระหว่างโมเดลให้ด้วยนะ dbt จะไปสร้างโมเดล customers ก่อน ตามด้วย orders และสุดท้ายก็จะมารัน final

ใน PostgreSQL เราก็จะได้มา 3 Views ตามนี้

three-views-created-in-postgresql

การทดสอบข้อมูล

การทดสอบจะแบ่งออกเป็น 2 อย่าง (อ่านเพิ่มเติมได้ที่ dbt Documentation)

  1. Data Tests - จะรัน Specific Query กับ Specific Models
  2. Schema Tests - จะ General กว่า แล้วมี 4 แบบ คือ unique, not_null, relationships และ accepted_values

มาพูดถึง Data Tests กันก่อน ตรงนี้ถ้าใครไม่ค่อยคุ้นกับ SQL อาจจะสับสนเล็กน้อยตอนที่เขียน ผมเองก็ด้วย 😂 คือเราจะเขียน SQL ที่จะได้ผลลัพธ์เป็นข้อมูลที่ "ไม่ควร" มีอยู่ หรือถ้าพูดเป็นภาษาอังกฤษก็ A data test passes if the number of records returned is 0.

ยกตัวอย่างเช่น ถ้าเราต้องการที่จะทดสอบข้อมูลสุดท้ายว่า มันไม่ควรจะมีลูกค้าที่ชื่อ Michael ในข้อมูลของเราเลยนะ เราจะเขียน SQL ประมาณนี้ เอาไว้ที่โฟลเดอร์ tests

-- tests/assert_michael_should_not_be_included.sql

select
    *

from dbt_tutorial.final

where first_name = 'Michael';

เวลาเรารัน จะสั่งตามนี้

dbt test --data

ผลลัพธ์ที่ได้ก็ตามคาด

dbt-data-test-failed

ซึ่งก็ควรจะ Fail จริงครับ เพราะว่าในข้อมูลผมมี Michael อยู่เนอะ 😎 ตรงนี้ก็สามารถเอาไปประยุกต์กันต่อได้ครับ เป็นการตรวจสอบ Data Quality ของเราไปในตัว

มาดู Schema Tests กันบ้าง เป็นอีกการทดสอบหนึ่งที่ช่วยให้เราตรวจสอบ Data Quality ได้เช่นกัน การใช้งานเราจะสร้างไฟล์ .yml ไว้ และควรจะวางไว้ใกล้ๆ โมเดลของเราเลย คือในโฟลเดอร์ models ตามนี้

# models/schema.yml

version: 2

models:
  - name: final
    columns:
      - name: status
        tests:
          - not_null
          - accepted_values:
              values: ['placed', 'shipped', 'completed', 'returned', 'return_pending']

จากไฟล์จะหมายความว่าผมกำลังจะทดสอบข้อมูลใน final ว่าในคอลัมน์ status จะต้องไม่มีค่า NULL นะ แล้วจะต้องมีแค่ค่า 5 ค่านี้ placed, shipped, completed, returned และ return_pending เท่านั้น

เราจะรันแบบนี้ (ไม่มี --data)

dbt test --model final

ผลลัพธ์ที่ได้คือ

dbt-schema-test-failed

หมายความว่าในข้อมูลผมจะมี status ที่เป็น NULL อยู่นั่นเอง แล้วก็มี Accepted Values ครบตามที่ผมกำหนด ไม่มีนอกเหนือจากนี้

สรุป

OK! เป็นอย่างไรกันบ้างครับกับ dbt 😄 จากบทความนี้น่าจะได้เริ่มเห็นถึงความสามารถของ dbt กันแล้วเนอะ ทั้งการจัดการโมเดล และการทดสอบ ใครได้ลองเอาไปใช้แล้ว หรือตรงไหนที่ดูเจ๋งๆ ก็เอามาแชร์กันได้นะ ☺️

Source Code ทั้งหมดผมเอาไว้ที่นี่ 👇🏻 ลองไปเล่นกันได้ ถ้าเจอบั๊กก็เปิด Pull Request แก้กันมาได้เลยครับ 😂

More from this blog

Data Engineer Cafe: Data Engineering Center of Excellence ของคนไทย เพื่อคนไทย

ตอนนี้ผมพยายามอยากจะสร้าง community ของชาว data engineer ในเมืองไทย เป้าหมายคืออยากจะเป็น center of excellence ที่ใครก็ตามอยากทำงานในสายงานนี้ หรือมีความสนใจจะเข้ามาพูดคุยแลกเปลี่ยนความคิดเห็นกัน ก็จะสามารถเข้ามาแสดงความคิดเห็นได้อย่างมีอิสระเสรีภาพเ...

Apr 17, 20221 min read452
Data Engineer Cafe: Data Engineering Center of Excellence ของคนไทย เพื่อคนไทย

มาลองเล่น Dagster เบื้องต้นกัน

Dagster ตามชื่อของมันเลยคือ data orchestrator สำหรับงานพวก machine learning, analytics และ ETL ซึ่งเป็นเครื่องมือที่ช่วยเรื่อง workflow management อีกตัวหนึ่งที่กำลังมาแรงไม่แพ้ Airflow เลยทีเดียว จริงๆ แล้ว Nick Schrock (ผู้พัฒนา) ไม่ได้ตั้งใจจะทำอ...

Sep 24, 20213 min read1.2K
มาลองเล่น Dagster เบื้องต้นกัน

Scraping ข้อมูลประกาศรับสมัคร Data Engineer บนเว็บ Indeed.com

เกริ่น ในสายงาน Data Science & Engineering การทำ Scraping เป็นวิธีหนึ่งในการดึงข้อมูลจากระบบหนึ่งๆ ในกรณีที่เค้าไม่ได้มี API ไว้ให้เราใช้ อย่างเช่น หน้าเว็บไซต์ต่างๆ หรือข้อมูลที่อยู่ในไฟล์พวก PDF เป็นต้น ถ้าเรามีความสามารถในการเขียนโค้ดทำ Scraping ไ...

Aug 28, 20212 min read719
Scraping ข้อมูลประกาศรับสมัคร Data Engineer บนเว็บ Indeed.com

รีวิวหนังสือ "Data Pipelines Pocket Reference: Moving and Processing Data for Analytics"

เป็นหนังสือที่เขียนโดย James Densmore, Director of Data Infrastructure ที่ HubSpot ขอสรุปออกมา 3 ประโยค 😎 ครบเครื่องในการออกแบบและสร้าง Data Pipeline ที่ดี เริ่มตั้งแต่ Data Pipeline คืออะไร มีบทบาทอย่างไรใน Data Infrastructure แนวทางการออกแบบ และ...

Aug 26, 20211 min read888
รีวิวหนังสือ "Data Pipelines Pocket Reference: Moving and Processing Data for Analytics"

Kan Ouivirach

13 posts

Data Craftsman. Passionate in software engineering, data engineering, and data science. ❤️