300 Data Challenges Thực Chiến

[041225] – Dynamic KPI Theo Drill Level – Sức Mạnh Của ISINSCOPE – PBI Thực Chiến

Nếu bạn thích vừa học Power BI Thực Chiến trực tiếp qua GG Meet, vừa luyện giải 100+ Challenges như thế này thì có thể đăng ký khóa học Power BI Mastery Thực Chiến hoặc Data Analyst BootCamp nhé. Inbox mình TẠI ĐÂY

Vấn Đề Cần Giải Quyết

Yêu cầu của Sếp: Tôi không muốn tạo quá nhiều visual khác nhau, tôi muốn một Matrix tự động thay đổi insight theo drill-down

  • Khi nhìn REGION → Hiện % đóng góp (Khu vực đóng góp nhiều nhất?)
  • Khi drill-down COUNTRY → Hiện YoY Growth (Đất nước tăng trưởng tốt?)
  • Khi drill-down CITY → Hiện Rank (Thành phố nào dẫn đầu?)
  • Khi drill-down STORE → Hiện Target % (Store nào đạt mục tiêu?)

Kết quả mong muốn

Step by Step Guide

Bước 01: Tạo Hierarchy

Dim_Geography → Chuột phải vào “Region” → Create Hierarchy → Đặt tên: “Geography Hierarchy” → Chọn theo thứ tự: Region → Country → City → Store

Bước 02: Tạo Measures

Tạo Measure Table: Home → Enter Data → Tạo bảng trống tên “_Measures”

Bước 02.1: Tạo measure 1 → Total Sales
Total Sales = SUM(Fact_Sales[SalesAmount])

Tại sao cần? Đây là MEASURE NỀN TẢNG để các measure khác sử dụng.

Bước 02.2: Tạo measure #2 → Total Target
Total Target = SUM(Fact_Target[TargetAmount])

Tại sao cần? Tính TỔNG CHỈ TIÊU (Target) để so sánh với thực tế.

  • Level STORE: Hiển thị % đạt Target.
  • [Total Target] lấy từ Fact_Target (theo Store + Month)
Bước 02.3: Tạo measure #3 → Sales Last Year
Sales LY = 
CALCULATE(
    [Total Sales],
    SAMEPERIODLASTYEAR(Dim_Date[Date])
)

Tại sao cần? Lấy doanh số CÙNG KỲ NĂM TRƯỚC để so sánh tăng trưởng

  • Level COUNTRY: Hiển thị YoY Growth %
  • YoY = ([Total Sales] – [Sales LY]) / [Sales LY]
  • Cần [Sales LY] làm mẫu số để tính %
Bước 02.4: Tạo measure #4 → YoY Growth %
YoY Growth % = 
VAR _CurrentYear = [Total Sales]
VAR _LastYear = [Sales LY]
RETURN
    IF(
        _LastYear = 0 || ISBLANK(_LastYear),
        BLANK(),
        DIVIDE(_CurrentYear - _LastYear, _LastYear)
    )

Tại sao cần? Đo lường TỐC ĐỘ TĂNG TRƯỞNG so với năm trước

  • Level COUNTRY: Matrix hiển thị YoY Growth %.
  • User có thể so sánh tốc độ tăng trưởng giữa các quốc gia.
  • Màu xanh = tăng trưởng, Đỏ = suy giảm.
Bước 02.5: Tạo measure #5 → % of Total
% of Total = 
VAR _CurrentSales = [Total Sales]
VAR _TotalSales = CALCULATE([Total Sales], REMOVEFILTERS(Dim_Geography))
RETURN
    DIVIDE(_CurrentSales, _TotalSales)

Tại sao cần? Tính TỶ TRỌNG (%) của từng vùng so với TỔNG TOÀN BỘ

