Thep Excel

IF – ตรวจสอบเงื่อนไขและคืนค่าตามผลลัพธ์ TRUE/FALSE

IF เป็นฟังก์ชันตรรกะพื้นฐานที่สำคัญที่สุดใน DAX ใช้สำหรับตรวจสอบเงื่อนไข (logical test) แล้วคืนค่าที่แตกต่างกันตามผลลัพธ์ว่าเป็นจริง (TRUE) หรือเท็จ (FALSE) สามารถใช้ได้ทั้งในบริบท calculated column, measure, calculated table และ visual calculation ซึ่งทำให้มีความยืดหยุ่นสูง คืนค่าเป็น scalar value ได้ทุกประเภท ไม่ว่าจะเป็นตัวเลข ข้อความ วันที่ หรือค่า boolean ถ้าไม่ระบุค่าสำหรับกรณีเท็จ (value_if_false) จะคืน BLANK() โดยอัตโนมัติ มีคุณสมบัติพิเศษคือ lazy evaluation behavior ที่ประมวลผลเฉพาะเส้นทาง (branch) ที่ถูกเลือกเท่านั้น ทำให้มีประสิทธิภาพในการทำงานที่ดี แตกต่างจาก Excel IF อย่างชัดเจนตรงที่ใช้รูปแบบอ้างอิงแบบ Table[Column] ทำงานภายใต้ filter context และแยกความแตกต่างระหว่าง BLANK กับ 0 อย่างเข้มงวด

=IF(<logical_test>, <value_if_true>[, <value_if_false>])

By ThepExcel AI Agent
18 December 2025

Function Metrics


Popularity
10/10

Difficulty
2/10

Usefulness
10/10

Syntax & Arguments

=IF(<logical_test>, <value_if_true>[, <value_if_false>])

Argument Type Required Default Description
logical_test boolean Yes เงื่อนไขตรรกะที่ต้องการตรวจสอบ สามารถเป็น expression ใดก็ได้ที่ evaluate ออกมาเป็น TRUE หรือ FALSE เช่น Sales[Amount] > 1000, Products[Category] = “Electronics”, [Total Profit] >= 0 หรือใช้ร่วมกับ logical operator && (AND), || (OR), ! (NOT) ใน row context (calculated column) จะ evaluate แต่ละ row ของ table โดยใช้ค่าจาก column นั้นๆ ใน filter context (measure) จะ evaluate ตาม filter ปัจจุบันของ visual สามารถใช้ ISBLANK, HASONEVALUE, HASONEFILTER หรือ logical function อื่นๆ ได้ ถ้า logical_test evaluate เป็น BLANK จะถือว่าเป็น FALSE
value_if_true scalar Yes ค่าที่ต้องการคืนเมื่อเงื่อนไขเป็น TRUE สามารถเป็นตัวเลข ข้อความ วันที่ boolean ผลลัพธ์จาก measure อื่น หรือ expression ที่คำนวณได้ เช่น “High”, 100, [Total Sales] * 1.1, BLANK() ถ้า value_if_true และ value_if_false มี data type ต่างกัน DAX จะคืนเป็น VARIANT type และอาจแปลง type โดยอัตโนมัติ (เช่น integer เป็น decimal เมื่อมี branch ใดเป็น decimal, boolean เป็น integer) ถ้า expression ซับซ้อน แนะนำให้ใช้ VAR เก็บค่าก่อนแล้วค่อย reference ใน IF เพื่อ performance และ readability ที่ดีขึ้น เนื่องจาก IF มี lazy evaluation value_if_true จะถูก evaluate เฉพาะเมื่อ logical_test เป็น TRUE
value_if_false scalar Optional BLANK() ค่าที่ต้องการคืนเมื่อเงื่อนไขเป็น FALSE ถ้าไม่ระบุจะคืน BLANK() โดยอัตโนมัติ ซึ่งมักใช้เพื่อ “ซ่อน” ผลลัพธ์ในบางกรณี เช่น ซ่อนค่าในระดับ total หรือซ่อนค่าที่ไม่มีความหมาย BLANK ใน DAX มีลักษณะพิเศษ: ไม่แสดงใน visual (cell ว่าง), ไม่นับรวมใน AVERAGE (ข้าม BLANK ไปเลย), มี propagation rule (BLANK + 5 = 5, BLANK * 5 = BLANK) สามารถเป็น data type เดียวกันหรือต่างจาก value_if_true ได้ แต่ VARIANT return type อาจส่งผลต่อ performance และ memory usage ในบางกรณี เนื่องจาก lazy evaluation value_if_false จะถูก evaluate เฉพาะเมื่อ logical_test เป็น FALSE

How it works

สร้าง KPI Label และ Status Indicator

แปลงผลการคำนวณตัวเลขให้เป็นข้อความสถานะ (ผ่าน/ไม่ผ่าน, สูง/กลาง/ต่ำ) หรือสถานะ emoji เพื่อใช้ใน card, table หรือเป็น conditional formatting condition ทำให้รายงานอ่านง่ายและเข้าใจได้ทันที เหมาะกับ executive dashboard ที่ผู้บริหารต้องการเห็นสถานะชัดเจนโดยไม่ต้องตีความตัวเลข

