Power BI ตอนที่ 10: เรียนรู้ DAX เบื้องต้น 1

Power BI ตอนที่ 10: เรียนรู้ DAX เบื้องต้น

ในบทความนี้เราจะมาเรียนรู้การใช้ DAX เบื้องต้นกันครับ เดี๋ยวจะสอนแบบที่ว่า ไม่เคยใช้ DAX เลยก็สามารถเข้าใจได้นะ ก่อนอื่นเรามาลองดู Data ของตารางหลัก นั่นก็คือ fSales กันก่อนครับ

Power BI ตอนที่ 10: เรียนรู้ DAX เบื้องต้น 2

เกิดอะไรขึ้นเมื่อลาก Field ตัวเลขลง Visual

สิ่งที่เราควรรู้คือ เราสามารถลาก Field ที่เป็นตัวเลข (สังเกตว่าที่ Field มีสัญลักษณ์ Sigma อยู่) อย่างเช่น SalesQuantity ลง Visual ได้เลย และเราก็สามารถเลือกวิธีสรุปข้อมูลได้เช่นจะให้ Sum, Count, Average, Max, Min หรือแม้แต่ Distinct Count

Power BI ตอนที่ 10: เรียนรู้ DAX เบื้องต้น 3

เวลาใน Values เรากดที่ลูกศรลงของ SalesQuantity ก็จะเห็นวิธีสรุปที่เลือกได้มากมายดังนี้

Power BI ตอนที่ 10: เรียนรู้ DAX เบื้องต้น 4

ค่า Default ของการลากข้อมูลตัวเลขลงมาก็คือการ Sum แต่เราสามารถเปลี่ยนวิธีการสรุปข้อมูลได้ตาม Choice ที่เห็นในรูปข้างบน (เช่นเดียวกับใน PivotTable ของ Excel นั่นแหละ)

ทั้งหมดที่ทำข้างบนนี้ เป็นสิ่งที่ไม่ควรทำ!

การลาก Field ลงมาที่ Visual ตรงๆ เพื่อให้เกิดการสรุปแบบอัตโนมัติแบบนี้ เป็นการสร้าง Measure แบบที่เรียกว่า Implicit Measure ซึ่งเป็นสิ่งที่กูรูเรื่อง DAX บอกว่าเป็นสิ่งที่ไม่ควรทำ โดยที่เค้าแนะนำให้เราตั้งใจสร้าง Measure โดยเขียนสูตรการคำนวณขึ้นมาเองด้วย DAX จะดีกว่า

ข้อดีของการสร้าง Measure ขึ้นมาเอง

  • Number format ติดตัวอยู่ที่ Measure
  • ชื่อไม่สับสน (ปกติถ้าลากด้วยวิธีการ Sum มันจะใช้ชื่อ Field มาให้เลย โดยที่ไม่มีคำว่า Sum โผล่มาด้วย ทำให้อาจจะสับสนได้ว่ามันสรุปด้วยวิธีไหน)
  • เขียนการคำนวณที่ซับซ้อนได้
  • เวลาเอาเข้า Power BI Service แล้วไม่มีปัญหากับการกด Analyze in Excel

ดังนั้น มาเริ่มสร้าง Measure (Explicit Measure) กันเถอะ!!

คำนวณ Total Sales Quantity

การสร้าง Measure ขึ้นมาใหม่ ให้กดปุ่ม New Measure

Power BI ตอนที่ 10: เรียนรู้ DAX เบื้องต้น 5

แล้วเขียนสูตรในรูปแบบ

ชื่อMeasure=สูตรของMeasure

Tips : หากเราอยู่ที่ Table ไหนแล้วกดสร้าง Measure ขึ้นมา Measure ก็จะถือว่ามีที่อยู่ใน Table นั้น (เรียกว่า Home Table) แต่เราสามารถย้าย Measure ไปอยู่ที่ Table ไหนก็ได้ (คือการเปลี่ยน Home Table) โดยที่จริงๆ แล้วไม่ได้มีผลเรื่องการคำนวณอะไรทั้งสิ้น

