SUMX เป็น iterator function ที่ทรงพลังใน DAX ออกแบบมาเพื่อวนลูปทีละแถวในตารางที่กำหนด แล้วคำนวณ expression ที่ซับซ้อนสำหรับแต่ละแถวก่อนนำผลลัพธ์ทั้งหมดมารวมกัน ต่างจาก SUM ที่รวมเฉพาะค่าใน column เดียว SUMX สร้าง row context สำหรับแต่ละแถว ทำให้สามารถคำนวณ expression อย่าง Quantity × Price หรือใช้ RELATED ดึงข้อมูลข้ามตารางได้ มีกลไก context transition อัตโนมัติเมื่อเจอ measure ในนั้น เหมาะสำหรับการคำนวณซับซ้อนระดับแถวข้อมูล ประหยัดพื้นที่ model เพราะไม่ต้องสร้าง calculated column แต่ช้ากว่า SUM และไม่รองรับ DirectQuery mode ใน calculated columns หรือ RLS rules
=SUMX(<table>, <expression>)
=SUMX(<table>, <expression>)
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| table | table | Yes | ตารางที่ต้องการให้ SUMX iterate ทีละแถว สามารถเป็นชื่อตารางโดยตรง (เช่น Sales, Products) หรือ expression ที่ return table (เช่น FILTER, CALCULATETABLE, VALUES, SUMMARIZE) SUMX จะสร้าง row context ขึ้นมาใหม่สำหรับแต่ละแถวในตารางนี้ ทำให้สามารถอ้างถึงค่าในคอลัมน์ของแถวนั้นๆ ได้โดยตรงในระหว่างการ evaluate expression ตาราง argument นี้จะถูก evaluate ภายใน filter context ที่มีอยู่ก่อนจึงจะเริ่ม iterate | |
| expression | scalar | Yes | Expression หรือสูตรคำนวณที่ต้องการให้ evaluate สำหรับแต่ละแถวในตาราง สามารถเป็นการคูณคอลัมน์ (เช่น Sales[Quantity] * Sales[UnitPrice]) การใช้ RELATED ดึงข้อมูลข้ามตาราง (เช่น Sales[Quantity] * RELATED(Products[Cost])) หรือการเรียกใช้ measure (เช่น [Unit Profit] ซึ่งจะเกิด context transition อัตโนมัติ) Expression นี้ทำงานภายใน row context ที่ SUMX สร้างขึ้นสำหรับแต่ละแถว ทำให้สามารถอ้างถึงค่าในคอลัมน์ของแถวปัจจุบันได้ ผลลัพธ์ที่ได้จากแต่ละแถวจะถูกรวมกันเป็นผลลัพธ์สุดท้าย ตาม Microsoft Learn “Only numeric values are counted. Blanks, logical values, and text are ignored” |
เมื่อไม่มี calculated column สำหรับเก็บยอดเงินไว้ในตาราง สามารถใช้ SUMX คูณจำนวนกับราคาในแต่ละแถวแล้วรวมผลลัพธ์ได้ทันที โดยไม่ต้องสร้าง calculated column ใหม่ที่จะเปลืองพื้นที่ใน data model วิธีนี้ยืดหยุ่นกว่าเพราะสามารถเปลี่ยนสูตรคำนวณได้ตลอดเวลา นี่คือ use case ที่พบบ่อยที่สุดของ SUMX
ใช้ร่วมกับ FILTER เพื่อกรองเฉพาะแถวที่ตรงเงื่อนไข เช่น สินค้าในหมวดหมู่ใดหมวดหมู่หนึ่ง ลูกค้าจากประเทศที่กำหนด หรือรายการที่มียอดเกินจำนวนที่กำหนด ก่อนที่จะให้ SUMX iterate และคำนวณ Pattern SUMX(FILTER(…), …) เป็นหนึ่งใน pattern ที่ใช้บ่อยที่สุดใน DAX เพราะให้ control ทั้ง filter context และ row context ไปพร้อมกัน
ใช้ RELATED ภายใน expression ของ SUMX เพื่อเข้าถึงข้อมูลจากตารางที่มีความสัมพันธ์ เช่น ดึงราคาต้นทุนจาก Products table มาคูณกับจำนวนใน Sales table หรือดึงอัตราแลกเปลี่ยนจาก Currency table วิธีนี้ช่วยให้สามารถคำนวณข้ามตารางได้โดยไม่ต้องสร้าง calculated column ใหม่ที่จะเพิ่มขนาดไฟล์ RELATED ทำงานได้เฉพาะใน row context ซึ่ง SUMX สร้างให้อยู่แล้ว
ใช้ measure ที่สร้างไว้แล้วเป็น expression ภายใน SUMX โดยจะเกิด context transition อัตโนมัติที่แปลง row context เป็น filter context ทันที เหมาะสำหรับการคำนวณที่ซับซ้อนหลายขั้นตอน เช่น การรวม measure ที่คำนวณกำไรของแต่ละสินค้า หรือการรวม measure ที่มีการคำนวณส่วนลดตามเงื่อนไข ทำให้สามารถนำ business logic ที่ซับซ้อนมาใช้ร่วมกับ SUMX ได้อย่างมีประสิทธิภาพ
Total Sales = SUMX( Sales, Sales[Quantity] * Sales[UnitPrice] ) // Context: Sales table มี columns: Quantity และ UnitPrice // Data model: Sales[Quantity] เก็บจำ…Total Sales =
SUMX(
Sales,
Sales[Quantity] * Sales[UnitPrice]
)
// Context: Sales table มี columns: Quantity และ UnitPrice
// Data model: Sales[Quantity] เก็บจำนวนสินค้า, Sales[UnitPrice] เก็บราคาต่อหน่วย
//
// ตัวอย่างข้อมูลใน Sales table:
// Quantity | UnitPrice
// 10 | 50
// 5 | 100
// 8 | 75
//
// SUMX iterate แต่ละแถว:
// Row 1: 10 * 50 = 500
// Row 2: 5 * 100 = 500
// Row 3: 8 * 75 = 600
// Result: 500 + 500 + 600 = 1,600
1,600 (ผลรวมยอดขายทั้งหมดจากการคูณจำนวนกับราคาในแต่ละแถว)
// ✅ วิธีถูก: กรองที่ Dimension Table โดยตรงด้วย CALCULATE Electronics Sales = CALCULATE( SUMX( Sales, Sales[Quantity] * Sales[UnitPrice] ), Products[Category]…// ✅ วิธีถูก: กรองที่ Dimension Table โดยตรงด้วย CALCULATE
Electronics Sales =
CALCULATE(
SUMX(
Sales,
Sales[Quantity] * Sales[UnitPrice]
),
Products[Category] = "Electronics"
)
// ❌ หลีกเลี่ยง: FILTER(Sales, RELATED(...)) ช้ามากและอาจให้ผลผิด!
// Context: Sales table มี relationship กับ Products table (many-to-one)
// Data model:
// Sales: OrderID, ProductID, Quantity, UnitPrice
// Products: ProductID, ProductName, Category
// Relationship: Sales[ProductID] → Products[ProductID]
//
// ตัวอย่างข้อมูล:
// Sales table: Products table:
// ProductID | Qty | Price ProductID | Category
// 101 | 10 | 50 101 | Electronics
// 102 | 5 | 30 102 | Clothing
// 103 | 8 | 75 103 | Electronics
//
// CALCULATE กรองเฉพาะ Electronics:
// Row 1: (10 * 50 = 500) ✅ included
// Row 2: (5 * 30 = 150) ❌ excluded (Clothing)
// Row 3: (8 * 75 = 600) ✅ included
// Result: 500 + 600 = 1,100
1,100 (ยอดขายรวมเฉพาะสินค้าในหมวด Electronics)
Total Profit = SUMX( Sales, VAR Revenue = Sales[Quantity] * Sales[UnitPrice] VAR Cost = Sales[Quantity] * RELATED(Products[UnitCost]) RETURN Revenue - Cost ) //…Total Profit =
SUMX(
Sales,
VAR Revenue = Sales[Quantity] * Sales[UnitPrice]
VAR Cost = Sales[Quantity] * RELATED(Products[UnitCost])
RETURN
Revenue - Cost
)
// Context: Sales table มี relationship กับ Products table
// Data model:
// Sales: OrderID, ProductID, Quantity, UnitPrice
// Products: ProductID, ProductName, UnitCost
// Relationship: Sales[ProductID] → Products[ProductID]
//
// ตัวอย่างข้อมูล:
// Sales table: Products table:
// ProductID | Qty | Price ProductID | UnitCost
// 101 | 10 | 50 101 | 30
// 102 | 5 | 100 102 | 60
// 103 | 8 | 75 103 | 45
//
// SUMX iterate:
// Row 1: Revenue = 10 * 50 = 500, Cost = 10 * 30 = 300, Profit = 200
// Row 2: Revenue = 5 * 100 = 500, Cost = 5 * 60 = 300, Profit = 200
// Row 3: Revenue = 8 * 75 = 600, Cost = 8 * 45 = 360, Profit = 240
// Result: 200 + 200 + 240 = 640
640 (กำไรสุทธิรวมจากทุกรายการขาย)
// สมมติว่ามี measure อยู่แล้ว: // [Total Sales Per Product] = SUM(Sales[Amount]) Sales By Product = VAR ProductList = VALUES(Products[ProductName]) VAR SalesBy…// สมมติว่ามี measure อยู่แล้ว:
// [Total Sales Per Product] = SUM(Sales[Amount])
Sales By Product =
VAR ProductList = VALUES(Products[ProductName])
VAR SalesByProduct =
SUMX(
ProductList,
[Total Sales Per Product]
)
RETURN
SalesByProduct
// Context: VALUES สร้างตารางที่มีเฉพาะ ProductName ที่ไม่ซ้ำ
// Data model:
// Sales: OrderID, ProductID, Amount
// Products: ProductID, ProductName
// Measure: [Total Sales Per Product] = SUM(Sales[Amount])
//
// ตัวอย่าง VALUES(Products[ProductName]):
// ProductName
// "Laptop"
// "Mouse"
// "Keyboard"
//
// SUMX iterate แต่ละ product:
// Row 1: ProductName = "Laptop"
// → Context transition →
// CALCULATE([Total Sales Per Product], Products[ProductName] = "Laptop")
// → Result: 5,000
// Row 2: ProductName = "Mouse"
// → Context transition →
// CALCULATE([Total Sales Per Product], Products[ProductName] = "Mouse")
// → Result: 1,500
// Row 3: ProductName = "Keyboard"
// → Context transition →
// CALCULATE([Total Sales Per Product], Products[ProductName] = "Keyboard")
// → Result: 2,000
// Result: 5,000 + 1,500 + 2,000 = 8,500
8,500 (ผลรวมยอดขายทั้งหมดจากการรวม measure ของแต่ละ product)
ช้ากว่าครับ 😅 เพราะ SUMX ต้องวนลูปทีละแถวและคำนวณ expression ในแต่ละแถวโดยใช้ formula engine ในขณะที่ SUM ประมวลผลโดย storage engine โดยตรง
.
ถ้าคุณมี calculated column ที่เก็บยอดเงินไว้แล้ว (เช่น Sales[Amount] = Quantity × Price) การใช้ SUM(Sales[Amount]) จะเร็วกว่ามาก แต่ถ้าไม่มี calculated column นั้น การใช้ SUMX จะประหยัดพื้นที่ data model มากกว่า
.
ดังนั้นต้องชั่งน้ำหนักระหว่าง performance (SUM + calculated column) กับขนาดไฟล์ (SUMX โดยตรง)
.
ส่วนตัวผมแนะนำให้ใช้ SUMX ก่อน ถ้าไม่มีปัญหาความเร็ว แล้วค่อยสร้าง calculated column + SUM เฉพาะเมื่อต้องการ optimize performance สูงสุด 💡
SUMX สร้าง **row context** ครับ สำหรับ expression ที่อยู่ภายในขณะที่วนลูปแต่ละแถว ทำให้เราอ้างถึงค่าในคอลัมน์ของแถวนั้นๆ ได้โดยตรง เช่น Sales[Quantity] จะเข้าถึงค่าในแถวปัจจุบัน
.
แต่ถ้า expression นั้นเป็น measure ที่สร้างไว้แล้ว (เช่น [Total Sales]) จะเกิด **context transition** อัตโนมัติ! Row context จะถูกแปลงเป็น filter context ทันที โดยการสร้าง implicit CALCULATE ครอบ measure นั้นไว้
.
นี่คือเหตุผลที่ SUMX สามารถใช้ measure เป็น expression ได้โดยไม่มีปัญหา ในขณะที่ SUM ปกติใช้ได้เฉพาะ column reference โดยตรงเท่านั้น 😎
ใช้ SUMX เมื่อต้องการวนลูปทีละแถวเพื่อคำนวณ expression ที่ต่างกันหรือซับซ้อนในแต่ละแถว เช่น การคูณ Quantity กับ Price หรือการใช้ RELATED ดึงข้อมูลจากตารางอื่นมาคำนวณ
.
ส่วน CALCULATE ใช้เมื่อต้องการเปลี่ยน filter context เพื่อหาค่า aggregation แบบเดียวทั้งหมด เช่น SUM ยอดขายเฉพาะหมวด Electronics โดยไม่ต้องวนลูปทีละแถว
.
หลายครั้งเราใช้ทั้งสองร่วมกัน เช่น SUMX(FILTER(…), …) หรือ CALCULATE(SUMX(…), …)
.
กฎง่ายๆ คือ ถ้าต้องการคำนวณ expression ที่ต่างกันในแต่ละแถว → ใช้ SUMX ถ้าแค่ต้องการเปลี่ยน filter context เพื่อ aggregate แบบเดียว → ใช้ CALCULATE 💡
VALUES เป็นฟังก์ชันที่สร้างตารางที่มีเฉพาะค่าที่ไม่ซ้ำกัน (distinct values) ซึ่งเหมาะกับการใช้ร่วมกับ SUMX มากครับ มี 2 เหตุผลหลัก:
.
**(1) Performance Optimization** ลดจำนวนแถวที่ต้องวนลูปจากทุกแถวใน fact table (หลายล้านแถว) เหลือเฉพาะค่าที่ unique (หลายพันค่า) ทำให้เร็วขึ้นมาก 🚀 โดยเฉพาะเมื่อใช้ร่วมกับ measure ที่ trigger context transition
.
**(2) Granularity Control** บังคับให้ SUMX วนลูปในระดับ granularity ที่ต้องการ เช่น SUMX(VALUES(Products[ProductName]), [Total Sales]) จะวนลูปเฉพาะ product ที่ unique แล้ว evaluate [Total Sales] สำหรับแต่ละ product
.
Pattern SUMX + VALUES เป็น best practice ที่ใช้บ่อยมากเมื่อต้องการ aggregate measure ในระดับ dimension table แทน fact table 😎
การใช้ VAR…RETURN ร่วมกับ SUMX เป็น best practice สำหรับ measure ที่ซับซ้อนครับ เพราะช่วยให้ formula อ่านง่ายและบำรุงรักษาได้ง่ายขึ้น มี 2 แนวทางหลัก:
.
**(1) VAR ภายนอก SUMX** ใช้เก็บตารางที่ผ่านการกรองจาก FILTER, CALCULATETABLE หรือ VALUES ไว้ก่อน แล้วส่งให้ SUMX วนลูป วิธีนี้ลดการคำนวณซ้ำและทำให้ code อ่านง่าย
.
เช่น `VAR FilteredSales = FILTER(Sales, …) RETURN SUMX(FilteredSales, …)`
.
**(2) VAR ภายใน SUMX expression** ใช้เก็บ intermediate result ภายใน expression ทำให้สูตรซับซ้อนอ่านง่าย
.
เช่น `SUMX(Sales, VAR Revenue = … VAR Cost = … RETURN Revenue – Cost)`
.
ส่วนตัวผมชอบใช้ VAR มากเพราะทำให้แต่ละขั้นตอนมีชื่อที่มีความหมายชัดเจน debug ได้ง่าย และอาจช่วยเรื่อง performance ด้วยนะครับ 💡
ใช้ได้ครับ แต่มีข้อจำกัดสำคัญที่ต้องระวัง ⚠️
.
ตาม Microsoft Learn ระบุชัดเจนว่า SUMX “is not supported for use in DirectQuery mode when used in calculated columns or row-level security (RLS) rules” ซึ่งหมายความว่า:
.
❌ **(1) Calculated Columns** ใช้ SUMX ไม่ได้ใน DirectQuery mode
❌ **(2) RLS Rules** ใช้ SUMX ไม่ได้ใน DirectQuery mode
✅ **(3) Measures** ใช้ SUMX ได้ตามปกติใน DirectQuery mode
.
ดังนั้นถ้าคุณใช้ DirectQuery ต้องระวังว่าจะใช้ SUMX ที่ไหน และควรพิจารณาใช้ SUM กับ calculated column แทนถ้าเป็นไปได้ นี่เป็นข้อจำกัดสำคัญที่ต้องคำนึงถึงเมื่อออกแบบ data model สำหรับ DirectQuery นะครับ
ตาม Microsoft Learn ระบุว่า “Only numeric values are counted. Blanks, logical values, and text are ignored” ซึ่งหมายความว่า SUMX จะประมวลผลเฉพาะค่าตัวเลขเท่านั้นครับ
.
ค่าประเภทอื่นๆ จะถูกข้าม (ignore) โดยอัตโนมัติ:
• Blank values → ข้าม (ไม่นับรวมในผลลัพธ์)
• Logical values (TRUE/FALSE) → ข้าม
• Text values → ข้าม
.
ดังนั้นถ้า expression ของคุณคำนวณออกมาเป็น blank หรือ text ใน row ใดๆ row นั้นจะไม่มีส่วนร่วมในผลรวมสุดท้าย
.
นี่ต่างจาก Excel ที่อาจ treat blank เป็น 0 นะครับ ใน DAX blank จะถูกข้ามโดยสมบูรณ์ 💡
SUMX เป็น iterator function ที่ทรงพลังที่สุดตัวหนึ่งใน DAX ครับ 😎
.
มันแตกต่างจาก SUM อย่างสิ้นเชิง… ถ้า SUM รวมค่าใน column เดียวโดยตรง SUMX จะ iterate ทีละแถวในตารางที่กำหนด แล้ว evaluate expression สำหรับแต่ละแถวก่อนนำผลลัพธ์ทั้งหมดมารวมกัน
.
ความสามารถนี้ทำให้ SUMX เหมาะอย่างยิ่งสำหรับการคำนวณที่ซับซ้อนในระดับแถวข้อมูล เช่น การคูณ Quantity × Price การใช้ RELATED ดึงข้อมูลข้ามตาราง หรือการคำนวณตาม business logic ที่ต้องการ row context
SUMX เป็น iterator function ที่สร้าง row context ขึ้นมาใหม่สำหรับแต่ละแถวที่ iterate ผ่าน ตามที่ Microsoft Learn อธิบายไว้ว่า “The function evaluates the expression for each row of the table”
.
ซึ่งหมายความว่าเมื่อ SUMX ทำงาน มันจะ scan table ทีละแถว และสร้าง row context ที่ทำให้เราสามารถอ้างถึงค่าในคอลัมน์ต่างๆ ของแถวปัจจุบันได้โดยตรง เช่น Sales[Quantity] * Sales[UnitPrice] ซึ่งคูณจำนวนกับราคาในแถวนั้นๆ นี่คือสิ่งที่ SUM ทำไม่ได้ครับ 💡
กระบวนการทำงานของ SUMX มี 3 ขั้นตอนหลัก:
.
**(1)** Evaluate table argument ภายใน filter context ที่มีอยู่เพื่อกำหนดว่าจะ iterate แถวไหนบ้าง
**(2)** สำหรับแต่ละแถว สร้าง row context ขึ้นมาแล้ว evaluate expression argument ภายใน row context นั้น
**(3)** รวมผลลัพธ์ทั้งหมดจากทุกแถวเป็นค่าสุดท้าย
.
ตาม DAX.guide อธิบายไว้ว่า “SUMX returns the sum of an expression evaluated for each row in a table” ซึ่งกลไกนี้ทำให้ SUMX แตกต่างจาก SUM อย่างสิ้นเชิงครับ
มาดูความแตกต่างกันครับ ตาม DAX.guide ได้อธิบายไว้อย่างชัดเจนว่า “SUM works only with columns in the model” ในขณะที่ SUMX สามารถ evaluate expression ที่ซับซ้อนได้:
นี่เป็นเรื่องสำคัญมากที่ต้องเข้าใจครับ… เมื่อ expression ภายใน SUMX เป็น measure ที่สร้างไว้แล้ว จะเกิดกลไก context transition อัตโนมัติ
.
โดย DAX จะแปลง row context เป็น filter context ทันทีผ่านการสร้าง implicit CALCULATE ครอบ measure นั้นไว้ ตัวอย่างเช่น SUMX(Products, [Total Sales]) เมื่อ iterate แต่ละ product จะแปลง row context (Products[ProductName] = “Product A”) ให้กลายเป็น CALCULATE([Total Sales], Products[ProductName] = “Product A”) โดยอัตโนมัติ
.
ทำให้ measure คำนวณถูกต้องสำหรับแต่ละ product แยกกัน นี่คือเหตุผลสำคัญที่ SUMX สามารถใช้ measure เป็น expression ได้โดยไม่มีปัญหา 😎
อย่างไรก็ตาม context transition มีค่าใช้จ่าย (overhead) ที่ไม่น้อย ดังนั้นควรใช้อย่างระมัดระวังกับตารางขนาดใหญ่นะครับ ⚠️
เรื่องนี้ต้องระวังครับ… ตาม Microsoft Learn ระบุไว้อย่างชัดเจนว่า SUMX “is not supported for use in DirectQuery mode when used in calculated columns or row-level security (RLS) rules”
.
ซึ่งหมายความว่าถ้าคุณใช้ DirectQuery mode คุณไม่สามารถใช้ SUMX ใน calculated column หรือ RLS rules ได้ แต่สามารถใช้ใน measure ได้ตามปกติ
.
นี่เป็นข้อจำกัดสำคัญที่ต้องคำนึงถึงเมื่อออกแบบ data model สำหรับ DirectQuery ครับ
คุณควรใช้ SUMX เมื่อ:
SUMX เป็นส่วนหนึ่งของตระกุล X Functions (iterator functions) ใน DAX ซึ่งรวมถึง AVERAGEX, MAXX, MINX, COUNTX และ CONCATENATEX ซึ่งทั้งหมดมี row context behavior และ context transition เหมือนกัน
.
เมื่อคุณเข้าใจ SUMX แล้ว คุณจะสามารถใช้ X Functions อื่นๆ ได้อย่างมีประสิทธิภาพทันที ส่วนตัวผมว่านี่เป็นหนึ่งในฟังก์ชันที่สำคัญที่สุดที่ต้องเชี่ยวชาญใน DAX เลยครับ 😎