ซ่อนค่าในระดับ Total/Subtotal

คืน BLANK() เมื่ออยู่ในระดับรวม (total/subtotal) เพื่อไม่แสดงค่าที่ไม่มีความหมายในภาพรวม เช่น average of averages (ค่าเฉลี่ยของค่าเฉลี่ยไม่ถูกต้อง), ratio ที่ควรคำนวณจาก total numerator/total denominator ไม่ใช่ average ของ ratio, margin % ที่ต้องคำนวณจาก aggregated values ใช้ HASONEVALUE, HASONEFILTER, ISINSCOPE หรือ ISFILTERED เพื่อตรวจสอบว่าอยู่ใน detail level หรือไม่

Data Segmentation – จัดกลุ่มข้อมูล

สร้างกลุ่มเช่น Low/Medium/High, A/B/C grade, age group (18-25, 26-35), customer segment (VIP/Regular/New) จากเงื่อนไขที่กำหนด แล้วนำไปใช้เป็น slicer, axis หรือ legend ใน visual ช่วยให้วิเคราะห์ข้อมูลตาม cohort ได้ง่ายขึ้น สร้าง RFM analysis, ABC analysis หรือ customer lifetime value segment มักใช้ใน calculated column เพื่อให้ segment เป็นส่วนหนึ่งของ data model

Handle Missing Data และ Edge Cases

ตรวจสอบและจัดการข้อมูลที่ขาดหาย (BLANK, NULL) หรือกรณีพิเศษ เช่น division by zero, ค่าที่อยู่นอกช่วง (outlier), data quality issue ถ้าไม่มีข้อมูลให้ใช้ค่า default หรือคืน BLANK เพื่อป้องกัน error หรือผลลัพธ์ที่ผิดพลาด เช่น IF(ISBLANK([Sales]), 0, [Sales]) หรือใช้ IF ร่วมกับ DIVIDE, COALESCE เพื่อสร้าง robust measure ที่ทำงานได้แม้มีข้อมูลไม่ครบ

Dynamic Calculation Logic

เปลี่ยนวิธีการคำนวณตาม filter context, user selection ใน slicer หรือเงื่อนไขที่กำหนด เช่น ถ้าเลือก View = "YTD" ให้คำนวณแบบ year-to-date ถ้าเลือก "MTD" ให้คำนวณแบบ month-to-date หรือสลับระหว่าง Actual vs Budget calculation, Standard vs Custom formula ทำให้ measure เดียวรองรับหลาย scenario ลดจำนวน measure ที่ต้อง maintain และให้ user มี flexibility ในการวิเคราะห์

Conditional Formatting Logic

สร้าง logic เพื่อควบคุม conditional formatting ของ visual เช่น ถ้ายอดขายต่ำกว่า target แสดงสีแดง ถ้ามากกว่าแสดงสีเขียว หรือใช้ icon set ตามระดับ performance (⬆️⬇️➡️) หรือแสดง warning indicator เมื่อมี data anomaly ทำให้ visual มี visual cue ที่ชัดเจน ช่วย user spot issue หรือ highlight achievement ได้ทันที

Examples

ตัวอย่างที่ 1: Foundation – จัดกลุ่มราคาสินค้าใน Calculated Column
-- Calculated Column ใน Products table Price Group = IF( Products[List Price] < 500, "Low", "High" ) -- Context: -- Products table มี columns: ProductID, Produc…
Calculated column ทำงานใน row context ดังนั้น IF จะ evaluate แต่ละ row ของ Products table โดยตรวจสอบค่าใน List Price column ของ row นั้นๆ ถ้าราคาต่ำกว่า 500 จะ assign ค่า "Low" ให้ row นั้น ถ้าไม่ใช่จะได้ "High"
.
ผลลัพธ์คือ column ใหม่ชื่อ "Price Group" ที่เก็บกลุ่มราคาไว้ สามารถนำไปใช้เป็น slicer, filter ใน visual หรือ axis ใน chart ได้ทันที ส่วนตัวผมว่านี่เป็นวิธีที่ง่ายที่สุดในการจัดกลุ่มข้อมูลครับ 😎
.
Column นี้เป็นส่วนหนึ่งของ data model และคำนวณ 1 ครั้งตอน refresh data ไม่ใช่ dynamic ตาม filter (ต่างจาก measure) เหมาะกับกรณีที่ต้องการ fixed grouping ที่ไม่เปลี่ยนแปลง
DAX Formula:

-- Calculated Column ใน Products table
Price Group = 
IF(
    Products[List Price] < 500,
    "Low",
    "High"
)

-- Context:
-- Products table มี columns: ProductID, Product Name, List Price, Category
-- ตัวอย่างข้อมูล:
-- | ProductID | Product Name | List Price | Price Group |
-- | 1         | Widget A     | 250        | Low         |
-- | 2         | Widget B     | 750        | High        |
-- | 3         | Gadget X     | 450        | Low         |

Result:

คืน "Low" ถ้าราคาต่ำกว่า 500 มิฉะนั้นคืน "High" - Column ใหม่ที่แสดงกลุ่มราคาของแต่ละสินค้า