│   ┌─────────────────────────────────────────────────────────────────────
│   │  % OF TOTAL (so với TOÀN CÔNG TY):                               
│   │  ─────────────────────────────────                                 
│   │  ASIA: $9M / TOÀN CÔNG TY: $15M                                                                                               
│   │  ├── Vietnam:   $5.4M → 5.4M / 15M = 36% (của TOÀN CẦU)         
│   │  ├── Singapore: $2.1M → 2.1M / 15M = 14% (của TOÀN CẦU)         
│   │  └── Thailand:  $1.5M → 1.5M / 15M = 10% (của TOÀN CẦU)          
│   │                                       ─────                        
│   │                                        60% (không phải 100%!)                                                                   
│   │  Ý nghĩa: Vietnam đóng góp 36% doanh số TOÀN CÔNG TY                                                                     
│   └─────────────────────────────────────────────────────────────────────   
│   ┌─────────────────────────────────────────────────────────────────────
│   │  % OF PARENT (so với ASIA - cấp cha):                               
│   │  ────────────────────────────────────                              
│   │  ASIA: $9M (đây là Parent)                                                                                                     
│   │  ├── Vietnam:   $5.4M → 5.4M / 9M = 60% (của ASIA)              
│   │  ├── Singapore: $2.1M → 2.1M / 9M = 23% (của ASIA)                
│   │  └── Thailand:  $1.5M → 1.5M / 9M = 17% (của ASIA)               
│   │                                       ─────                          
│   │                                       100% ✓ (tổng = 100%!)                                                                          
│   │  Ý nghĩa: Vietnam đóng góp 60% doanh số trong CHÂU Á             
│   └─────────────────────────────────────────────────────────────────────  
% of Parent = 
VAR _CurrentSales = [Total Sales]
VAR _ParentSales = 
    SWITCH(
        TRUE(),
        -- Level Store: Parent = City
        ISINSCOPE(Dim_Geography[Store]), 
            CALCULATE([Total Sales], REMOVEFILTERS(Dim_Geography[Store])),
        -- Level City: Parent = Country
        ISINSCOPE(Dim_Geography[City]), 
            CALCULATE([Total Sales], REMOVEFILTERS(Dim_Geography[City])),
        -- Level Country: Parent = Region
        ISINSCOPE(Dim_Geography[Country]), 
            CALCULATE([Total Sales], REMOVEFILTERS(Dim_Geography[Country])),
        -- Level Region: Parent = Grand Total
        ISINSCOPE(Dim_Geography[Region]), 
            CALCULATE([Total Sales], REMOVEFILTERS(Dim_Geography[Region])),
        -- Grand Total: so với chính nó
        [Total Sales]
    )
RETURN
    DIVIDE(_CurrentSales, _ParentSales)
Bước 02.6: Tạo measure #6 → Target Achievement %
Target Achievement % = DIVIDE([Total Sales], [Total Target])

Tại sao cần? Đo lường mức độ HOÀN THÀNH MỤC TIÊU của từng Store

  • Level STORE: Hiển thị Target Achievement %
  • User drill down tới Store để xem store nào đạt/không đạt target
Bước 02.7: Tạo measure #7 → Dynamic KPI Value
Dynamic KPI Value = 
SWITCH(
    TRUE(),
    // Level 4: Store - Hiện % đạt Target
    ISINSCOPE(Dim_Geography[Store]), [Target Achievement %],
    
    // Level 3: City - Hiện Rank trong Country
    ISINSCOPE(Dim_Geography[City]), 
        RANKX(
            ALLSELECTED(Dim_Geography[City]),
            [Total Sales],
            ,
            DESC,
            DENSE
        ),
    
    // Level 2: Country - Hiện YoY Growth %
    ISINSCOPE(Dim_Geography[Country]), [YoY Growth %],
    
    // Level 1: Region - Hiện % đóng góp vào tổng
    ISINSCOPE(Dim_Geography[Region]), [% of Total],
    
    // Level 0: Grand Total
    [Total Sales]
)