การที่จะได้ Total Sales Quantity ซึ่งก็คือจำนวนชิ้นของสินค้าที่ขายได้ทั้งหมด เราก็ต้องเอาข้อมูลในคอลัมน์ SalesQuantity มาสรุปด้วยวิธี SUM จริงมั้ยครับ? ดังนั้นสูตรใน Measure นี้ก็คือการ SUM ง่ายๆ ธรรมดาๆ เลย

TotalSalesQuantity=SUM(fSales[SalesQuantity])

Tips : จำได้มั้ยว่าเราควรจะอ้างอิงคอลัมน์ด้วย ชื่อตาราง[ชื่อคอลัมน์]

ใส่ Number Format ให้กับ Measure

ข้อดีนึงของการสร้าง Measure ก็คือ เราสามารถกำหนด Number Format ฝังไว้กับ Measure ไว้เลย

Power BI ตอนที่ 10: เรียนรู้ DAX เบื้องต้น 6

ไม่ว่าจะลาก Measure นั้นลงไปที่ Visual ใดก็ตาม เราก็จะได้ Number format นั้นไปเลย โดยไม่ต้องมานั่งเปลี่ยนใหม่ทุกครั้ง

Power BI ตอนที่ 10: เรียนรู้ DAX เบื้องต้น 7

ดังนั้นแม้มันจะเป็นการคำนวณง่ายๆ โง่ๆ อย่าง SUM ก็ทำ Explicit Measure ให้เป็นนิสัยเถอะครับ ^^

คำนวณ Total Revenue

บอกลา VLOOKUP

ต่อไปสมมติว่าเราอยากจะคำนวณยอดขาย สิ่งที่เราต้องทำคือเอา SalesQuantity มาคูณกับ UnitPrice ในแต่ละแถว

แต่ปัญหาก็คือ Unit Price มันดันอยู่อีกตารางนึง ซึ่งถ้าเป็น Excel เราคงต้องใช้สูตรแนว VLOOKUP ดึงข้อมูลมาก่อนใช่มะ?

แต่ใน DAX เราสามารถสร้าง New Column แล้วใช้ฟังก์ชันที่ชื่อว่า RELATED ดึงข้อมูลจากอีกตารางนึงมาได้เลย ข้อแม้คือต้องผูก Relationship แล้วเท่านั้น (และต้อง active ด้วย) ซึ่งวิธีเขียนสูตรมันง่ายมากกว่า VLOOKUP หลายร้อยเท่า เพราะแค่เลือกว่าต้องการข้อมูลในคอลัมน์ไหนแค่นั้นเอง (ห๊ะ!)

Power BI ตอนที่ 10: เรียนรู้ DAX เบื้องต้น 8

นี่ไง เราได้ผลลัพธ์มาอย่างง่ายดาย แบบเรียบรู้ด้วยเวลาไม่ถึง 10 วินาที (ตอนเรียน VLOOKUP ครั้งแรกยากขนาดไหนคิดดู)

Power BI ตอนที่ 10: เรียนรู้ DAX เบื้องต้น 9

แบบนี้ถ้าอยากได้ยอดขายต่อ 1 แถว ก็สร้าง New Column ขึ้นมาด้วยสูตรว่า

LineRevenue = fSales[SalesQuantity]*fSales[UnitPrice]
Power BI ตอนที่ 10: เรียนรู้ DAX เบื้องต้น 10

และถ้าอยากได้ Total Revenue เราก็สามารถเอา LineRevenue มา SUM กันได้ ดังนั้นเราจะสร้าง New Measure ได้ว่า

TotalRevenue = SUM(fSales[LineRevenue])

เริ่มรู้จัก Row Context