ตัวอย่างที่ 2: Practical – ซ่อนค่าเฉลี่ยในระดับ Total
-- Measure ที่แสดงค่าเฉพาะระดับ detail Average Unit Price (Detail Only) = IF( HASONEVALUE(Products[Product Name]), AVERAGE(Sales[Unit Price]), BLANK() ) -- Cont…
HASONEVALUE ตรวจสอบว่า filter context ปัจจุบันมีสินค้าเดียวหรือไม่ ถ้ามี (user กำลังดูระดับสินค้าแต่ละตัว = detail level) จะคำนวณ AVERAGE(Sales[Unit Price]) ปกติ แต่ถ้าไม่มี (อยู่ใน total/subtotal row ที่มีหลายสินค้ารวมกัน) จะคืน BLANK() เพื่อไม่แสดงค่า
.
เหมาะกับกรณีที่ค่าเฉลี่ยในระดับรวมไม่มีความหมาย เพราะ average of averages ไม่ถูกต้อง (แต่ละสินค้ามีจำนวน transaction ไม่เท่ากัน) 😅
.
วิธีนี้ทำให้ matrix visual มี cell ว่างใน total row แทนที่จะแสดงค่าที่เข้าใจผิดได้ BLANK ใน DAX ไม่เท่ากับ 0 และจะไม่แสดงใน visual (แตกต่างจาก Excel)
DAX Formula:

=-- Measure ที่แสดงค่าเฉพาะระดับ detail
Average Unit Price (Detail Only) = 
IF(
    HASONEVALUE(Products[Product Name]),
    AVERAGE(Sales[Unit Price]),
    BLANK()
)

-- Context:
-- Sales table มี columns: OrderID, ProductID, Unit Price, Quantity, Date
-- Products table มี: ProductID, Product Name, Category
-- Relationship: Sales[ProductID] → Products[ProductID]

-- ผลลัพธ์ใน Matrix visual:
-- | Product Name | Avg Unit Price |
-- | Product A    | 125.50         |
-- | Product B    | 89.25          |
-- | Product C    | 156.00         |
-- | Total        | (blank)        | ← ซ่อนเพราะไม่มีความหมาย

Result:

แสดงราคาเฉลี่ยเฉพาะระดับสินค้าแต่ละตัว ซ่อนค่าในระดับ total/subtotal (แสดงเป็น cell ว่าง)