Tại sao cần? Bạn có 1 Matrix với 4 levels: Region → Country → City → Store. Mỗi level cần hiển thị KPI KHÁC NHAU:

  • REGION → Hiện % đóng góp (Khu vực đóng góp nhiều nhất?)
  • COUNTRY → Hiện YoY Growth (Đất nước tăng trưởng tốt?)
  • CITY → Hiện Rank (Thành phố nào dẫn đầu?)
  • STORE → Hiện Target % (Store nào đạt mục tiêu?)

Giải pháp ISINSCOPE: TẠO 1 MEASURE DUY NHẤT – Tự động PHÁT HIỆN đang ở level nào – Trả về KPI phù hợp cho level đó

Giải thích luồng hoạt động của Dynamic KPI Value qua TẤT CẢ 5 LEVELS

ALLSELECTED Hoạt Động Như Thế Nào Trong RANKX
│   FORMULA CẦN PHÂN TÍCH:                                               
│   ─────────────────────────                                                                                                          
│   RANKX(                                                                   
│       ALLSELECTED(Dim_Geography[City]),   ← Tham số 1: Bảng để rank       
│       [Total Sales],                       ← Tham số 2: Giá trị so sánh   
│       ,                                    ← Tham số 3: Value (skip)       
│       DESC,                                ← Tham số 4: Thứ tự             
│       DENSE                                ← Tham số 5: Cách xử lý ties    
│   )                                                                                                                                               
│   CÂU HỎI: ALLSELECTED(Dim_Geography[City]) trả về GÌ?                   

SCENARIO: User đang drill down từ Asia → Vietnam → Đang xem row “Ho Chi Minh”

