שתי שפות, שני מנועים: M מכין את הנתונים פעם אחת, DAX מחשב עליהם בכל קליק. מ-SUM הראשון ועד CALCULATE, משפחת פונקציות החלון ועשרים פונקציות DAX מתקדמות.
רק בזמן הרענון. אחר כך העבודה הסתיימה והטבלה קבועה
רגישות לאותיות
כן — Table.AddColumn עובד, table.addcolumn יזרוק שגיאה
מבנה
let לשלבים · in לפלט. כל שלב מקבל שם ונשען על הקודם
מקבילה מוכרת
SELECT · JOIN · WHERE ב-SQL, או Pandas בעולם ה-Python
✓ כלל אצבע: "צריך לשנות את מבנה הטבלה או לנקות מידע?" ← זה תפקיד של M.
פרק I · שתי שפות, שני מנועים
שפת DAX — חמשת התפקידים
DAX — Data Analysis Expressions — היא שפת הנוסחאות של מודל הנתונים. הסינטקס מזכיר Excel, אבל אקסל עובד על תאים בודדים ו-DAX על עמודות וטבלאות שלמות.
01
מדדים (Measures)
הכוח האמיתי: חישובים דינמיים שמשתנים בזמן אמת לפי מה שהמשתמש בוחר. סך הכנסות, ממוצע ללקוח, אחוז רווח.
02
עמודות מחושבות
נתון חדש לטבלה קיימת, מחושב לכל שורה בנפרד — למשל מחיר יחידה × כמות = סך מכירה לשורה.
03
טבלאות מחושבות
טבלאות חדשות לגמרי מתוך הנתונים הקיימים — טבלת תאריכים, טבלת סיכום, טבלת פרמטרים.
04
Time Intelligence
השוואות לאורך זמן: YTD, החודש מול אותו חודש אשתקד, ממוצעים נעים.
05
שליטה ב-Filter Context
עם CALCULATE משנים את חוקי הסינון: מציגים מכירות של מוצר מסוים גם כשהגרף מציג את כולם.
נקודת המפתח: DAX נכנסת לפעולה אחרי M — היא לא מנקה ולא טוענת, היא מנתחת. מספרים חיים שמגיבים לכל קליק.
פרק I · שתי שפות, שני מנועים
M מול DAX — טבלת ההשוואה
אותו קובץ, שני מנועים שונים לגמרי. הטבלה הזו היא כל ההבדל.
מאפיין
שפת M · Power Query
שפת DAX · Data Model
מטרה עיקרית
עיצוב, ניקוי והכנת נתונים (ETL)
ניתוח, אגרגציות וחישובים עסקיים
מתי מחושב
רק בזמן רענון (Data Refresh)
דינמית, בזמן השימוש בדשבורד
על איזה מנוע
Power Query Mashup Engine
VertiPaq — In-Memory Columnar
רגישות לאותיות
כן (Case Sensitive)
לא (Case Insensitive)
כלל אצבע
"לשנות מבנה או לנקות מידע?"
"לחשב משהו שמגיב לפילטרים?"
M מכין את השולחן — פעם אחת. DAX מבשל לפי ההזמנה — כל פעם מחדש.
פרק I · שתי שפות, שני מנועים
כלל הזהב — דחיפת העיבוד אחורה
Roche's Maxim: בצעו כל טרנספורמציה כמה שיותר "אחורה" בשרשרת — קרוב ככל האפשר למקור הנתונים.
1
מסד הנתונים
SQL / Database. אם אפשר לעשות את הטרנספורמציה כאן — זו האפשרות המועדפת. השרת עושה את העבודה, לא הדוח.
2
Power Query
שפת M. אם אי אפשר במסד — מנקים ומעצבים כאן, פעם אחת בכל רענון.
3
DAX
Data Model. רק לחישובים שחייבים להיות דינמיים ולהגיב למשתמש הקצה.
סדר העדיפות: מקור הנתונים ← Power Query ← DAX. ככל שאחורה יותר, המודל קל ומהיר יותר.
✓ הטעות הנפוצה: לבנות עמודה מחושבת ב-DAX כשאפשר היה לחשב אותה ב-M. העמודה תופסת זיכרון בכל רענון, לנצח.
פרק II · יצירת טבלאות ב-M
שלוש דרכים ליצור טבלה ב-Power Query
לפני שעוברים ל-DAX — יוצרים טבלאות בעורך. שלוש דרכים, וכל אחת למצב אחר.
01
מקור חיצוני
טעינה מאקסל או ממסד נתונים — כמו שבע הטבלאות של AdventureWorks.
02
Reference
גזירת טבלה חדשה מטבלה קיימת. השינויים במקורית זורמים אליה אוטומטית.
03
Enter Data
הזנה ידנית של טבלה קטנה — טבלת מיפוי, טבלת פרמטרים, רשימת יעדים.
M
let Source = Sales, Cols = Table.SelectColumns( Source, {"CustomerKey"} ), Uniq = Table.Distinct( Cols ) in Uniq
שימו לב:Reference ממשיך מהשאילתה המקורית, Duplicate יוצר עותק מנותק. כל טבלה שנוצרת ב-M "נצרבת" — היא קבועה עד הרענון הבא.
פרק II · יצירת טבלאות ב-M
הדגמה — טבלת מימד ב-Reference
גוזרים טבלת מימד מתוך טבלת העובדה — חמישה קליקים, בלי לכתוב שורת קוד.
קליק ימני על Sales ← Referenceנוצרת שאילתה חדשה שמצביעה על Sales כנקודת פתיחה.
Choose Columnsמשאירים רק את CustomerKey ומורידים את השאר.
Remove Rows ← Remove Duplicatesמקבלים רשימת לקוחות ייחודיים.
Rename ← DimCustomerשם השאילתה הוא שם הטבלה במודל.
Close & Applyהטבלה נטענת למודל ומוכנה לקשר.
18,485שורות בטבלה החדשה
18,484לקוחות אמיתיים
-1המפתח הטכני של ערוץ Reseller
✓ הפרש של שורה אחת: מכירות ערוץ ה-Reseller נושאות CustomerKey = -1 — "לא רלוונטי". זכרו את זה, נחזור לזה בספירות.
פרק II · יצירת טבלאות ב-M
טבלת תאריכים — שלושת התנאים
זו הטבלה הנפוצה ביותר שאנליסטים יוצרים ב-M — כמעט בכל מודל שתבנו בקריירה. בלעדיה אין YoY, אין YTD, ואין השוואות תקופות.
1
רציפה
כל יום בטווח, בלי אף חור. אצלנו: 1,461 ימים רצופים מ-1.7.2017 עד 30.6.2021.
2
מפתח ייחודי
DateKey אחד לכל יום — זה צד ה-1 בקשר אל טבלת העובדה.
3
מסומנת
Mark as date table — בלי זה פונקציות Time Intelligence לא יעבדו נכון.
שלב
מה לוחצים
1
Home ← Get Data ← Blank Query
2
בסרט: Advanced Editor
3
מוחקים את התוכן ומדביקים את הסקריפט
4
מוודאים ✓ No syntax errors ← Done
5
קליק ימני ← Rename ← Date Ultimate ← Close & Apply
6
במודל: קשר OrderDateKey ← DateKey + Mark as date table
הסקריפט המוכן: Date_Ultimate_Table.m — 34 עמודות, כולל השנה הפיסקלית של AdventureWorks (יולי–יוני), מותאם 1:1 ל-OrderDateKey שבקובץ.
פרק II · יצירת טבלאות ב-M
ככה זה נראה על המסך — צעד אחר צעד
ארבעה צילומי מסך מתוך העורך — בדיוק מה שתראו כשתעשו את זה בעצמכם.
שלב 1 — פותחים את העורך לשונית Home ← Advanced Editor. הכפתור יושב בקבוצת Query, משמאל ל-Manage.שלב 2 — נפתחת Query1 ריקה מוחקים את כל התוכן שבפנים ומדביקים את הסקריפט המלא.שלב 3 — בודקים למטה חייבת להופיע ההודעה ✓ No syntax errors have been detected לפני שלוחצים Done.שלב 4 — קליק ימני על השאילתהRename ← Date Ultimate. שם השאילתה הוא שם הטבלה שתופיע במודל.
✓אל תשכחו:Close & Apply, ואז במסך Model — בונים את הקשר OrderDateKey ← DateKey ומסמנים Mark as date table.
פרק III · מבוא ל-DAX
מה זה DAX — ולמה אי אפשר בלעדיו
המודל מחובר: 121,253 שורות מכירה, מוצרים, לקוחות ותאריכים. הנתונים יושבים שם — אבל הם לא אומרים כלום.
מה יש לנו
טבלאות מחוברות
שבע טבלאות, קשרים תקינים, כוכב מסודר. אבל אין בקובץ עמודה בשם "רווח" או "מספר לקוחות".
מה שואלים
שאלות עסקיות
"כמה מכרנו?" · "מה הרווח?" · "רק אופניים?" · "כמה לקוחות?" — לכל שאלה כזו אין עמודה מוכנה.
נתונים גולמייםמה שנטען למודל
←
DAXשפת הנוסחאות
←
תשובות עסקיותמספרים חיים
"חיים" = מחושבים מחדש כל הזמן. המשתמש לחץ על "Bikes"? כל המדדים מחושבים מחדש באותה שנייה, רק על אופניים.
פרק III · מבוא ל-DAX
שלושה דברים ש-DAX יודע ליצור
שלושה אובייקטים, ולכל אחד מקום משלו. ההבדל ביניהם הוא ההבדל הכי חשוב בשיעור.
Calculated Table
טבלה שלמה חדשה
מתי? כשצריך טבלה שלא קיימת במקור — טבלת תאריכים, טבלת פרמטרים. נדיר יחסית: רוב הטבלאות מגיעות מ-M.
Calculated Column
ערך לכל שורה
מתי? כשצריך ערך פר-שורה לסינון או לקיבוץ — למשל Profit לכל שורת מכירה. מחושבת ברענון, נשמרת פיזית, תופסת זיכרון.
⭐ Measure
חישוב מצרפי חי
מתי? כמעט תמיד — לכל סיכום וחישוב. מחושב בכל אינטראקציה, לא נשמר. זה 90% מהשיעור.
Discount Given = [Before Discount] - [Total Sales]
$110,336,782לפני הנחהSUMX
$109,809,274ששולם בפועלSUM
$527,508סך ההנחותההפרש
והטוויסט: עמודת Unit Price Discount Pct מציגה 0 בכל השורות. אבל 3,282 שורות קיבלו הנחה בפועל — במדרגות של 2% ועד 40% — וכולן בערוץ ה-Reseller (ה-1- שכבר הכרנו). הדאטה "שיקר" בעמודה אחת, ו-SUMX חשף את האמת.
✓ מדד טוב הוא גם כלי ביקורת על הנתונים. כשמספר לא מסתדר — זו לא תמיד שגיאה בנוסחה. לפעמים זו שגיאה במקור.
פרק VII · Filter Context ו-CALCULATE
Filter Context — מדד אחד, ערך שונה בכל תא
כתבנו את [Total Sales] פעם אחת. איך הוא מציג ארבעה מספרים שונים בטבלה אחת?
קטגוריה
[Total Sales]
Bikes
$94.6M
Components
$11.8M
Clothing
$2.1M
Accessories
$1.3M
Power BI מסתכל איפה התא יושבשורת Bikes? עמודת FY2020?
בונה לתא "בועת סינון"רק השורות שרלוונטיות לו — זה ה-Filter Context.
מריץ את המדד בתוך הבועהבשורת Bikes המדד "רואה" רק אופניים.
לכן מדד לא "נשבר" מפילטרים — הוא חי מהם. כל תא מקבל בועה משלו, והמדד רץ בפנים. CALCULATE הוא הדרך לשנות את הבועה בכוונה.
פרק VII · Filter Context ו-CALCULATE
CALCULATE — המלך של DAX
מריצה מדד קיים — אבל קודם משנה את בועת הסינון לפי התנאים שלכם. הפונקציה החשובה ביותר ב-DAX.
ל-ALL אין תנאי ואין איפה לכתוב אחד. הוא רק קובע את נקודת הפתיחה: הטבלה המקורית, נקייה מסלייסרים.
על הדף פעיל סלייסר Color = "Red", והוא מציג $21,597,891
DAX
All Products Sales = CALCULATE ( [Total Sales], ALL ( Product ) )
$21,597,891מה שהדף מציג (Red בלבד)
$109,809,274מה ש-ALL מחזיר — הכול
וריאציה
מה היא מוחקת
ALL( Product )
טבלה שלמה — כל הפילטרים ממנה. סלייסר על טבלת Date לא נוגע בו
ALL( Product[Color] )
עמודה בודדת — משחררת רק את הצבע ומשאירה את השאר
ALL( 'Date' )
בשביל כל התקופות
REMOVEFILTERS()
הכול מהכול
✓מתי ALL לבד מספיק? כשהשאלה היא "וכמה זה הכל?" — כבסיס להשוואה מול המסונן. לכל שאלה שיש בה תנאי ("רק יקרים", "רק אדומים") — ALL לבד לא יעזור: הוא פותח הכול ולא בוחר כלום.
ALL לבד = חצי עבודה: מחק ולא בחר. רוצים למחוק וגם לבחור מחדש? מכניסים אותו לתוך FILTER — בנושא הבא.
פרק VII · Filter Context ו-CALCULATE
FILTER — השומר שעובר שורה-שורה
FILTER רץ שורה-שורה על הטבלה שנתתם לו, ומחזיר טבלה חדשה — רק השורות שענו TRUE.
ALL פתח את כל 397 המוצרים, ו-FILTER בחר מתוכם 13 — בלי קשר לסלייסר.
המלכודת מהדאטה: סלייסר על Product[Category] = "Clothing", ומבקשים אדום. עם FILTER ( Product, … ) תקבלו $0 — כי אין ביגוד אדום שנמכר בכלל. עם FILTER ( ALL ( Product ), … ) תקבלו $21,597,891. כשהמדד "מקולקל" — כמעט תמיד חסר ALL.
✓שתי השאלות לפני כל מדד: (א) יש לי תנאי לבדוק? ← FILTER, או בוליאני אם התנאי פשוט. (ב) להתעלם ממה שמסונן בדף? ← ALL בפנים. שתי תשובות "כן" = FILTER ( ALL ( … ), … ).
פרק VII · Filter Context ו-CALCULATE
שלילה, שחרור ואחוז מהכל
שלוש תנועות שמסיימות את הפרק: שלילה, שחרור, ואחוז מהסך הכולל.
All Sales = CALCULATE ( [Total Sales], ALL ( Sales ) )
% of Total = DIVIDE ( [Total Sales], [All Sales] )
86.2%חלקם של האופניים מכלל המכירות
שכבות שוב:[Total Sales] ← [All Sales] ← [% of Total]. כל מדד עומד על כתפי הקודם.
פרק VIII · פונקציות חלון
הרעיון — רשימה אחת, שבע שאלות
כל תא בטבלה חי לבד בבועה. בשורת Canada המדד רואה רק את קנדה — הוא לא יודע שארה"ב לפניה, שאוסטרליה אחריה, או שהוא שני מתוך שישה. פונקציית חלון היא הכלי שנותן לתא להסתכל על השכנים ברשימה.
שורת המסדר — הרשימה שכל הפרק עומד עליה
שש המדינות במודל, מסודרות לפי מכירות. כל פונקציה בפרק שואלת שאלה אחרת על אותה רשימה בדיוק.
מקום
Country
[Total Sales]
1
United States
$62,997,591
2
Canada
$16,355,770
3
Australia
$10,655,336
4
United Kingdom
$7,670,721
5
France
$7,251,556
6
Germany
$4,878,300
שבע פונקציות, שבע שאלות
פונקציה
השאלה שהיא שואלת
ORDERBY
לפי מה מסתדרים בתור
PARTITIONBY
שורה אחת ארוכה, או כמה שורות נפרדות
ROWNUMBER
"מה המספר הסידורי שלי?"
RANK
"איזה מקום תפסתי?"
INDEX
"מי עומד במקום N?"
OFFSET
"מי השכן שלי?"
WINDOW
"תן לי קטע מהמסדר"
שלושה כללים שמונעים 95% מהתקלות
01
שם הטבלה — מההשלמה האוטומטית
במודל יש טבלאות כפולות (SalesTerritory וגם Sales data_Territory). מתחילים להקליד ובוחרים מהרשימה. מדד שמצביע על טבלה שהוויזואל לא משתמש בה = ריק.
02
המדד תמיד בסוגריים מרובעים
[Total Sales] ולא SUM ( … ) גלמי. SUM בתוך פונקציית חלון מתעלם משורות ההקשר — וכל המדינות יוצאות באותו מקום.
03
כלל הריק — רמת הפירוט
הפונקציות עונות רק כשהוויזואל נמצא ברמת הפירוט של הרשימה. הרשימות כאן ברמת Country ← המדדים חיים בטבלה שבשורותיה Country. בקארד או בשורת Total — התנהגות אחרת.
הערת שמות: במדריך המקורי המדד נקרא [total salse] והטבלה SalesTerritory. כאן נשתמש ב-[Total Sales] — המדד שבנינו בפרק IV — ובאותה טבלת SalesTerritory. אם השמות אצלכם שונים, החליפו אותם ותו לא.
פרק VIII · פונקציות חלון
1 · ORDERBY — לפי מה מסתדרים
ORDERBY היא ההחלטה הראשונה של כל פונקציית חלון: לפי מה מסתדרים בתור. היא לא עומדת בפני עצמה — תמיד ארגומנט בתוך פונקציה מארחת.
✓ ברירת המחדל ASC נכנסת לפעולה: Germany = 1 · France = 2 · United Kingdom = 3 · Australia = 4 · Canada = 5 · United States = 6. המדינה החלשה ביותר קיבלה מקום 1. זו התקלה הנפוצה ביותר בסעיף הזה.
מיון ותיקו: ROWNUMBER מחויבת למספר ייחודי. בדאטה שלנו אין שתי מדינות עם אותו סכום, ולכן מפתח מיון אחד מספיק. אם יופיע תיקו מדויק — המנוע לא יוכל להכריע מי 3 ומי 4. RANK, לעומתה, יודעת לחלוק מקום בתיקו.
פרק VIII · פונקציות חלון
2 · PARTITIONBY — מתי הספירה מתאפסת
PARTITIONBY לוקחת שורת מסדר אחת ומפרקת אותה לליגות נפרדות — תחרות לכל קבוצה, עם מקום 1 משלה. היא לא ממיינת ולא מסננת; היא רק קובעת איפה מתאפסים.
משורת מסדר אחת לשלוש ליגות
DAX
Row In Group = ROWNUMBER ( ALL ( SalesTerritory[Country], SalesTerritory[Group] ), ORDERBY ( [Total Sales], DESC ), PARTITIONBY ( SalesTerritory[Group] ) )
Group
Country
[Total Sales]
Row In Group
Europe
United Kingdom
$7,670,721
1
Europe
France
$7,251,556
2
Europe
Germany
$4,878,300
3
North America
United States
$62,997,591
1
North America
Canada
$16,355,770
2
Pacific
Australia
$10,655,336
1
01
העמודה המחלקת חייבת להיות ב-ALL
SalesTerritory[Group] חייבת להופיע גם בתוך ה-ALL — היא חלק מרשימת המתמודדים.
02
הוויזואל חייב להציג את שתי הרמות
גם Group וגם Country בשורות. אחרת המנוע לא מזהה "אני" יחיד — ומחזיר ריק.
אותה עוזרת עובדת על כל המשפחה — כאן על WINDOW: מצטבר שמתאפס בכל יבשת
DAX
Running Total In Group = CALCULATE ( [Total Sales], WINDOW ( 1, ABS, 0, REL, ALL ( SalesTerritory[Country], SalesTerritory[Group] ), ORDERBY ( [Total Sales], DESC ), PARTITIONBY ( SalesTerritory[Group] ) ) )
Group
Country
[Total Sales]
Running Total In Group
Europe
United Kingdom
$7,670,721
$7,670,721
Europe
France
$7,251,556
$14,922,277
Europe
Germany
$4,878,300
$19,800,577
North America
United States
$62,997,591
$62,997,591
North America
Canada
$16,355,770
$79,353,361
Pacific
Australia
$10,655,336
$10,655,336
✓בדיקת שפיות: השורה האחרונה של כל ליגה שווה לסך הליגה כולה — אירופה: $19,800,577.
פרק VIII · פונקציות חלון
3 · ROWNUMBER — לכל אחד מספר משלו
ROWNUMBER עוברת על שורת המסדר ומדביקה לכל מתמודד סטיקר עם מספר — 1, 2, 3… ייחודי בהכרח: גם אם שני מתמודדים שווים לגמרי, כל אחד יקבל מספר משלו.
מספור לפי מכירות — המסדר מהפתיחה
DAX
Row By Sales = ROWNUMBER ( ALL ( SalesTerritory[Country] ), ORDERBY ( [Total Sales], DESC ) )
Country
[Total Sales]
Row By Sales
United States
$62,997,591
1
Canada
$16,355,770
2
Australia
$10,655,336
3
United Kingdom
$7,670,721
4
France
$7,251,556
5
Germany
$4,878,300
6
#
מה כתוב
מה זה אומר
1
ALL ( SalesTerritory[Country] )
המתמודדים: כל המדינות. ה-ALL מוחקת את סינון השורה — בלעדיה, בשורת Canada יש מתמודדת אחת ואין את מי למספר
2
ORDERBY ( [Total Sales], DESC )
הסדר: כסף, מהגדול לקטן. עמודת מיון אחת, כיוון כתוב במפורש
3
ROWNUMBER ( … )
התשובה: המספר הסידורי של המדינה שבשורה הנוכחית
מפה: שורות ← 1–6 · שורת Total ← ריק (אין "מדינה נוכחית" — מומלץ לכבות Totals). אם צצה שורת Corporate HQ — ערך ממד בלי מכירות — מוסיפים פילטר ויזואל Total Sales is not blank.
✓ Australia = 1 · Canada = 2 · France = 3 · Germany = 4 · United Kingdom = 5 · United States = 6. אותה פונקציה בדיוק, רק עמודת מיון אחרת — וכל המספרים משתנים. המספרים לא נקבעים על ידי הפונקציה, אלא על ידי הרשימה שהיא מקבלת.
פרק VIII · פונקציות חלון
4 · RANK — מקום, לא מספר סידורי
נשמע כמו ROWNUMBER, אבל השאלה אחרת: לא "מה המספר שלי בתור" אלא "איזה מקום תפסתי בתחרות". ההבדל מתגלה בתיקו.
דירוג המכירות הבסיסי
DAX
Country Rank = RANK ( ALL ( SalesTerritory[Country] ), ORDERBY ( [Total Sales], DESC ) )
Country
[Total Sales]
Country Rank
United States
$62,997,591
1
Canada
$16,355,770
2
Australia
$10,655,336
3
United Kingdom
$7,670,721
4
France
$7,251,556
5
Germany
$4,878,300
6
כך מלמדים את זה: שימו את עמודת הכסף ליד עמודת הדירוג — רואים בעיניים שהמספר הגדול ביותר מקבל 1. סננו עם סלייסר לשנה מסוימת: עמודת הכסף משתנה, והדירוג מסתדר מחדש לבד — כי ה-ORDERBY ממיין לפי מדד, והמדד חי בבועה.
ומה קורה בתיקו
פרמטר ראשון (אופציונלי)
התנהגות
SKIP — ברירת המחדל
שתי מדינות במקום 2 ← הבאה אחריהן תקבל 4. מדלגים על מקום
DENSE
שתי מדינות במקום 2 ← הבאה אחריהן תקבל 3. בלי חורים
בדאטה שלנו אין תיקו, אז שתי הגרסאות זהות — אבל בדירוג מוצרים לפי כמויות זה קורה כל הזמן.
דירוג בתוך קבוצה — חמשת אזורי ארה"ב
DAX
Region Rank in Country = RANK ( ALL ( SalesTerritory[Region], SalesTerritory[Country] ), ORDERBY ( [Total Sales], DESC ), PARTITIONBY ( SalesTerritory[Country] ) )
Country
Region
[Total Sales]
Rank
United States
Southwest
$24,184,610
1
United States
Northwest
$16,084,943
2
United States
Central
$7,909,009
3
United States
Southeast
$7,879,655
4
United States
Northeast
$6,939,374
5
✓ שאר המדינות — אזור יחיד, תמיד 1. שימו לב כמה Central ו-Southeast צמודים — $29K הפרש בלבד. מועמדים טבעיים לתיקו בשנה בודדת.
פרק VIII · פונקציות חלון
RANK מול RANKX — שני עולמות
RANKX הוותיקה (2015) ו-RANK החדשה (2022) עונות על אותה שאלה — "איזה מקום תפסתי" — במנגנונים הפוכים. על המודל שלנו שתיהן מחזירות דירוג זהה, אבל ההתנהגות בקצוות שונה לגמרי.
היבט
RANKX — האיטרטור הוותיק
RANK — פונקציית החלון
המנגנון
משווה ערכים: מחשבת את המדד שלי, מחשבת לכל מתמודד, סופרת כמה גדולים ממני
מזהה שורה: בונה רשימה ממוינת ומאתרת בה את "אני"
כיוון ברירת מחדל
DESC — הגדול ראשון, כמו שמצפים מדירוג
ORDERBY ברירת מחדל ASC — חובה לכתוב DESC בעצמכם
תיקו
פרמטר חמישי: SKIP / DENSE
פרמטר ראשון: SKIP / DENSE
ליגות
אין — עוקפים בטבלאות מסוננות מסורבלות
מובנה: PARTITIONBY
רמת פירוט
עובד בכל רמה — קארד, Total, היררכיה
כלל הריק: עונה רק ברמת הרשימה. קארד ← ריק
בשורת Total
מציג 1 — הסך הכללי מנצח את כולם
ריק
התקלה הקלאסית
SUM גלמי במקום [מדד] ← אין מעבר הקשר ← כולם מקום 2
שם טבלה שגוי או כפול ← עמודה ריקה
מתי לבחור
דירוג מהיר וחסין-תקלות בכל ויזואל
ליגות, שליטה מלאה בסדר ובשוברי-שוויון, עבודה לצד שאר המשפחה
שני הסינטקסים המינימליים, זה מול זה
DAX
Rank Old = RANKX ( ALL ( SalesTerritory[Country] ), [Total Sales] )
Rank New = RANK ( ALL ( SalesTerritory[Country] ), ORDERBY ( [Total Sales], DESC ) )
המשפט לכיתה: RANKX שואלת "כמה מוכרים יותר ממני?" — חשבון על ערכים, עובד בכל מקום. RANK שואלת "איפה אני עומד בשורה?" — זיהוי מיקום, שדורש שורה מזוהה. אותו דירוג, פילוסופיה הפוכה — ולכן גם התקלות הפוכות.
פרק VIII · פונקציות חלון
5 · INDEX — מי עומד במקום N
עד עכשיו כל שורה שאלה על עצמה. INDEX הופכת את הכיוון: קובעים מקום — 1 לראשון, -1 לאחרון — והיא שולפת את השורה שיושבת בו.
✓קנדה "שנייה" — אבל רבע מארה"ב. דירוג מספר לך מיקום; INDEX נותנת פרופורציה.
#
מה כתוב
מה זה אומר
1
INDEX ( 1, … )
המקום המבוקש: הראשון בשורת המסדר — ארה"ב
2
CALCULATE ( [Total Sales], … )
ההזזה: חשב את המדד בתוך השורה ששלפנו — לא בשורה שאני עומד בה
INDEX + PARTITIONBY — האלוף של הליגה שלי
DAX
Group Leader Sales = CALCULATE ( [Total Sales], INDEX ( 1, ALL ( SalesTerritory[Country], SalesTerritory[Group] ), ORDERBY ( [Total Sales], DESC ), PARTITIONBY ( SalesTerritory[Group] ) ) )
✓ כל שורה מציגה את מכירות אלוף היבשת שלה — אירופה: $7,670,721 (בריטניה) · צפון אמריקה: $62,997,591 (ארה"ב) · פסיפיק: $10,655,336 (אוסטרליה). מחליפים את ה-1 ב--1 ומקבלים את החלש: באירופה, גרמניה ($4,878,300).
איך להסביר: RANK עונה "איפה אני"; INDEX עונה "תראה לי את מי שבמקום 1". אותה שורת מסדר, כיוון השאלה מתהפך. וברגע שיש לכל שורה את ערך המוביל שלה — כל השוואת "מרחק מהאלוף" היא DIVIDE אחד.
פרק VIII · פונקציות חלון
6 · OFFSET — השכן, צעד למעלה או למטה
OFFSET זזה במרחק יחסי ממני בשורת המסדר: -1 = השורה שמעליי, +1 = שמתחתיי. זו הפונקציה של "כמה חסר לי כדי לעקוף את הבא בתור" — השוואה לשכן, לא לאלוף.
מפה: הריק בשורת ארה"ב מכוון ומתועד — לראשון אין "מעליי". זה קצה החלון, לא תקלה. אומרים את זה לכיתה לפני שמישהו שואל.
עקרון הלגו — מדד על מדד: הפער מהמדינה שמעליי
DAX
Gap To Above = [Total Sales] - [One Above Sales]
Country
Gap To Above
Canada
−$46,641,820 — תהום מארה"ב
Australia
−$5,700,434
United Kingdom
−$2,984,615
France
−$419,165 — נושפת בעורף בריטניה
Germany
−$2,373,255
✓התובנה העסקית: המרדף הצמוד בליגה נמצא על מקומות 4–5, לא בצמרת. OFFSET משווה אותי לשכן — יעד אחר לכל שורה, שנע יחד איתי. זו שאלה עסקית שונה לגמרי מ"כמה אני מהמוביל".
פרק VIII · פונקציות חלון
7 · WINDOW — קטע מהמסדר
כל מה שראינו עד עכשיו החזיר שורה אחת. WINDOW מחזירה רצועה: מנקודת התחלה עד נקודת סיום. כל נקודה מוגדרת או יחסית אליי (REL) או מוחלטת (ABS).
התחלה מוחלטת: מקום 1 — ארה"ב. לא תלוי בשורה שאני עומד בה
2
3, ABS
סיום מוחלט: מקום 3 — אוסטרליה. שתי נקודות קבועות ← חלון זהה לכל השורות ← עמודה קבועה
3
ההשוואה למצטבר
במצטבר הסיום היה 0, REL — נע איתי. מילה אחת (REL↔ABS) הופכת חלון נודד לחלון קבוע
✓ $90,008,697 הוא בדיוק ה-Running Total של אוסטרליה (מקום 3) — שני חלונות שונים שנפגשים באותו תא. אימות הדדי יפהפה.
ההיגיון של ארבעת המספרים
הצירוף
מה מקבלים
1, ABS → 0, REL
סכום מצטבר — חלון שנע איתי וגדל
1, ABS → 3, ABS
טופ 3 קבוע — חלון שעומד במקום
-1, REL → 0, REL
אני והשכן שמעליי
REL = "ממני והלאה", חלון שנע איתי · ABS = "מהמקום הקבוע", חלון שעומד במקום. משחקים בשני זוגות המספרים — ומקבלים משפחה שלמה של מדדים מאותה שורת מסדר.
פרק VIII · פונקציות חלון
סיכום המשפחה — שבע פונקציות בשורה אחת
שבע פונקציות, ארבע-עשרה דוגמאות, ורשימה אחת. הנה כל המשפחה בשורת Canada — כדי לראות את ההבדלים בתא אחד.
פונקציה
השאלה
מה יופיע בשורת Canada
ORDERBY
לפי מה מסתדרים (ASC ברירת מחדל!)
Rank Low→High = 5
PARTITIONBY
מתי הספירה מתאפסת
Running Total In Group = $79,353,361
ROWNUMBER
מה המספר הסידורי שלי
Row By Sales = 2
RANK
איזה מקום תפסתי (תיקו חולק)
Country Rank = 2
INDEX
מי עומד במקום N
Leader Sales = $62,997,591
OFFSET
מי השכן שלי
One Above = $62,997,591 · Gap = −$46.6M
WINDOW
תן לי קטע מהמסדר
Running Total = $79,353,361
שתי העוזרות במשפט: ORDERBY — לפי מה מסתדרים · PARTITIONBY — מתי מתאפסים. שתי תשובות לשתי שאלות שונות, ושתיהן חיות בתוך חמש הפונקציות הראשיות: ROWNUMBER, RANK, INDEX, OFFSET, WINDOW.
✓ כל תא בפרק אומת ב-pandas מול AdventureWorks_Sales.xlsx — 121,253 שורות, $109,809,274.
פרק IX · DAX מתקדם — 20 פונקציות
מפת הפרק — 20 פונקציות מתקדמות
עשרים פונקציות שממשיכות מאיפה ש-CALCULATE ומשפחת החלון עצרו. אין כאן פונקציות בסיס — כל אחת פותרת בעיה שאי אפשר לפתור בלעדיה.
משפחה
פונקציות
הבעיה שהן פותרות
שליטה עדינה בהקשר
KEEPFILTERS · ALLSELECTED · ALLEXCEPT
לא לדרוס את מה שהמשתמש בחר, ולחשב נתח מתוך קבוצה
קשרים בזמן ריצה
USERELATIONSHIP · CROSSFILTER · TREATAS
להפעיל קשר פסיבי, להפוך כיוון סינון, או לסנן בלי קשר פיזי בכלל
לבנות טבלאות וירטואליות, לסכם, ולחשב רק על המובילים
מודעות להקשר
ISINSCOPE · SELECTEDVALUE · USERPRINCIPALNAME
לדעת באיזו רמה אני נמצא, מה המשתמש בחר, ומי הוא בכלל
ערכים ומפתחות
COALESCE · COMBINEVALUES · CONTAINSROW · EARLIER
לטפל בריקים, לבנות מפתח משולב, ולבדוק שייכות
מבנה ונתוני עזר
PATH · PATHITEM · DATATABLE
היררכיות אב-בן וטבלאות שנכתבות ישירות בקוד
שקיפות לגבי מספרים: לא כל פונקציה כאן ניתנת להדגמה על AdventureWorks — חלקן דורשות קשר פסיבי במודל, היררכיית אב-בן, או סביבת Power BI Service. איפה שיש מספר מאומת מהקובץ — הוא מופיע. איפה שאין — מופיעים התחביר וההתנהגות בלבד, בלי להמציא תוצאה.
✓הכלל שמאחד את כל הפרק: כל אחת מהפונקציות האלה היא בסופו של דבר שינוי של ההקשר — או של רשימת המתמודדים. אם הבנתם את Filter Context מפרק VII, יש לכם את כל התשתית.
פרק IX · DAX מתקדם — 20 פונקציות
שליטה עדינה בהקשר — KEEPFILTERS · ALLSELECTED · ALLEXCEPT
שלוש הפונקציות שמחליפות את "הכול או כלום" של ALL בשליטה מדויקת: לחתוך במקום לדרוס, לכבד את בחירת המשתמש, ולנקות הכול חוץ מעמודה אחת.
KEEPFILTERS — לחתוך, לא לדרוס
ברירת המחדל של CALCULATE היא דריסה: תנאי על עמודה מפיל כל סינון קיים על אותה עמודה. KEEPFILTERS הופך את זה לחיתוך.
על הדף פעיל סלייסר Product[Color] = "Red" שמציג $21,597,891
מתי כל אחת: "כמה אחוז מכלל העסק?" ← ALL. "כמה אחוז ממה שאני מסתכל עליו עכשיו?" ← ALLSELECTED. זו הפונקציה של כל מדד "נתח מתוך הבחירה".
ALLEXCEPT — מנקה הכול חוץ מ…
מוחקת את כל הסינונים מטבלה, למעט העמודות שציינתם. בפועל: "תן לי את סך הקבוצה שאני שייך אליה".
בטבלה שבשורותיה Group וגם Country
DAX
Group Total = CALCULATE ( [Total Sales], ALLEXCEPT ( SalesTerritory, SalesTerritory[Group] ) )
% of Group = DIVIDE ( [Total Sales], [Group Total] )
Group
Country
[Total Sales]
Group Total
Europe
United Kingdom
$7,670,721
$19,800,577
Europe
France
$7,251,556
$19,800,577
Europe
Germany
$4,878,300
$19,800,577
North America
United States
$62,997,591
$79,353,361
North America
Canada
$16,355,770
$79,353,361
Pacific
Australia
$10,655,336
$10,655,336
✓ אותם מספרים בדיוק שראינו בשורה האחרונה של כל ליגה ב-Running Total In Group. שתי דרכים שונות לגמרי, אותה תוצאה — אימות הדדי.
פרק IX · DAX מתקדם — 20 פונקציות
קשרים בזמן ריצה — USERELATIONSHIP · CROSSFILTER · TREATAS
שלוש פונקציות שנוגעות בקשרים עצמם — לזמן החישוב בלבד, בלי לשנות את המודל.
USERELATIONSHIP — מפעילה קשר פסיבי
בין שתי טבלאות יכול להיות יותר מקשר אחד, אבל רק אחד פעיל. USERELATIONSHIP מפעילה את הפסיבי — בתוך המדד בלבד.
התבנית הקלאסית: אותה טבלת תאריכים, פעם לפי תאריך הזמנה ופעם לפי תאריך משלוח
DAX
Sales By Ship Date = CALCULATE ( [Total Sales], USERELATIONSHIP ( Sales[ShipDateKey], 'Date'[DateKey] ) )
לא ניתן להדגמה על המודל שלנו: ב-AdventureWorks כפי שבנינו אותו יש קשר יחיד Sales[OrderDateKey] → Date[DateKey], ואין קשר פסיבי שני. כדי לנסות — הוסיפו במודל קשר נוסף מ-ShipDateKey ואל תסמנו אותו כפעיל. שני כללים: הקשר חייב להתקיים במודל, ו-USERELATIONSHIP חייבת לשבת בתוך CALCULATE.
CROSSFILTER — משנה כיוון סינון
משנה זמנית את כיוון הסינון של קשר קיים — Single, Both, או None — רק בתוך החישוב.
DAX
Products Sold = CALCULATE ( DISTINCTCOUNT ( Product[ProductKey] ), CROSSFILTER ( Sales[ProductKey], Product[ProductKey], BOTH ) )
הכיוון הרגיל במודל הוא מהממד אל העובדה. עם BOTH הסינון זורם גם חזרה — למשל "כמה מוצרים בכלל נמכרו בשנה שנבחרה". עם NONE מנתקים את הקשר לרגע. זו האלטרנטיבה המדודה לסינון דו-כיווני גלובלי, שאותו הכרנו בשיעור 2.
TREATAS — קשר וירטואלי
מחילה ערכים של טבלה אחת כסינון על עמודה בטבלה אחרת — גם כשאין ביניהן שום קשר פיזי.
✓ בדיוק אותה תוצאה של Product[Category] IN { "Clothing", "Accessories" } מפרק VII. ההבדל מתחיל כשהרשימה מגיעה מטבלה אחרת לגמרי — טבלת יעדים, טבלת פרמטרים, או תוצאה של חישוב. אז TREATAS היא הדרך היחידה.
למה לא SUMMARIZE הישנה: SUMMARIZECOLUMNS מטפלת לבד ב-Context Transition, מקבלת מסננים כארגומנט, ומדלגת על צירופים ריקים. SUMMARIZE עם עמודות מחושבות בתוכה נחשבת היום דפוס שגוי.
ROLLUPADDISSUBTOTAL — שורות סיכום אוטומטיות
נכתבת בתוך SUMMARIZECOLUMNS ומוסיפה שורות Subtotal ו-Total, עם עמודת דגל שאומרת לכל שורה אם היא סיכום או פירוט.
DAX
SUMMARIZECOLUMNS ( ROLLUPADDISSUBTOTAL ( SalesTerritory[Group], "Is Group Total", SalesTerritory[Country], "Is Country Total" ), "Sales", [Total Sales] )
✓ בתוצאה יתווספו שורות עם Is Group Total = TRUE — שם יופיעו $19,800,577 לאירופה, $79,353,361 לצפון אמריקה ו-$10,655,336 לפסיפיק. אלה בדיוק המספרים מ-ALLEXCEPT.
פרק IX · DAX מתקדם — 20 פונקציות
פונקציות טבלה — TOPN · EXCEPT · GENERATE
שלוש פונקציות שמחזירות טבלה וירטואלית — ואז מכניסים אותה ל-CALCULATE כדי לחשב רק עליה.
TOPN — רק המובילים
סכום שלוש המדינות הגדולות
DAX
Top 3 Countries = CALCULATE ( [Total Sales], TOPN ( 3, ALL ( SalesTerritory[Country] ), [Total Sales], DESC ) )
$90,008,697שלוש המדינות הגדולותTOPN
82.0%חלקן מכלל העסק
✓ אותו מספר בדיוק שקיבלנו מ-WINDOW ( 1, ABS, 3, ABS, … ) בפרק הקודם. שתי פונקציות שונות לגמרי, אותה תשובה — זו בדיקת השפיות הטובה ביותר שיש.
שימו לב לתיקו: TOPN לא שוברת שוויון. אם המקומות 3 ו-4 שווים בדיוק — היא תחזיר ארבע שורות, לא שלוש. בדירוג מוצרים לפי כמויות זה קורה כל הזמן.
EXCEPT — מה יש כאן ואין שם
כל המדינות חוץ מארה"ב
DAX
Countries Without US = EXCEPT ( VALUES ( SalesTerritory[Country] ), { "United States" } )
✓ מחזירה טבלה של חמש שורות: Canada, Australia, United Kingdom, France, Germany. פעולה קבוצתית — מסירה גם כפילויות.
השימוש האמיתי: "אילו לקוחות קנו בשנה שעברה ולא קנו השנה?" — EXCEPT בין שתי רשימות VALUES ( Sales[CustomerKey] ) בהקשרי זמן שונים. זו תבנית ה-Churn הקלאסית.
GENERATE — Cross Join מבוקר
לכל שורה בטבלה הראשונה, המנוע מעריך מחדש את הטבלה השנייה — שיכולה להשתמש בערכי השורה הנוכחית — ומחזיר את השילוב.
DAX
Category By Year = GENERATE ( VALUES ( Product[Category] ), VALUES ( 'Date'[Fiscal Year] ) )
כאן זה Cross Join פשוט — 4 קטגוריות × 4 שנות כספים. הכוח האמיתי מתגלה כשהטבלה השנייה תלויה בראשונה, למשל "שלושת המוצרים הגדולים בכל קטגוריה" — GENERATE עם TOPN בפנים.
✓ נבחרה מדינה אחת בסלייסר ← "מכירות · Canada". נבחרו שתיים או אף אחת ← "מכירות · כל המדינות". שימו את המדד בכותרת כרטיס — והכותרת מתעדכנת לבד.
USERPRINCIPALNAME — מי מחובר
מחזירה את כתובת הדוא"ל של המשתמש המחובר. זהו הבסיס לאבטחת שורות דינמית (Dynamic RLS).
DAX
// Row-Level Security rule on a security table UserSecurity[Email] = USERPRINCIPALNAME ()
לא ניתן להדגמה ב-Desktop: ב-Power BI Desktop הפונקציה מחזירה את שם המשתמש המקומי, והערך האמיתי מתקבל רק ב-Power BI Service. בודקים אותה עם View as roles.
ארבע פונקציות קטנות שפותרות בעיות יומיומיות: ריקים, מפתחות משולבים, בדיקת שייכות, והקשר שורה מקונן.
COALESCE — הערך הראשון שאינו ריק
DAX
Sales Or Zero = COALESCE ( [Total Sales], 0 )
בטבלה שבשורותיה Country, מדינה בלי מכירות תציג ריק. עם COALESCE היא תציג 0 — וחשוב מכך, השורה תפסיק להיעלם מהוויזואל. הרבה יותר קריא מ-IF ( ISBLANK ( … ), 0, … ) כי הביטוי מחושב פעם אחת בלבד.
✓ התוצאה: United States|Southwest. המפריד מובטח לא להופיע בטעות בתוך הערכים, ולכן זה בטוח יותר משרשור עם &. השימוש הנפוץ: מפתח מורכב לאבטחת שורות, או קשר וירטואלי על שתי עמודות עם TREATAS.
הקשר ל-IN: האופרטור IN שהשתמשנו בו בפרק VII הוא בדיוק סוכר תחבירי ל-CONTAINSROW. הכוח האמיתי מתחיל כשבודקים שילוב של כמה עמודות יחד — CONTAINSROW ( Table, val1, val2 ) — משהו ש-IN לא יודע לעשות על טבלה וירטואלית.
EARLIER — הקשר שורה חיצוני
בעמודה מחושבת אפשר לפתוח הקשר שורה שני (למשל בתוך FILTER). EARLIER מאפשרת להתייחס לערך של השורה מההקשר החיצוני יותר.
עמודה מחושבת בטבלת Product: דירוג המוצר בתוך הקטגוריה שלו
איך לקרוא את זה: ה-FILTER פותח הקשר שורה חדש שרץ על כל 397 המוצרים; EARLIER מחזירה את הערכים של המוצר שעליו אנחנו עומדים בעמודה. סופרים כמה מוצרים באותה קטגוריה יקרים ממני, ומוסיפים 1.
✓הערה מקצועית: מאז שהוצגו משתנים (VAR), EARLIER כמעט לא נחוצה — כותבים VAR CurrentCat = Product[Category] ומשתמשים במשתנה. עדיין חשוב להכיר אותה, כי היא מופיעה בכל קוד DAX שנכתב לפני 2015.
פרק IX · DAX מתקדם — 20 פונקציות
מבנה ונתוני עזר — PATH · PATHITEM · DATATABLE
שתי הפונקציות האחרונות עוסקות במבנה: היררכיה שמסתתרת בטבלה שטוחה, וטבלה שנכתבת ישירות בקוד.
PATH ו-PATHITEM — היררכיית אב-בן
טבלת עובדים שטוחה עם EmployeeKey ו-ManagerKey מסתירה בתוכה עץ ארגוני. PATH פורשת אותו למחרוזת, ו-PATHITEM שולפת ממנה רמה.
לא ניתן להדגמה על המודל שלנו: ב-AdventureWorks_Sales.xlsx אין טבלת עובדים עם עמודת מנהל. הדפוס עצמו זהה בכל מודל אב-בן — עץ ארגוני, עץ חשבונות הנהלת חשבונות, או קטגוריות בתוך קטגוריות. את העמודות שנוצרות שמים בהיררכיה בוויזואל.
DATATABLE — טבלה שנכתבת בקוד
מגדירה טבלה סטטית ישירות ב-DAX, בלי שום מקור חיצוני. מושלמת לסולמות מיפוי, טבלאות פרמטרים ונתוני עזר.
טבלת יעדים לפי קטגוריה — נכתבת פעם אחת ומתחברת ל-TREATAS
הטבלה הזו לא מגיעה מ-Power Query ולא מתרעננת — היא חלק מהקוד. שימו לב שזו בדיוק החלופה ל-Enter Data שהשתמשנו בו לטבלת Measures: שם דרך הממשק, כאן דרך הקוד. היתרון: הטבלה מתועדת בקוד ואפשר להעתיק אותה בין קבצים.
✓הצירוף המנצח: DATATABLE יוצרת את טבלת היעדים, TREATAS מחברת אותה לקטגוריות בלי קשר פיזי, ו-DIVIDE מחשב אחוז עמידה ביעד. שלוש פונקציות מהפרק הזה — מדד עסקי שלם.
פרק IX · DAX מתקדם — 20 פונקציות
סיכום הפרק — 20 פונקציות בטבלה אחת
עשרים פונקציות, שורה אחת לכל אחת. זו טבלת ההתמצאות של הפרק.
#
פונקציה
מה היא עושה
מאומת על הקובץ
1
KEEPFILTERS
חיתוך במקום דריסה של סינון קיים
✓ $21,597,891
2
ALLSELECTED
מוחקת סינון שורה ומכבדת את בחירת המשתמש
✓ 86.2%
3
ALLEXCEPT
מנקה הכול חוץ מהעמודות שצוינו
✓ $19,800,577
4
USERELATIONSHIP
מפעילה קשר פסיבי בתוך החישוב
— דורש קשר פסיבי
5
CROSSFILTER
משנה כיוון סינון זמנית
— דורש בדיקה במודל
6
TREATAS
קשר וירטואלי בין טבלאות בלי קשר פיזי
✓ $3,389,671
7
EARLIER
ערך מהקשר שורה חיצוני יותר
— דפוס, הוחלף ב-VAR
8
SUMMARIZECOLUMNS
צבירה מודרנית ומאופטמת לשאילתות
✓ טבלת המדינות
9
ISINSCOPE
באיזו רמת היררכיה אני נמצא
✓ $19,800,577
10
SELECTEDVALUE
הערך היחיד שנבחר, או ברירת מחדל
— התנהגות
11
PATH · PATHITEM
פורשת היררכיית אב-בן
— אין טבלת עובדים
12
COALESCE
הערך הראשון שאינו ריק
— התנהגות
13
COMBINEVALUES
מפתח משולב עם מפריד בטוח
— התנהגות
14
CONTAINSROW
האם השילוב קיים בטבלה
✓ זהה ל-IN
15
TOPN
N השורות המובילות בלבד
✓ $90,008,697
16
GENERATE
Cross Join מבוקר בין שתי טבלאות
— דפוס
17
ROLLUPADDISSUBTOTAL
שורות סיכום אוטומטיות בשאילתה
✓ $79,353,361
18
USERPRINCIPALNAME
המשתמש המחובר — בסיס ל-RLS דינמי
— רק ב-Service
19
EXCEPT
מה יש בטבלה אחת ואין בשנייה
✓ חמש מדינות
20
DATATABLE
טבלה סטטית שנכתבת בקוד
— התנהגות
שלושת הצירופים ששווה לזכור: TOPN בתוך CALCULATE = "רק המובילים" · DATATABLE + TREATAS = טבלת יעדים בלי קשר פיזי · SUMMARIZECOLUMNS + ROLLUPADDISSUBTOTAL = דוח סיכום שלם בשאילתה אחת.
✓ כל פונקציה בפרק היא בסופו של דבר שינוי הקשר או שינוי רשימת המתמודדים. אם Filter Context ברור — אין כאן שום דבר חדש מבחינה רעיונית, רק כלים חדים יותר.
פרק X · סיכום ובדיקה עצמית
כללי זהב לכתיבת DAX
שישה הרגלים שמפרידים בין קוד שעובד לקוד שאפשר לתחזק.
01
Measure כברירת מחדל
עמודה מחושבת רק כשצריך לסנן, לקבץ או לשים על ציר. אחרת — מדד.
02
DIVIDE, לא סלאש
מונע קריסה של הוויזואל בחלוקה באפס.
03
מדדים בשכבות
מדד קורא למדד. מקום אחד לתקן, קוד נקי.
04
שמות ברורים
Total Sales, לא Measure 1. השם מופיע בוויזואל.
05
CALCULATE לסינון
לא לשכפל לוגיקה ידנית בכל מדד מחדש.
06
טבלת Measures
ריכוז כל המדדים במקום אחד — הם לא שייכים לשום טבלת נתונים.
ובנוסף: כלל הזהב מפרק I — לדחוף כל עיבוד אחורה. מה שאפשר לעשות במקור או ב-M, לא עושים ב-DAX.
פרק X · סיכום ובדיקה עצמית
מה למדנו — עשר נקודות
01
שתי שפות, שני מנועים
M מכין את הנתונים פעם אחת ברענון. DAX מחשב עליהם בכל אינטראקציה.
02
כלל הזהב
דוחפים כל עיבוד אחורה: מסד הנתונים ← Power Query ← DAX.
רוצים לסווג מוצרים לרמות מחיר. איפה עדיף לכתוב את העמודה?
הסבר: עמודה על 397 שורות משרתת דרך הקשר את כל 121,253 המכירות. פחות זיכרון, אותה תוצאה.
למה צריך SUMX כדי לחשב כמות × מחיר?
הסבר: האיטרטור מחשב את הביטוי בתוך כל שורה ורק אז מחבר את התוצאות.
ההפרש בין SUMX(Qty × Unit Price) לבין SUM(Sales Amount) יצא $527,508. מה זה?
הסבר: עמודת אחוז ההנחה הציגה 0 בכל השורות, אבל 3,282 שורות קיבלו הנחה — כולן בערוץ ה-Reseller.
אותו מדד מציג ערך שונה בכל שורה בטבלה. למה?
הסבר: זה ה-Filter Context: מיקום התא קובע אילו שורות המדד "רואה".
מה CALCULATE עושה, לפי הסדר?
הסבר: בהתנגשות — הפילטר של CALCULATE דורס את זה של הדוח על אותה עמודה.
כתבתם שני תנאים בתוך CALCULATE, מופרדים בפסיק. מה המשמעות?
הסבר: הפסיק בין תנאים הוא AND אוטומטי; אין צורך לכתוב אותו.
מה עושה ALL ( Product ) בתוך CALCULATE?
הסבר: סלייסר על טבלת Date לא יושפע. בשביל הכול מהכול יש REMOVEFILTERS().
מתי FILTER הוא חובה ולא אפשר להסתפק בתנאי בוליאני?
הסבר: המנוע מחשב את המדד מחדש לכל שורה (Context Transition). לתנאי פשוט על עמודה — בוליאני, וזה מהיר יותר.
טרם ענית על שאלות — 0 מתוך 39
מושגים ותחביר — בלי צורך בקובץ הנתונים
פרק X · סיכום ובדיקה עצמית
בנק DAX — 25 שאלות מושגים
שאלות על המושגים ועל התחביר של DAX — מה כל פונקציה מקבלת, איך היא מתנהגת, ומתי בוחרים בה. אין כאן שאלות שדורשות את קובץ הנתונים: אפשר לענות על כולן בלי לפתוח Power BI.
שמות הטבלאות והעמודות שמופיעים בשאלות לקוחים מהמודל שאיתו עבדנו בשיעור, אבל אף שאלה לא מבקשת תוצאה מספרית מהקובץ.
איזו נוסחה מחזירה את סך המכירות במודל?
הסבר: SUM מקבלת עמודה מספרית אחת בפורמט טבלה[עמודה]. התוצאה: $109,809,274.
מה מחזיר COUNTROWS ( Product )?
הסבר: טבלת המוצרים היא ממד קטן — 397 מוצרים בלבד.
מה מחזיר COUNTROWS ( 'Sales Territory' )?
הסבר: טבלת האזורים היא הממד הקטן ביותר במודל — 11 שורות.
איך מגדירים נכון את מדד הרווח?
הסבר: מדד קורא למדד — זה עיקרון השכבות. האפשרות השלישית היא הגדרה של עמודה מחושבת, לא של מדד.
למה לא כותבים [Total Profit] / [Total Sales]?
הסבר: DIVIDE מחזירה Blank במקום שגיאה — ובוויזואל זה ההבדל בין תא ריק לדוח שבור.
מה מייצג ההפרש בין SUMX ( Sales, Qty * Unit Price ) לבין SUM ( Sales[Sales Amount] )?
הסבר: ההכנסה התיאורטית פחות מה ששולם — וזה בדיוק שווי ההנחות.
למה SUM על העמודה Unit Price Discount Pct לא יגלה את ההנחות?
הסבר: הדאטה "שיקר" בעמודה אחת — ורק SUMX חשף את האמת.
באיזה ערוץ מכירה ניתנו כל ההנחות?
הסבר: אותו ערוץ שנושא את המפתח הטכני CustomerKey = -1.
let … in · פסיקים בין שלבים · each · רגישות לאותיות
למה דווקא תחביר: רוב השגיאות ב-DAX הן לא שגיאות חשיבה אלא שגיאות סדר — פרמטר במקום הלא נכון, סוגר חסר, או פסיק מיותר. השאלות כאן בנויות בדיוק על הנקודות שבהן זה קורה.
איזו כתיבה של SUM תקינה?
התחביר הנכון: SUM מקבלת ארגומנט יחיד בפורמט טבלה[עמודה]. שם הטבלה חובה.
רוצים לסכם כמות × מחיר. איזו שורה תקינה תחבירית?
התחביר הנכון: SUM מקבלת עמודה בלבד — לא ביטוי ולא שני ארגומנטים. לביטוי בתוך שורה צריך איטרטור: SUMX ( טבלה, ביטוי ).
מה הארגומנט שמקבלת COUNTROWS?
התחביר הנכון: COUNTROWS היא היחידה במשפחת הספירה שמקבלת טבלה, או ביטוי שמחזיר טבלה.
איזו כתיבה של DISTINCTCOUNT תקינה?
התחביר הנכון: עמודה אחת בלבד. לספירת שילוב של כמה עמודות צריך COUNTROWS ( SUMMARIZE ( … ) ).