ตัวอย่างที่ 3: Advanced – เงื่อนไขซับซ้อนด้วย Logical Operators
-- Measure สำหรับจัดกลุ่ม customer segment Customer Segment = VAR TotalSales = [Total Sales Amount] VAR TotalProfit = [Total Profit] VAR ProfitMargin = DIVIDE(T…
ตัวอย่างนี้แสดงการใช้ VAR ร่วมกับ IF และ logical operators (&&, ||) เพื่อสร้าง segmentation logic ที่ซับซ้อน
.
ขั้นแรกใช้ VAR คำนวณค่าที่ต้องใช้ซ้ำ (TotalSales, TotalProfit, ProfitMargin) เพื่อป้องกันการคำนวณซ้ำและทำให้โค้ดอ่านง่าย ใน IF ชั้นแรกใช้ && (AND operator) เช็คว่ายอดขายมากกว่า 100,000 AND margin มากกว่า 20% ถึงจะเป็น "High Value"
.
ถ้าไม่ผ่านเงื่อนไขแรก จะเช็ค IF ชั้นที่สอง ซึ่งใช้ || (OR operator) ถ้ายอดขายมากกว่า 50,000 OR margin มากกว่า 15% (เงื่อนไขใดเงื่อนไขหนึ่งผ่านก็พอ) จะได้ "Medium Value" ถ้าไม่ผ่านทั้งสองจะได้ "Standard"
.
วิธีนี้ดีกว่าใช้ AND()/OR() function เพราะกระชับกว่าและ DAX engine optimize logical operator ได้ดีกว่า 😎
DAX Formula:

-- Measure สำหรับจัดกลุ่ม customer segment
Customer Segment = 
VAR TotalSales = [Total Sales Amount]
VAR TotalProfit = [Total Profit]
VAR ProfitMargin = DIVIDE(TotalProfit, TotalSales, 0)
RETURN
    IF(
        TotalSales > 100000 && ProfitMargin > 0.2,
        "High Value",
        IF(
            TotalSales > 50000 || ProfitMargin > 0.15,
            "Medium Value",
            "Standard"
        )
    )

-- Context:
-- Sales table มี: CustomerID, Amount, Cost, Date
-- [Total Sales Amount] = SUM(Sales[Amount])
-- [Total Profit] = SUM(Sales[Amount]) - SUM(Sales[Cost])

-- ตัวอย่างผลลัพธ์:
-- | Customer | Total Sales | Profit Margin | Segment      |
-- | ABC Corp | 150,000     | 25%          | High Value   |
-- | XYZ Inc  | 60,000      | 12%          | Medium Value |
-- | 123 Ltd  | 30,000      | 8%           | Standard     |

Result:

คืน "High Value" เมื่อยอดขายสูงและ margin ดี, "Medium Value" เมื่อผ่านเงื่อนไขใดเงื่อนไขหนึ่ง, มิฉะนั้นคืน "Standard"

ตัวอย่างที่ 4: Advanced – Dynamic Time Intelligence
-- Measure ที่เปลี่ยน calculation logic ตาม user selection Dynamic Sales = VAR SelectedPeriod = SELECTEDVALUE(PeriodType[Period], "Current") VAR CurrentSales =…
ตัวอย่างนี้แสดงการสร้าง dynamic measure ที่เปลี่ยน calculation logic ตาม user selection โดยใช้ SELECTEDVALUE ดึงค่าที่เลือกจาก PeriodType slicer (ถ้าไม่เลือกหรือเลือกหลายค่าจะได้ default "Current")
.
จากนั้นใช้ SWITCH เปรียบเทียบ SelectedPeriod กับค่าต่างๆ แล้วเรียก time intelligence function ที่เหมาะสม ถ้าเลือก "YTD" จะใช้ DATESYTD คำนวณ year-to-date ถ้า "MTD" ใช้ DATESMTD สำหรับ month-to-date และถ้าไม่ตรงกรณีไหนจะแสดง current period sales ปกติ
.
วิธีนี้ทำให้ measure เดียวรองรับหลาย scenario ลดจำนวน measure ที่ต้อง maintain 😎
.
ตัวอย่างยังแสดงวิธีเขียนด้วย nested IF (IF Version) เพื่อเปรียบเทียบ แต่ชัดเจนว่า SWITCH อ่านง่ายกว่าและแนะนำให้ใช้สำหรับกรณีที่มีหลายทางเลือก
DAX Formula:

-- Measure ที่เปลี่ยน calculation logic ตาม user selection
Dynamic Sales = 
VAR SelectedPeriod = SELECTEDVALUE(PeriodType[Period], "Current")
VAR CurrentSales = [Total Sales Amount]
RETURN
    SWITCH(
        SelectedPeriod,
        "YTD", CALCULATE(CurrentSales, DATESYTD(Calendar[Date])),
        "MTD", CALCULATE(CurrentSales, DATESMTD(Calendar[Date])),
        "QTD", CALCULATE(CurrentSales, DATESQTD(Calendar[Date])),
        CurrentSales  -- Default: Current period
    )

-- Alternative ด้วย IF (ไม่แนะนำ - ใช้ SWITCH ดีกว่า):
Dynamic Sales IF Version = 
VAR SelectedPeriod = SELECTEDVALUE(PeriodType[Period], "Current")
RETURN
    IF(
        SelectedPeriod = "YTD",
        CALCULATE([Total Sales Amount], DATESYTD(Calendar[Date])),
        IF(
            SelectedPeriod = "MTD",
            CALCULATE([Total Sales Amount], DATESMTD(Calendar[Date])),
            [Total Sales Amount]
        )
    )

-- Context:
-- PeriodType table (disconnected table สำหรับ slicer): Period = {"Current", "MTD", "QTD", "YTD"}
-- Calendar table มี: Date, Year, Month, Quarter
-- Sales table มี: Amount, Date
-- Relationship: Sales[Date] → Calendar[Date]

Result:

แสดง Current/MTD/QTD/YTD sales ตามที่ user เลือกใน PeriodType slicer - Measure เดียวรองรับหลาง time intelligence pattern

FAQs

ควรใช้ IF หรือ SWITCH ในกรณีไหน?

ใช้ IF เมื่อมีเงื่อนไข 1-2 ระดับ หรือเงื่อนไข boolean ที่ซับซ้อน (เช่น [Sales] > 1000 && [Profit] > 0) IF ตรงไปตรงมาและเข้าใจง่าย
.
แต่ถ้ามีหลายกรณี (3-5+ ระดับ) ให้ใช้ SWITCH แทนเพราะอ่านง่ายกว่า maintain ง่ายกว่า และมี query plan ที่ดีกว่า (execution path ชัดเจนกว่า nested IF)
.
SWITCH เหมาะกับการเปรียบเทียบ expression เดียวกับค่าหลายค่า (เช่น status = “Active”/”Pending”/”Closed”) หรือใช้ SWITCH(TRUE(), condition1, result1, condition2, result2, …) pattern เพื่อแทน nested IF ที่มีเงื่อนไขต่างกัน
.
ส่วน IF เหมาะกับการตัดสินใจแบบ binary (yes/no) หรือเงื่อนไขที่แต่ละ branch มี logic ที่แตกต่างกันมาก Microsoft และ SQLBI แนะนำให้ใช้ SWITCH เมื่อมีทางเลือกมากกว่า 2-3 ทาง

ถ้าไม่ใส่ value_if_false จะเกิดอะไรขึ้น?

IF จะคืนค่า BLANK() โดยอัตโนมัติ BLANK ใน DAX ต่างจาก 0 หรือ empty string (“”) อย่างชัดเจน
.
BLANK มีพฤติกรรมพิเศษ 4 ประการ:
1) ไม่แสดงใน visual (จะเห็น cell ว่าง)
2) ไม่นับรวมใน aggregation บางประเภท (AVERAGE จะข้าม BLANK ไม่นับเป็น 0, ส่วน SUM จะนับเป็น 0)
3) มี propagation rule เฉพาะ (BLANK + number = number, BLANK * number = BLANK, BLANK & text = text)
4) BLANK ใน logical expression ถือว่าเป็น FALSE
.
วิธีนี้มักใช้เพื่อ “ซ่อน” ผลลัพธ์ในบางกรณี เช่น IF(condition, result) จะแสดง result เฉพาะตอนที่ condition เป็นจริง มิฉะนั้นจะไม่แสดงอะไร (cell ว่าง)
.
แตกต่างจาก IF(condition, result, 0) ที่จะแสดง 0 เมื่อ condition เป็นเท็จ การเลือกใช้ BLANK vs 0 ส่งผลต่อการแสดงผลและการคำนวณ aggregation อย่างมาก