ก่อนหน้านี้ผมได้มีการพูดถึง Filter Context ไว้ว่า แม้วิธีสรุปจะเหมือนกัน (Measure เหมือนกัน) แต่สามารถแสดงค่าผลลัพธ์ใน Visual ต่างกัน เพราะมี Filter Context หรือวิธีการ Filter ข้อมูลก่อนที่จะคำนวณ Measure ที่ต่างกัน

ในทำนองเดียวกัน ถ้าเราสังเกตผลลัพธ์แต่ละบรรทัดของ LineRevenue ก็จะเห็นว่าผลลัพธ์ออกมาต่างกัน ทั้งๆ ที่สูตรเขียนเหมือนกันแท้ๆ

เหตุการณ์นี้เกิดขึ้นได้เพราะว่าตอนคำนวณมันมี Row Context อยู่ ซึ่งเป็นตัวที่บ่งบอกว่าตัวมันกำลังคำนวณข้อมูลที่ Row ไหนอยู่ มันจึงรู้ว่า สูตร fSales[SalesQuantity] นั้นให้อ้างอิงจากข้อมูลที่อยู่ในแถวของตัวเองเท่านั้น ซึ่งการคำนวณมันจะทำซ้ำๆ ทีละแถวจนกว่าจะครบทุกแถวในตาราง (การทำซ้ำ เรียกว่า Iterate) ซึ่งเราจะเห็นพฤติกรรมนี้ในสูตรอย่าง SUMX ด้วย

สำหรับ Row Context ในเบื้องต้นนี้ไม่น่าจะงงอะไร เพราะมันคล้ายกับการที่เราเขียนสูตรใน Table ของ Excel มากๆ เลย จนคิดว่ามันไม่ได้มีอะไรพิสดารในการที่จะรู้ว่ากำลังคำนวณ Row ไหนอยู่ แต่เดี๋ยวพอเราเจอ Concept ที่ซับซ้อนขึ้นอย่าง SUMX และ CALCULATE เราจะได้เห็นความสำคัญของมันจริงๆ อีกทีนึงครับ

New Column หรือ New Measure ?

ถึงจุดนี้หลายคนอาจจะเริ่มสงสัยแล้วว่าเมื่อไหร่ต้อง New Column เมื่อไหร่ถึงจะ New Measure??

หลักการคือ

  • ถ้าจะสร้างให้มีคอลัมน์เพิ่มมาจริงๆ ในตารางก็ต้อง New Column ซึ่ง New Column จะถูกสร้างให้มีตัวตนอยู่จริงๆ ในตารางของเรา และมักถูกใช้ใน Visual ได้ในฐานะ Category ในแกนต่างๆ ด้วย
  • ถ้าเราต้องการสร้างผลสรุป (เหมือนตอนลากเข้าไปสรุปใน PivotTable ด้วย Sum) เราก็จะใช้ Measure ซึ่ง Measure จะไม่มีตัวตนจนกว่าเราจะลากลงไปใน Visual

ความต่างในเชิง Performance

  • การที่ New Column ถูกสร้างให้มีตัวตนจริงๆ ซึ่งเกิดการคำนวณขึ้นตอนที่ Refresh Data (ซึ่งเป็นขั้นตอนสร้าง Data Model) ซึ่งกิน Memory และ Disk Space จริงๆ
  • ส่วน Measure จะถูกคำนวณตอนที่ User ใช้งาน Report แล้วกดโน่นนี่นั่น ซึ่งจะทำให้ Filter Context เปลี่ยนไป ก็จะต้องมีการคำนวณใหม่

แปลว่ามันคือการเลือก ว่าจะช้าตอนกด Refresh และเปลือง ram+space (ก็สร้าง Column) หรือจะช้าตอน User กดเล่น Report นิดหน่อย (สร้าง Measure)

ซึ่งโดยทั่วไปเค้าจะคิดกันว่า ถ้าสร้าง Measure ได้ก็สร้าง Measure ไป เพราะมันคำนวณเร็วมาก จะได้ไม่เปลือง Ram และ Space (ลองดูก็ได้ครับ มันไม่ได้ช้าอะไรเลยตอนกดเล่น)