│   MATRIX VISUAL:                                                         
│   ┌──────────────────────────────────────────────────────────────────────  
│   │ ▼ Asia                                                                  
│   │   ▼ Vietnam                                                             
│   │     ├ Ho Chi Minh  │ $2,700,000 │ [Dynamic KPI = ?]  ◄── ĐANG TÍNH    
│   │     ├ Hanoi        │ $1,620,000 │                                      
│   │     └ Da Nang      │ $1,080,000 │                                      
│   │   ▶ Singapore                                                         
│   │   ▶ Thailand                                                          
│   └──────────────────────────────────────────────────────────────────────                                                                         
│   FILTER CONTEXT CHO ROW "Ho Chi Minh":                                  
│   ──────────────────────────────────────────                                                                                                         
│   ┌────────────────────────────────────────────────────────────────────    
│   │  Dim_Geography[Region]  = "Asia"          ← Từ drill Level 1          
│   │  Dim_Geography[Country] = "Vietnam"       ← Từ drill Level 2           
│   │  Dim_Geography[City]    = "Ho Chi Minh"   ← Từ ROW HIỆN TẠI          
│   │  Dim_Date[Year]         = 2024            ← Từ Slicer       
│   └────────────────────────────────────────────────────────────────────
┌─────────────────────────────────────────────────────────────────────────────
│   BƯỚC: SWITCH(TRUE(), ...) kiểm tra ISINSCOPE                            
├─────────────────────────────────────────────────────────────────────────────                                                                          
│   Dynamic KPI Value = SWITCH(TRUE(),                                        
│       ISINSCOPE(Store)?   → FALSE (chưa drill tới Store)                   
│       ISINSCOPE(City)?    → TRUE ✅ DỪNG!                                   
│       ...                                                                    
│   )                                                                                                                                                    
│   → Trả về: RANKX(ALLSELECTED(Dim_Geography[City]), [Total Sales], ...)                                                                                
│   BÂY GIỜ CẦN HIỂU: ALLSELECTED(City) TRẢ VỀ GÌ?                                                                                                 
└─────────────────────────────────────────────────────────────────────────────
│   ALLSELECTED(Dim_Geography[City]) hỏi:                                     
│   "Những City nào VISIBLE trong context NGOÀI row hiện tại?"                                                                                       
│   ┌────────────────────────────────────────────────────────────────                                                                       
│   │  ALLSELECTED PHÂN TÍCH TỪNG FILTER:                                    
│   │  ══════════════════════════════════                                                                                                     
│   │  ┌─────────────────┬─────────────┬────────────┬────────────────  
│   │  │    Filter       │   Value     │ Loại Filter│ ALLSELECTED?     
│   │  ├─────────────────┼─────────────┼────────────┼────────────────                
│   │  │ Region = Asia   │ "Asia"      │ External   │ ✅ GIỮ LẠI     
│   │  │                 │             │ (từ drill) │                             
│   │  │ Country = VN    │ "Vietnam"   │ External   │ ✅ GIỮ LẠI           
│   │  │                 │             │ (từ drill) │                           
│   │  │ City = HCM      │ "Ho Chi     │ ROW        │ ❌ BỎ QUA!       
│   │  │                 │  Minh"      │ (current)  │ (để lấy all)          
│   │  │ Year = 2024     │ 2024        │ Slicer     │ ✅ GIỮ LẠI     
│   │  └─────────────────┴─────────────┴────────────┴─────────────────                                                              
│   │  NGUYÊN TẮC ALLSELECTED:                                      
│   │  ─────────────────────────────                                    
│   │  • GIỮ: Filter từ BÊN NGOÀI (drill, slicer, report filter)        
│   │  • BỎ:  Filter từ ROW HIỆN TẠI (để lấy tất cả items cùng level)                                                                  
│   └─────────────────────────────────────────────────────────────────                                                                       
│   Sau khi áp dụng filters (GIỮ Region, Country, Year; BỎ City):                                                                               
│   ┌───────────────────────────────────────────────────────────────                                                            
│   │  ALLSELECTED(Dim_Geography[City]) TRẢ VỀ:                     
│   │  ═════════════════════════════════════════                                                                                  
│   │  WHERE:  Region = "Asia"                                       
│   │          Country = "Vietnam"                                   
│   │          Year = 2024                                          
│   │          City = (KHÔNG FILTER - lấy tất cả!)                                                                         
│   │  ┌────────────────────────────────────────────────────────────                                                         
│   │  │  KẾT QUẢ: Bảng gồm 3 cities của Vietnam                                                                     
│   │  │  ┌────────────────┐                                      
│   │  │  │     City       │                                        
│   │  │  ├────────────────┤                                        
│   │  │  │ Ho Chi Minh    │  ← Row 1                            
│   │  │  │ Hanoi          │  ← Row 2                               
│   │  │  │ Da Nang        │  ← Row 3                               
│   │  │  └────────────────┘                                                                                           
│   │  │  → Đây là 3 cities VISIBLE trong context hiện tại!         
│   │  │  → User đang xem Vietnam → Chỉ lấy cities của Vietnam                                                                 
│   │  └───────────────────────────────────────────────────────────── 
│   └────────────────────────────────────────────────────────────────                                                                   
│   NẾU DÙNG ALL(City) THAY VÌ ALLSELECTED(City):                      
│   ──────────────────────────────────────────────────                  
│   ALL(City) BỎ QUA TẤT CẢ FILTER → Trả về 50 cities toàn cầu!          
│   → Rank sẽ so với thế giới, không phải Vietnam!                                                                                         
└────────────────────────────────────────────────────────────────────
│   RANKX(                                                                    
│       {Ho Chi Minh, Hanoi, Da Nang},    ← Bảng từ ALLSELECTED              
│       [Total Sales],                     ← Tính Sales cho mỗi city         
│       ,                                                                     
│       DESC,                              ← Cao nhất = Rank 1               
│       DENSE                                                                 
│   )                                                                                                                                             
│   ┌───────────────────────────────────────────────────────────────                                                                   
│   │  RANKX HOẠT ĐỘNG TỪNG BƯỚC:                                            
│   │  ══════════════════════════                                                                                                           
│   │  Iterate qua bảng, tính [Total Sales] cho mỗi city                                                                     
│   │  ┌────────────────┬──────────────────────────┬────────────────
│   │  │     City       │  CALCULATE Total Sales   │    Result      
│   │  ├────────────────┼──────────────────────────┼────────────────
│   │  │ Ho Chi Minh    │  SUM WHERE City="HCM"    │  $2,700,000        
│   │  │ Hanoi          │  SUM WHERE City="Hanoi"  │  $1,620,000    
│   │  │ Da Nang        │  SUM WHERE City="Da Nang"│  $1,080,000    
│   │  └────────────────┴──────────────────────────┴────────────────
│   │  Sắp xếp theo DESC (cao → thấp)                       
│   │  ┌────────────────┬────────────────┬────────────────           
│   │  │     City       │    Sales       │   Rank (DESC)         
│   │  ├────────────────┼────────────────┼────────────────           
│   │  │ Ho Chi Minh    │  $2,700,000    │   #1                     
│   │  │ Hanoi          │  $1,620,000    │   #2                   
│   │  │ Da Nang        │  $1,080,000    │   #3                     
│   │  └────────────────┴────────────────┴────────────────                                                                              
│   │  Trả về rank của ROW HIỆN TẠI (Ho Chi Minh)            
│   │  ───────────────────────────────────────────────────                                                                             
│   │  Current row = "Ho Chi Minh" → Rank = #1                                                                                        
│   └─────────────────────────────────────────────────────                                                                       
│   KẾT QUẢ CUỐI CÙNG:                                                 
│   ─────────────────────                                                  
│   [Dynamic KPI Value] cho row "Ho Chi Minh" = 1 (Rank #1)                                                                                       
└──────────────────────────────────────────────────────────────────
│   Power BI lần lượt tính [Dynamic KPI Value] cho từng row:                  
│   ┌────────────────────────────────────────────────────────────────────                                                                       
│   │  ROW "Hanoi":                                                    
│   │  ─────────────                                                     
│   │  Filter Context: Region=Asia, Country=Vietnam, City=Hanoi          
│   │  ALLSELECTED(City) = {Ho Chi Minh, Hanoi, Da Nang} (giống nhau!)   
│   │  RANKX tính: Hanoi $1.62M → Rank #2                                
│   │  ─────────────────────────────────────────────────────────────    
│   │  ROW "Da Nang":                                                    
│   │  ──────────────                                                    
│   │  Filter Context: Region=Asia, Country=Vietnam, City=Da Nang       
│   │  ALLSELECTED(City) = {Ho Chi Minh, Hanoi, Da Nang} (giống nhau!) 
│   │  RANKX tính: Da Nang $1.08M → Rank #3                                                                                           
│   └────────────────────────────────────────────────────────────────────
Bước 02.8: Tạo measure #8 → Dynamic KPI Label
Dynamic KPI Label = 
SWITCH(
    TRUE(),
    ISINSCOPE(Dim_Geography[Store]), "Target %",
    ISINSCOPE(Dim_Geography[City]), "Rank",
    ISINSCOPE(Dim_Geography[Country]), "YoY %",
    ISINSCOPE(Dim_Geography[Region]), "% Total",
    "Sales"
)

Tại sao cần? Hiển thị TÊN/LABEL cho Dynamic KPI Value

  • [Dynamic KPI Value] trả về số: 0.08, 3, 0.95, 0.36…
  • User nhìn số không biết đó là GÌ!
  • 0.08 là YoY%? hay Target%? hay % of Total?

Giải pháp: Tạo measure song song trả về LABEL/TÊN

Bước 02.9: Tạo measure #9 → Dynamic Color Value
Dynamic Color Value = 
VAR _Value = [Dynamic KPI Value]
VAR _Label = [Dynamic KPI Label]
RETURN
    SWITCH(
        _Label,
        "Target %", IF(_Value >= 1, 1, -1),
        "Rank", IF(_Value <= 3, 3, IF(_Value <= 6, 2, 1)),
        "YoY %", IF(_Value > 0, 1, IF(_Value < 0, -1, 0)),
        "% Total", _Value * 10,
        0
    )

Tại sao cần? Chuyển KPI thành GIÁ TRỊ SỐ để dùng cho CONDITIONAL FORMAT

  • Power BI Conditional Formatting cần GIÁ TRỊ SỐ thống nhất.
  • Nhưng các KPI có LOGIC KHÁC NHAU:
    • Target %: >= 100% = TỐT (xanh)
    • YoY %: > 0% = TỐT (xanh)
    • Rank: <= 3 = TỐT (xanh)
    • % Total: Cao = TỐT (xanh)

Giải pháp: Chuẩn hóa tất cả về 1 thang đo: Dương = TỐT, Âm = XẤU – Dùng số này cho Conditional Formatting

Bước 02.10: Tạo measure #10 → Dynamic Color Value

Dynamic KPI = 
VAR _Value = [Dynamic KPI Value]
VAR _Label = [Label]
VAR _ColorValue = [Dynamic Color Value]

VAR _Icon = 
    SWITCH(
        TRUE(),
        _ColorValue >= 1, "▲ ",      // Lên (positive)
        _ColorValue < 0, "▼ ",       // Xuống (negative)
        "► "                          // Ngang (neutral)
    )

VAR _FormattedValue = 
    SWITCH(
        _Label,
        "Target %", FORMAT(_Value, "0.0%"),
        "Rank", "#" & FORMAT(_Value, "0"),
        "YoY %", IF(_Value >= 0, "+", "") & FORMAT(_Value, "0.0%"),
        "% Total", FORMAT(_Value, "0.0%"),
        "Sales", 
            SWITCH(
                TRUE(),
                _Value >= 1000000, FORMAT(_Value / 1000000, "$#,##0.0") & "M",
                _Value >= 1000, FORMAT(_Value / 1000, "$#,##0.0") & "K",
                FORMAT(_Value, "$#,##0")
            )
    )

RETURN
    _Icon & _FormattedValue

Tại sao cần? Kết hợp TẤT CẢ để hiển thị

  • [Dynamic KPI Value] = số thô (0.08, 3, 0.95)
  • [Dynamic KPI Label] = text (“YoY %”, “Rank”)
  • [Dynamic Color Value] = số để format màu (1, -1, 3)
  • Cần KẾT HỢP thành 1 ô hiển thị đẹp: “▲ +8.0%”
│  ┌──────────────────────────────────────────────────────────────────
│  │  FORMAT() - Định dạng số thành text                             
│  │  ──────────────────────────────────────                           
│  │  CÚ PHÁP: FORMAT(value, format_string)                              
│  │  ┌─────────────────────────────────────────────────────────────── 
│  │  │ Format String   │ Input    │ Output      │ Giải thích         
│  │  ├─────────────────┼──────────┼─────────────┼────────────────────
│  │  │ "0.0%"          │ 0.08     │ "8.0%"      │ Phần trăm 1 số lẻ 
│  │  │ "0"             │ 3        │ "3"         │ Số nguyên         
│  │  │ "$#,##0"        │ 1234     │ "$1,234"    │ Tiền có dấu phẩy  
│  │  │ "$#,##0.0"      │ 1234.5   │ "$1,234.5"  │ Tiền 1 số lẻ      
│  │  └───────────────────────────────────────────────────────────────                                                           
│  │  Ký hiệu:                                                        
│  │  • 0 = Hiện số, nếu không có thì hiện 0                          
│  │  • # = Hiện số, nếu không có thì bỏ qua                           
│  │  • % = Nhân 100 và thêm dấu %                                     
│  │  • , = Dấu phân cách hàng nghìn                                   
Bước 03: Tạo Matrix Visual