Nested IF ควรซ้อนกี่ชั้น และทำไมต้องระวัง?

ทางเทคนิคซ้อนได้ไม่จำกัด แต่ถ้าซ้อนมากกว่า 3 ชั้นจะมีปัญหา 4 อย่าง:
.
1) **Readability** – โค้ดอ่านยาก maintain ยาก debug ยาก วงเล็บเยอะ 😭
2) **Performance** – แม้ว่า IF มี lazy evaluation (evaluate เฉพาะ branch ที่เลือก) แต่ nested IF มากๆ ยังทำให้ query engine สร้าง execution plan ที่ซับซ้อนและ optimize ยากกว่า flat structure
3) **Error-prone** – ความเสี่ยงที่จะเขียนเงื่อนไขผิด ลืมวงเล็บ หรือวาง logic ผิดลำดับสูงขึ้น
4) **Testing** – ทดสอบยากเพราะมีหลาย code path
.
ถ้ามีเงื่อนไขหลายระดับ แนะนำใช้ SWITCH แทน (SWITCH อ่านง่ายกว่ามาก) หรือแยกเป็น VAR หลายตัวเพื่อให้ logic ชัดเจนขึ้น หรือสร้าง lookup table แทนการใช้ hardcoded conditions ทั้งหมด
.
**Best practice:** ถ้าเกิน 2-3 ชั้น ให้หยุดคิดก่อนว่ามีวิธีอื่นที่ดีกว่าไหม (SWITCH, lookup table, หรือแยก logic ออกเป็นหลาย measure)

IF ใน DAX ต่างจาก Excel IF อย่างไร?

แม้ว่า logic เหมือนกัน (ตรวจสอบเงื่อนไขแล้วคืนค่า) แต่มีความแตกต่างสำคัญ 6 ประการ:
.
1) **Reference Style** – DAX ใช้ Table[Column] reference (Sales[Amount]) ไม่มี cell range A1:A10 เพราะทำงานกับ data model ไม่ใช่ spreadsheet
2) **Context Awareness** – IF ใน DAX measure ทำงานภายใต้ filter context และเปลี่ยนผลลัพธ์ตาม slicer/filter/visual context ส่วน Excel IF คำนวณเฉพาะเซลล์นั้น
3) **Context Transition** – DAX ทำ context transition อัตโนมัติใน row context (calculated column) ส่วน Excel ไม่มีแนวคิดนี้
4) **BLANK vs 0** – DAX แยก BLANK() กับ 0 ชัดเจน มี propagation rule และ aggregation behavior ต่างกัน ส่วน Excel มักแปลง empty cell เป็น 0
5) **Evaluation** – DAX IF มี lazy evaluation (evaluate เฉพาะ branch ที่เลือก) ส่วน Excel มี short-circuit evaluation แบบเต็มรูปแบบ
6) **Alternatives** – DAX มี SWITCH, DIVIDE, COALESCE ที่เป็น alternative ที่ดีกว่า nested IF ส่วน Excel ไม่มี (Excel มี IFS แต่ไม่เหมือน SWITCH)

ใช้ VAR กับ IF อย่างไรให้มี Performance ดี?

การใช้ VAR ร่วมกับ IF มีประโยชน์มากแต่ต้องวางตำแหน่งให้ถูก มี 3 แนวทางหลัก:
.
**1) VAR ก่อน IF (แนะนำ)** – ใช้เมื่อค่านั้นถูกใช้ใน logical_test หรือใช้ทั้ง true และ false branch
เช่น `VAR Sales = [Total Sales] RETURN IF(Sales > 1000, Sales * 0.1, Sales * 0.05)`
วิธีนี้คำนวณแค่ครั้งเดียวและ cache ค่าไว้ เนื่องจาก IF มี lazy evaluation จึงไม่เสีย performance
.
**2) VAR ภายใน Branch** – ใช้เมื่อค่านั้นใช้เฉพาะใน branch ใดบ branch หนึ่ง
เช่น `IF(condition, (VAR x = [ComplexCalc] RETURN x * 2), [SimpleCalc])`
วิธีนี้ทำให้ VAR ถูก evaluate เฉพาะเมื่อ branch นั้นถูกเลือก ประหยัด computation
.
**3) VAR สำหรับ Intermediate Result** – แยก complex expression เป็น VAR หลายตัวเพื่อให้โค้ดอ่านง่ายและ debug ง่าย
.
ประโยชน์: คำนวณแค่ครั้งเดียว (ไม่ซ้ำ), query engine optimize ได้ดีกว่า, โค้ดอ่านง่าย, debug ง่าย แต่อย่าใช้ VAR มากเกินไปจนโค้ดยาวเกินจำเป็น 😅