สร้าง Measure TotalRevenue ตรงๆ โดยไม่ต้องมีคอลัมน์ช่วยเลยก็ได้

ในความเป็นจริงแล้วเราสามารถสร้าง Measure TotalRevenue ที่คำนวณการ Sum LineRevenue โดยไม่ต้องมี UnitPrice และ LineRevenue เลยก็ได้

ทั้งนี้เราจะใช้ฟังก์ชัน SUMX มาช่วย ซึ่งมีหลักการทำงานดัง slide นี้

Power BI ตอนที่ 10: เรียนรู้ DAX เบื้องต้น 11

หรือพูดอีกอย่างได้ว่า ก็คือ SUMX สามารถจำลองการคำนวณอะไรก็ได้โดยทดไว้ในหัวมัน โดยที่มันจะสร้างการคำนวณ <expression> ในแต่ละแถวของตาราง <table>* (ตรงนี้แหละคือ Row Context ที่มันรู้ว่าต้องวิ่งคำนวณ iterate ซ้ำๆ ในแต่ละแถวของ Table ไหนจนครบตาราง) จากนั้นก็เอาผลลัพธ์ในแต่ละแถวที่ทดไว้ทั้งหมดมา SUM กัน (เพราะเราใช้ SUMX)

*ซึ่ง<table> นี่ในอนาคตเราจะได้เรียนรู้ว่า มันสามารถระบุเป็นตารางที่ไม่ได้มีอยู่จริงใน Data Model ก็ได้ เพราะเราจะใช้ DAX สร้างตารางด้วยสูตรเช่น FILTER, DISTINCT, ALL, CALCULATETABLE ได้

เช่นจำลอง fSales[SalesQuantity]*fSales[UnitPrice] แต่ละแถวของตาราง fSales

TotalRevenueSUMX =
SUMX ( fSales, fSales[SalesQuantity] * fSales[UnitPrice] )

และไหนๆ ก็ไหนๆ แล้ว เราสามารถเขียน RELATED ในส่วนของ Expression ของ SUMX เพื่อที่จะได้ไม่ต้องสร้างคอลัมน์ fSales[UnitPrice] ด้วยซ้ำ (จะได้ไม่เปลืองที่) จะได้ว่า

TotalRevenueSUMX =
SUMX(fSales,fSales[SalesQuantity]*RELATED(dProduct[UnitPrice]))

ซึ่งค่าที่ได้จะออกมาเท่ากับ TotalRevenue ตัวเดิมที่เราทำไว้เป๊ะเลยครับ

Power BI ตอนที่ 10: เรียนรู้ DAX เบื้องต้น 12

ลบ Column และ Measure ที่ไม่จำเป็นซะ

ดังนั้นแปลว่า ใน fSales เราไม่จำเป็นต้องมีคอลัมน์ UnitPrice กับ LineRevenue อีกต่อไป ก็สามารถคลิ๊กขวา Delete ทิ้งได้เลย

Power BI ตอนที่ 10: เรียนรู้ DAX เบื้องต้น 13

ซึ่งพอกดลบคอลัมน์แล้ว Measure TotalRevenue ที่เคยอ้างอิงถึงคอลัมน์เหล่านั้นก็จะพังตามไปด้วย (ไม่แปลก) ก็ให้ลบ Measure เดิมทิ้งไปด้วยเช่นกันนะ

สร้าง Total Cost of Goods Sold

ในทำนองเดียวกับ TotalRevenueSUMX เราก็สามารถคำนวณ Total Cost of Goods Sold ได้ด้วย concept เดียวกันเลย แค่เปลี่ยนจาก UnitPrice เป็น UnitCost เอง

จะได้ว่า

TotalCOGS = 
SUMX(fSales,fSales[SalesQuantity]*RELATED(dProduct[UnitCost]))

สร้าง TotalGrossProfit

