On this page
מערך שיעור: Window Functions
משך: 2.5 שעות אקדמיות (113 דקות).
מטרת השיעור: הסטודנטים יבינו את ההבדל בין PARTITION BY ל-GROUP BY, ישתמשו ב-ROW_NUMBER/RANK/LAG/LEAD בביטחון, ויריצו Window Function אחת לפחות דרך ה-DbContext שלהם, לא רק ב-SQL בבידוד.
בסוף השיעור הסטודנטים צריכים: לכתוב שאילתת Window Function עם PARTITION BY+ORDER BY, להסביר את ההבדל בין ROW_NUMBER ל-RANK, להשתמש ב-LAG להשוואת שורה לקודמתה, ולהריץ שאילתה כזו דרך FromSqlRaw + endpoint אמיתי.
דגש קריטי: התחילו עם GROUP BY מוכר, והראו במפורש למה הוא לא מספיק לדרישה של “לשמור פירוט + לדעת דירוג” בו-זמנית. חדש בשיעור הזה: LINQ לא מתרגם Window Functions — זו הפעם הראשונה שסטודנטים כותבים SQL גולמי דרך FromSqlRaw, כולל .HasNoKey() למחלקת תוצאה שאינה טבלה.
מהלך השיעור
| חלק בשיעור | משך | הערות ודגשים |
|---|---|---|
| פתיחה וחיבור | 5 דקות | שאלו: “איך תדרגו תלמידים בתוך כל כיתה, בלי לאבד את הפרטים שלהם?” |
| הבעיה: GROUP BY מאבד פירוט | 10 דקות | הראו GROUP BY שנותן רק סיכום, לא דירוג פר-תלמיד |
| הרעיון: PARTITION BY + ROW_NUMBER | 20 דקות | דירוג בתוך כיתה, השוו ל-GROUP BY |
| מריצים דרך FromSqlRaw | 28 דקות | מחלקת תוצאה + .HasNoKey() + endpoint — על Grades/StudentId האמיתיים שלנו |
| RANK מול ROW_NUMBER | 15 דקות | תיקו — הדגימו את ההבדל בפועל |
| LAG/LEAD | 15 דקות | השוואה לחודש קודם, בלי self-join |
| תרגול מודרך | 20 דקות | תרגיל 1+2 |
דגשים להעברת השיעור
- ההבחנה
PARTITION BYמולGROUP BYהיא לב השיעור — הריצו שתי השאילתות זו לצד זו על אותם נתונים כדי שהסטודנטים יראו בעיניים את ההבדל בפלט (מספר שורות שונה!). FromSqlRawהוא הפעם הראשונה שסטודנטים “בורחים” מ-LINQ — הדגישו שזה לא “חזרה אחורה” לקוד גרוע, אלא פתח-מילוט מכוון למקרים שה-Framework לא תומך בהם ישירות..HasNoKey()שווה הסבר נפרד: בלעדיו EF Core מצפה ל-Primary Key ונכשל.- דוגמת ה-
RANKעם תיקו (1, 1, 3) היא מלכודת קלאסית — הדגישו אותה ותנו לסטודנטים לחזות מה יקרה לפני שאתם מריצים. - LAG/LEAD שווה להשוות בקצרה ל-self-join שהיה נדרש בלעדיהם — לא צריך להראות את ה-self-join המלא, רק לתאר בקצרה כמה זה היה מסובך יותר.
מושגים
Window Function — מחשבת ערך יחסית לחלון שורות, שומרת על פירוט.
PARTITION BY — מחלק לקבוצות בלי לאחד שורות.
ROW_NUMBER/RANK — מספור/דירוג בתוך החלון.
LAG/LEAD — גישה לשורה קודמת/הבאה.
FromSqlRaw — מריץ SQL גולמי דרך DbContext, למה ש-LINQ לא מתרגם.
Keyless Entity (.HasNoKey()) — מחלקת תוצאה שלא מגיעה מטבלה אמיתית, לכן אין לה Primary Key.
תרגיל חשיבה
שאלת פתיחה: “אתם רוצים דוח שמראה לכל עובד גם את המשכורת שלו וגם את הדירוג שלו בתוך המחלקה — למה GROUP BY Department לבד לא יכול לתת לכם את זה?”
מהלך: הובילו לתובנה: GROUP BY מייצר שורה אחת לכל מחלקה (סיכום), לא שורה לכל עובד. כדי לראות גם את הפרט (כל עובד) וגם את ההקשר הקבוצתי (הדירוג בתוך המחלקה) באותה שורה, צריך PARTITION BY — זה בדיוק מה ש-Window Functions נועדו לפתור.
מה המרצה מראה בסוף: הריצו SELECT Department, AVG(Salary) FROM Employees GROUP BY Department (סיכום בלבד, שורה למחלקה) לצד SELECT Name, Department, Salary, RANK() OVER (PARTITION BY Department ORDER BY Salary DESC) FROM Employees (כל עובד עם דירוגו) — ההבדל במספר השורות ובמידע שמוצג הוא כל הלקח.