IF มี Lazy Evaluation จริงหรือ และต่างจาก IF.EAGER อย่างไร?

ใช่! DAX IF มี lazy evaluation โดย default ซึ่งหมายความว่า IF จะ evaluate เฉพาะ branch ที่ถูกเลือกตาม logical test ไม่ evaluate ทั้งสอง branch พร้อมกัน
.
ตัวอย่าง: `IF([Sales] > 1000, [Expensive Calculation A], [Expensive Calculation B])`
ถ้า [Sales] > 1000 เป็นจริง มันจะคำนวณเฉพาะ [Expensive Calculation A] ไม่คำนวณ B นี่คือข้อได้เปรียบด้าน performance ของ IF 😎
.
ตรงข้ามกับ **IF.EAGER** ซึ่ง evaluate ทั้งสอง branch ก่อนตัดสินใจเลือก (eager evaluation) IF.EAGER มีไว้สำหรับกรณีพิเศษที่ต้องการให้ query engine เห็น execution plan ของทั้งสอง branch เพื่อ optimize ได้ดีกว่า แต่ส่วนใหญ่ไม่ควรใช้ เพราะ lazy evaluation ของ IF ปกติมี performance ดีกว่าในเกือบทุกกรณี
.
**Caveat:** แม้จะเป็น lazy evaluation แต่ DAX engine อาจ evaluate บาง expression ล่วงหน้าเพื่อ optimization จึงไม่ควรพึ่งพา lazy evaluation เพื่อป้องกัน error (ควรใช้ IFERROR หรือ DIVIDE แทน)

ทำไม DIVIDE ถึงดีกว่า IF สำหรับการหาร?

สำหรับการหารที่ต้องการ handle division by zero แนะนำให้ใช้ DIVIDE แทน IF pattern เสมอ เหตุผล 4 ประการ:
.
**1) Performance** – `DIVIDE(numerator, denominator, alternate_result)` มี built-in zero-division handling ที่ query engine optimize ได้ดีกว่า `IF(denominator 0, numerator/denominator, alternate_result)` เพราะ DIVIDE ถูกออกแบบมาเฉพาะ
.
**2) Readability** – DIVIDE กระชับกว่า อ่านง่ายกว่า เห็นเจตนาชัดเจนกว่า (“this is division with safe handling”)
.
**3) Handles BLANK** – DIVIDE จัดการ BLANK ใน denominator ได้โดยอัตโนมัติ (คืน alternate_result) ส่วน IF ต้องเช็คทั้ง 0 และ NOT(ISBLANK())
.
**4) Robust** – DIVIDE จัดการ edge case อื่นๆ ที่อาจพลาดใน IF (เช่น infinity, very small numbers)
.
ตัวอย่าง: `DIVIDE([Total Profit], [Total Sales], BLANK())` ดีกว่า `IF([Total Sales] 0, [Total Profit]/[Total Sales], BLANK())`
.
และถ้าไม่ระบุ alternate_result (argument ที่ 3) DIVIDE จะคืน BLANK โดย default Microsoft และ SQLBI แนะนำให้ใช้ DIVIDE เสมอสำหรับการหาร ไม่ใช้ IF pattern เว้นแต่มี logic พิเศษที่ซับซ้อนกว่า

Resources & Related

Additional Notes

IF เป็น logical function พื้นฐานที่สุดใน DAX ครับ ใช้สำหรับตรวจสอบเงื่อนไข (logical test) แล้วคืนค่าที่แตกต่างกันตามผลลัพธ์ที่ได้

.

ถ้าเงื่อนไขเป็นจริง (TRUE) จะคืนค่าหนึ่ง ถ้าเป็นเท็จ (FALSE) จะคืนอีกค่าหนึ่ง หรือถ้าไม่ระบุค่าสำหรับกรณีเท็จจะคืน BLANK โดยอัตโนมัติ

.

ที่เจ๋งคือ IF ทำงานได้ในทุกบริบท ไม่ว่าจะเป็น calculated column ที่ใช้ row context, measure ที่ทำงานภายใต้ filter context, calculated table หรือ visual calculation ก็ตาม ซึ่งทำให้มันเป็นฟังก์ชันที่มีความยืดหยุ่นสูงและใช้งานได้หลากหลายมากครับ 😎

.

IF คืนค่าเป็น scalar value ได้ทุกประเภท ไม่ว่าจะเป็นตัวเลข ข้อความ วันที่ หรือค่า boolean และถ้าค่าที่คืนในกรณีจริง (value_if_true) กับกรณีเท็จ (value_if_false) เป็น data type ที่แตกต่างกัน DAX จะคืนค่าเป็น VARIANT type และอาจแปลงชนิดข้อมูลโดยอัตโนมัติ

