ในบทความนี้เราจะมาเรียนรู้การใช้ DAX เบื้องต้นกันครับ เดี๋ยวจะสอนแบบที่ว่า ไม่เคยใช้ DAX เลยก็สามารถเข้าใจได้นะ ก่อนอื่นเรามาลองดู Data ของตารางหลัก นั่นก็คือ fSales กันก่อนครับ
สารบัญ
เกิดอะไรขึ้นเมื่อลาก Field ตัวเลขลง Visual
สิ่งที่เราควรรู้คือ เราสามารถลาก Field ที่เป็นตัวเลข (สังเกตว่าที่ Field มีสัญลักษณ์ Sigma อยู่) อย่างเช่น SalesQuantity ลง Visual ได้เลย และเราก็สามารถเลือกวิธีสรุปข้อมูลได้เช่นจะให้ Sum, Count, Average, Max, Min หรือแม้แต่ Distinct Count
เวลาใน Values เรากดที่ลูกศรลงของ SalesQuantity ก็จะเห็นวิธีสรุปที่เลือกได้มากมายดังนี้
ค่า 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
แล้วเขียนสูตรในรูปแบบ
ชื่อ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 ไว้เลย
ไม่ว่าจะลาก Measure นั้นลงไปที่ Visual ใดก็ตาม เราก็จะได้ Number format นั้นไปเลย โดยไม่ต้องมานั่งเปลี่ยนใหม่ทุกครั้ง
ดังนั้นแม้มันจะเป็นการคำนวณง่ายๆ โง่ๆ อย่าง SUM ก็ทำ Explicit Measure ให้เป็นนิสัยเถอะครับ ^^
คำนวณ Total Revenue
บอกลา VLOOKUP
ต่อไปสมมติว่าเราอยากจะคำนวณยอดขาย สิ่งที่เราต้องทำคือเอา SalesQuantity มาคูณกับ UnitPrice ในแต่ละแถว
แต่ปัญหาก็คือ Unit Price มันดันอยู่อีกตารางนึง ซึ่งถ้าเป็น Excel เราคงต้องใช้สูตรแนว VLOOKUP ดึงข้อมูลมาก่อนใช่มะ?
แต่ใน DAX เราสามารถสร้าง New Column แล้วใช้ฟังก์ชันที่ชื่อว่า RELATED ดึงข้อมูลจากอีกตารางนึงมาได้เลย ข้อแม้คือต้องผูก Relationship แล้วเท่านั้น (และต้อง active ด้วย) ซึ่งวิธีเขียนสูตรมันง่ายมากกว่า VLOOKUP หลายร้อยเท่า เพราะแค่เลือกว่าต้องการข้อมูลในคอลัมน์ไหนแค่นั้นเอง (ห๊ะ!)
นี่ไง เราได้ผลลัพธ์มาอย่างง่ายดาย แบบเรียบรู้ด้วยเวลาไม่ถึง 10 วินาที (ตอนเรียน VLOOKUP ครั้งแรกยากขนาดไหนคิดดู)
แบบนี้ถ้าอยากได้ยอดขายต่อ 1 แถว ก็สร้าง New Column ขึ้นมาด้วยสูตรว่า
LineRevenue = fSales[SalesQuantity]*fSales[UnitPrice]
และถ้าอยากได้ 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 นี้
หรือพูดอีกอย่างได้ว่า ก็คือ 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 ตัวเดิมที่เราทำไว้เป๊ะเลยครับ
ลบ Column และ Measure ที่ไม่จำเป็นซะ
ดังนั้นแปลว่า ใน fSales เราไม่จำเป็นต้องมีคอลัมน์ UnitPrice กับ LineRevenue อีกต่อไป ก็สามารถคลิ๊กขวา Delete ทิ้งได้เลย
ซึ่งพอกดลบคอลัมน์แล้ว 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 จะได้เห็นเลขเป็น % ไปด้วย
สรุป 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
- POWER BI ตอนที่ 01: POWER BI คืออะไร?
- POWER BI ตอนที่ 02: พื้นฐาน EXCEL ที่สำคัญก่อนจะเรียนรู้ POWER BI
- POWER BI ตอนที่ 03: ภาพรวมการใช้งาน POWER BI DESKTOP
- POWER BI ตอนที่ 04: สร้าง REPORT แรก ใน POWER BI
- POWER BI ตอนที่ 05: วิธีการ DRILL เพื่อเจาะลึกข้อมูลใน REPORT
- POWER BI ตอนที่ 06: การปรับแต่งสีใน VISUAL ด้วย CONDITIONAL FORMAT
- POWER BI ตอนที่ 07: เริ่ม GET DATA ตั้งแต่ไฟล์ยังว่างเปล่า
- POWER BI ตอนที่ 08: สร้าง DATA MODEL ที่เหมาะสม
- POWER BI ตอนที่ 09: สร้าง DATE TABLE ด้วย DAX
- POWER BI ตอนที่ 10: เรียนรู้ DAX เบื้องต้น
- POWER BI ตอนที่ 11: เรียนรู้ DAX Table Function – FILTER
- POWER BI ตอนที่ 12: DISTINCT, VALUES, ALL และผองเพื่อน
- POWER BI ตอนที่ 13: CALCULATE ฟังก์ชันที่ทรงพลังที่สุดใน DAX
- Power BI ตอนที่ 14: Context Transition และ พลังแฝงใน Measure
- Power BI ตอนที่ 15: วิธีดึงค่าจาก Slicer มาคำนวณใน Report
- Power BI ตอนที่ 16 : เดินทางข้ามเวลาไปกับ Time Intelligence DAX Function
- Power BI ตอนที่ 17 : วิธีทำรายงานเทียบเป้าหมาย Target vs Actual
- Power BI ตอนที่ 18 : วิธีการกระจายเป้า Allocate Target ด้วย DAX
- Power BI ตอนที่ 19 : การปรับ Cross Filter Direction เพื่อคำนวณค่าในตาราง Dimension
- ส่วนเสริม
- การคำนวณต้นทุนแบบ FIFO ด้วย DAX
- แสดงข้อมูลสรุปแบบ Top N + Others (ฉบับเทพเอ็กเซล)
- การวิเคราะห์ Event ที่มีช่วงเวลาเริ่มต้นกับสิ้นสุดคนละวัน
- เปรียบเทียบ MAX vs LASTDATE ในภาษา DAX
ใครสนใจอยากเรียนเป็นคลิปวีดีโอ ผมมี