Total Gross Profit เกิดจาก Total Revenue ลบด้วย Total Cost of Goods Sold ดังนั้นก็สามารถเขียนสูตรง่ายๆ โดยอ้างอิง Measure เดิมที่มีอยู่แล้วได้เลยว่า

TotalGrossProfit = [TotalRevenueSUMX]-[TotalCOGS]

Tips : จำได้มั้ยว่า ถ้าอ้างอิง Measure ให้ระบุแค่ [ชื่อmeasure] ไม่ต้องใส่ชื่อ Table นะ ตรงนี้ให้ทำเป็นนิสัยเลย เดี๋ยวมันจะส่งผลในอนาคตอย่างมากตอนเรียนเรื่อง CALCULATE ครับ

เปลี่ยนชื่อ Measure ก็ไม่ทำให้สูตรพังนะ

ชื่อ Measure เดิม TotalRevenueSUMX มันดูทุเรศไปหน่อย ให้เราคลิ๊กขวา Rename เป็น TotalRevenue เฉยๆ แหละดีกว่า ซึ่งพอทำแล้วสูตรทั้งหมดก็ยังใช้ได้อยู่นะครับ

โดยสูตร TotalGrossProfit = [TotalRevenueSUMX]-[TotalCOGS]
จะกลายเป็น TotalGrossProfit = [TotalRevenue]-[TotalCOGS] ให้เองเลย

สร้าง Gross Profit Margin

ทีนี้เราจะมาคำนวณว่ากำไรที่ได้มันเป็นกี่ % เมื่อเทียบกับยอดขาย สิ่งที่เราจะทำก็คือเอา TotalGrossProfit มาหารด้วย TotalRevenue ซึ่งเขียนได้ว่า

GrossProfitMargin=[TotalGrossProfit]/[TotalRevenue]

จริงมั้ยครับ?

แต่ใน DAX มีฟังก์ชันการหารโดยเฉพาะชื่อว่า DEVIDE ซึ่งดีกว่าการหารตรงๆ ที่มันดัก Error กรณีตัวส่วนเป็น 0 ให้แล้วด้วย ดังนั้นเราจะเขียนแบบนี้เลย

GrossProfitMargin = DIVIDE([TotalGrossProfit],[TotalRevenue])

จากนั้นอย่าลืมปรับ Format เป็น % ด้วยล่ะ ผลลัพธ์ใน Visual จะได้เห็นเลขเป็น % ไปด้วย

Power BI ตอนที่ 10: เรียนรู้ DAX เบื้องต้น 14

สรุป DAX เบื้องต้น

สรุปแล้ว สุดท้ายที่เราทำในบทนี้คือการสร้าง Measure ขึ้นมา 5 ตัว ดังนี้

TotalSalesQuantity=SUM(fSales[SalesQuantity])
TotalRevenue=SUMX(fSales,fSales[SalesQuantity]*RELATED(dProduct[UnitPrice]))
TotalCOGS =SUMX(fSales,fSales[SalesQuantity]*RELATED(dProduct[UnitCost]))
TotalGrossProfit = [TotalRevenue]-[TotalCOGS]
GrossProfitMargin = DIVIDE([TotalGrossProfit],[TotalRevenue])

ซึ่งทั้งหมดนี้คือ Measure ล้วนๆ ที่ไม่ต้องการคอลัมน์พิเศษอะไรเลยด้วยซ้ำ

สำหรับ DAX เบื้องต้นของบทความนี้ก็ขอจบเพียงเท่านี้ เดี๋ยวต่อไปเราจะเริ่มรู้จัก พวก CALCULATE, FILTER, ALL ที่จะสามารถเปลี่ยน Filter Context ได้ ซึ่งใกล้จะได้เห็นพลังของสูตรแบบ DAX ที่แท้จริงแล้วล่ะ!

สารบัญ Series Power BI

ใครสนใจอยากเรียนเป็นคลิปวีดีโอ ผมมี

อบรม In-House Training

Feedback การใช้งาน AI Chatbot