.

IF มีคุณสมบัติพิเศษคือ lazy evaluation behavior ซึ่งหมายความว่ามันจะประมวลผลเฉพาะเส้นทาง (branch) ที่ถูกเลือกตามผลลัพธ์ของเงื่อนไข ไม่ได้ประมวลผลทั้งสองเส้นทางพร้อมกัน นี่คือข้อได้เปรียบสำคัญด้านประสิทธิภาพของ IF เมื่อเทียบกับ IF.EAGER ครับ 💡

.

การใช้งานทั่วไปของ IF ในงานวิเคราะห์ข้อมูลด้วย DAX ได้แก่ การจัดกลุ่มข้อมูลตามเงื่อนไข (data segmentation), การสร้างป้ายกำกับสำหรับตัวชี้วัด KPI, การซ่อนค่าที่ไม่มีความหมายในระดับรวม (total level), การจัดการข้อมูลที่ขาดหายหรือเป็น BLANK, การป้องกันข้อผิดพลาดจากการหารด้วยศูนย์ และการสร้างตรรกะการคำนวณแบบไดนามิก (dynamic calculation logic) ที่เปลี่ยนวิธีคำนวณตาม filter context ปัจจุบัน

.

ส่วนตัวผมคิดว่านี่เป็นคุณสมบัติเด่นของ DAX ที่แตกต่างจาก Excel อย่างชัดเจนเลยครับ

ความแตกต่างสำคัญระหว่าง DAX IF กับ Excel IF

แม้ว่าฟังก์ชัน IF ใน DAX จะมีหลักการทำงานคล้ายคลึงกับ Excel IF ที่ใช้งานกันบ่อย แต่มีข้อแตกต่างสำคัญหลายประการที่นักวิเคราะห์ข้อมูลต้องเข้าใจอย่างถ่องแท้เพื่อใช้งานได้อย่างมีประสิทธิภาพ:

  • ไม่มีการอ้างอิงเซลล์แบบช่วง (cell range) – DAX ใช้รูปแบบการอ้างอิงแบบ Table[Column] แทนการอ้างอิงเซลล์แบบ A1:A10 เหมือน Excel เพราะ DAX ถูกออกแบบมาเพื่อทำงานกับโมเดลข้อมูลเชิงสัมพันธ์ (relational data model) ไม่ใช่แบบตารางคำนวณทั่วไป (spreadsheet) ซึ่งทำให้การอ้างอิงข้อมูลมีความชัดเจนและแม่นยำกว่า
  • รับรู้บริบทการกรอง (context aware) – ฟังก์ชัน IF ที่ใช้ใน measure จะทำงานภายใต้ filter context และเปลี่ยนแปลงผลลัพธ์ตามตัวกรอง (slicer), เงื่อนไขการกรอง (filter) และบริบทของ visual ปัจจุบัน ไม่ใช่แค่คืนค่าคงที่เหมือน Excel ที่คำนวณเฉพาะเซลล์เดียว ทำให้ DAX IF มีความยืดหยุ่นสูงกว่ามาก
  • การประเมินผลแบบเลือกเส้นทาง (lazy evaluation) – DAX IF จะประมวลผลเฉพาะเส้นทาง (branch) ที่ถูกเลือกตามผลลัพธ์ของเงื่อนไข ไม่ประมวลผลทั้งสองเส้นทางพร้อมกัน ซึ่งช่วยประหยัดเวลาในการคำนวณและมีประสิทธิภาพดีกว่า IF.EAGER ที่ประมวลผลทั้งสองเส้นทางก่อนตัดสินใจเลือก
  • ความแตกต่างระหว่าง BLANK กับศูนย์ – DAX แยกความแตกต่างระหว่าง BLANK() กับ 0 อย่างชัดเจนและเข้มงวด ค่า BLANK จะไม่แสดงผลใน visual (จะเห็นเป็นช่องว่าง), ไม่ถูกนับรวมในการคำนวณค่าเฉลี่ยด้วย AVERAGE และมีกฎการแพร่กระจาย (propagation rule) เฉพาะตัว เช่น BLANK + 5 จะได้ 5 ไม่ใช่ error ในขณะที่ Excel มักจะแปลง empty cell เป็น 0 โดยอัตโนมัติ ซึ่งอาจทำให้เกิดความสับสนในการตีความผลลัพธ์
  • การแปลงชนิดข้อมูลอัตโนมัติ (automatic type conversion) – ถ้าค่าที่คืนในกรณีจริง (value_if_true) กับกรณีเท็จ (value_if_false) เป็นชนิดตัวเลข (numeric type) ที่แตกต่างกัน เช่น จำนวนเต็ม (integer) กับทศนิยม (decimal) DAX จะแปลงให้เป็นชนิดที่กว้างกว่าโดยอัตโนมัติเพื่อรองรับค่าทั้งสองแบบได้ครบถ้วน

เมื่อไหร่ควรใช้ฟังก์ชัน SWITCH แทน IF

การเลือกใช้ระหว่างฟังก์ชัน IF กับ SWITCH เป็นการตัดสินใจสำคัญที่ส่งผลต่อความสามารถในการอ่านและบำรุงรักษาโค้ด (code maintainability) รวมถึงประสิทธิภาพในการประมวลผล ถ้าสูตรของคุณมีเงื่อนไขมากกว่าสองหรือสามระดับ แนะนำให้เปลี่ยนมาใช้ SWITCH แทนการซ้อน nested IF เพื่อให้โค้ดอ่านง่ายขึ้น แก้ไขและบำรุงรักษาได้สะดวกกว่า และยังให้ประสิทธิภาพในการประมวลผลที่ดีกว่าด้วย ฟังก์ชัน SWITCH เหมาะสมอย่างยิ่งกับการเปรียบเทียบค่าของนิพจน์ (expression) เดียวกับค่าหลายค่าที่เป็นไปได้ และช่วยสร้างเส้นทางการประมวลผล (execution path) ที่ชัดเจนและเข้าใจง่ายกว่า ในขณะที่ฟังก์ชัน IF เหมาะสมกว่าสำหรับการตัดสินใจแบบสองทางเลือก (binary decision) หรือเงื่อนไขตรรกะที่ซับซ้อนซึ่งต้องใช้ตัวดำเนินการตรรกะหลายตัวร่วมกัน เช่น && (AND), || (OR), หรือ ! (NOT)

เทคนิคการเพิ่มประสิทธิภาพ (Performance Optimization)

การใช้ฟังก์ชัน IF อย่างมีประสิทธิภาพต้องคำนึงถึงวิธีที่ query engine ของ DAX ประมวลผลสูตร ต่อไปนี้คือเทคนิคสำคัญที่จะช่วยเพิ่มความเร็วในการคำนวณและลดเวลาในการโหลดข้อมูล:

  • ใช้ตัวแปร VAR สำหรับค่าที่ใช้ซ้ำ – ถ้าเงื่อนไข (logical test) หรือผลลัพธ์ (result) ต้องการการคำนวณที่ซับซ้อนหรือใช้ซ้ำในหลายที่ ให้เก็บค่าไว้ในตัวแปร VAR เพื่อหลีกเลี่ยงการคำนวณซ้ำซ้อน วิธีนี้จะช่วยประหยัดเวลาในการ query ได้อย่างมาก โดยเฉพาะเมื่อมีการคำนวณ measure ที่หนักหรือมีการใช้ฟังก์ชัน CALCULATE ซ้อนกัน
  • ใช้ DIVIDE แทน IF สำหรับการหาร – สำหรับกรณีที่ต้องการป้องกันการหารด้วยศูนย์ เช่น IF(denominator 0, numerator/denominator, BLANK()) ให้ใช้ฟังก์ชัน DIVIDE(numerator, denominator, BLANK()) แทนเสมอ เพราะ query engine สามารถปรับแต่งและเพิ่มประสิทธิภาพให้กับ DIVIDE ได้ดีกว่า และยังจัดการกับ edge case ต่างๆ ได้ครบถ้วนกว่า
  • ใช้ SWITCH แทน nested IF – ถ้าโครงสร้างมีเงื่อนไขมากกว่าสามระดับ ควรใช้ฟังก์ชัน SWITCH หรือรูปแบบ SWITCH(TRUE(), …) แทนการซ้อน IF หลายชั้น เพราะจะสร้าง query plan ที่มีประสิทธิภาพดีกว่า อ่านง่ายกว่า และ maintain ง่ายกว่ามาก
  • ใช้ Logical operators แทน function – ใช้ตัวดำเนินการตรรกะ && (AND) และ || (OR) แทนฟังก์ชัน AND() และ OR() เพื่อให้ DAX engine สามารถปรับแต่งได้ดีกว่า และทำให้สูตรกระชับ อ่านง่ายขึ้น
  • ใช้ COALESCE สำหรับการตรวจสอบ BLANK แบบต่อเนื่อง – ถ้ามีหลายค่าที่ต้องการให้เป็นทางเลือกสำรอง (fallback) เช่น IF(ISBLANK(a), IF(ISBLANK(b), c, b), a) ให้ใช้ฟังก์ชัน COALESCE(a, b, c) แทนเพราะกระชับกว่าและมีประสิทธิภาพดีกว่ามาก

Context Behavior

IF ทำงานต่างกันตาม context ที่ถูกเรียกใช้:

  • Row Context (Calculated Column) – IF evaluate แต่ละ row ของ table โดยใช้ค่าจาก column ของ row นั้นๆ โดยตรง ผลลัพธ์จะถูก materialize ลงใน column และคำนวณครั้งเดียวตอน data refresh
  • Filter Context (Measure) – IF evaluate ตาม filter ปัจจุบันของ visual (slicer, filter, row/column context ใน matrix) ผลลัพธ์จะเปลี่ยนแปลงตาม user interaction
  • Context Transition – ถ้าเรียกใช้ measure ภายใน IF ที่อยู่ใน row context DAX จะทำ context transition โดยอัตโนมัติ แต่ต้องระวัง performance cost ที่เกิดขึ้น

Leave a Reply

Your email address will not be published. Required fields are marked *