Window Function מוסיפה עמודה בלי לכווץ שורות
GROUP BY מצמצם שורות רבות לאחת. Window function עושה משהו אחר: היא מחשבת ערך על פני קבוצת שורות קשורות, אבל שומרת כל שורת קלט בפלט. מקבלים את הפירוט שורה אחר שורה ואת הצבירה, זה לצד זה.
הצורה תמיד זהה: פונקציה, ואז OVER (...).
העמודה total_all מציגה את הסכום הכולל של כל השורות, חוזר על עצמו בכל שורה. השורות המקוריות לא נפגעות. השוו את זה ל-SELECT SUM(amount) FROM sales: אותו מספר, אבל רק שורה אחת חוזרת. Window functions נותנות את שתי התצוגות בבת אחת.
PARTITION BY: צבירה בתוך קבוצות
OVER () ריק צובר על פני כל הטבלה. הוסיפו PARTITION BY כדי לצבור בתוך קבוצות, בדומה ל-GROUP BY, אבל שוב, בלי לכווץ שורות.
כל שורה מקבלת את הסכום של האזור שלה ואת החלק שלה מהסכום הזה. עם GROUP BY רגיל הייתם מאבדים את הפירוט לכל עובד. זה היתרון המרכזי של window functions: פירוט וגם צבירה בשאילתה אחת.
דירוג: ROW_NUMBER, RANK, DENSE_RANK
משפחת הדירוג ממספרת שורות לפי ORDER BY בתוך OVER. שלושת הסוגים שונים באופן שבו הם מטפלים בתיקו.
קריאת הפלט:
ROW_NUMBER()תמיד ייחודי: תיקו נשבר באופן שרירותי. השתמשו בו כשצריך מספר יציב ושונה לכל שורה.RANK()נותן לשורות בתיקו את אותו דירוג, ואז מדלג על המספרים הבאים. אחרי שני שחקנים בתיקו במקום 1 בא דירוג 3.DENSE_RANK()גם הוא נותן תיקו, אבל לא מדלג. הדירוג הבא הוא 2.
ל"N המובילים בכל קבוצה", שלבו דירוג עם PARTITION BY וסננו בשאילתה חיצונית: WHERE לא יכולה להתייחס ל-window functions ישירות:
שני בעלי השכר הגבוה ביותר בכל אזור.
LAG ו-LEAD: להסתכל על שורות שכנות
LAG(col) מחזירה את הערך של col מהשורה הקודמת בחלון. LEAD(col) מסתכלת קדימה. שתיהן מושלמות לשאלות על שינוי לאורך זמן.
ה-yesterday של השורה הראשונה הוא NULL: אין שום דבר לפניה. אפשר לספק ערך ברירת מחדל: LAG(celsius, 1, celsius) OVER (ORDER BY day) ישתמש בערך של היום כשאין שורה קודמת.
LEAD היא תמונת הראי. שלבו את שתיהן עם PARTITION BY כדי לקבל רצפים לכל קבוצה, למשל השוואה של המכירות החודש לחודש הקודם בתוך כל אזור.
סכומים מצטברים עם מסגרות חלון
הוסיפו ORDER BY בתוך OVER, ופונקציות צבירה כמו SUM, AVG, COUNT מתחילות לחשב באופן מצטבר:
שני דברים שכדאי לשים לב אליהם:
SUM(amount) OVER (ORDER BY day)הוא סכום מצטבר. מסגרת ברירת המחדל כשכותביםORDER BYבלי מסגרת מפורשת היא "מתחילת החלון ועד השורה הנוכחית".- העמודה השנייה משתמשת במסגרת מפורשת:
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW. זה חלון נע של 3 שורות: ממוצע נע.
המודל המנטלי למסגרות: כל window function מחושבת על פני מסגרת של שורות, שמוגדרת ביחס לשורה הנוכחית. מסגרות נפוצות:
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: סכום מצטבר (ברירת המחדל המובלעת).ROWS BETWEEN N PRECEDING AND CURRENT ROW: חלון נגרר.ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING: כל המחיצה.
ROWS סופר שורות פיזיות. יש גם RANGE, שמקבץ לפי ערך: נוח כשיש תיקו בעמודת ה-ORDER BY ורוצים שיטופלו כצעד אחד.
FIRST_VALUE, LAST_VALUE, NTILE
עוד כמה window functions שכדאי להכיר:
FIRST_VALUEו-LAST_VALUEמחזירות את הערך הראשון או האחרון בתוך המסגרת. עםLAST_VALUE, שימו לב למסגרת: מסגרת ברירת המחדל מסתיימת ב-CURRENT ROW, ולכן בדרך כלל תרצוROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWINGכדי לקבל את הערך האחרון האמיתי של המחיצה.NTILE(n)מחלקת את השורות ל-nדליים שווים בערך: שימושי לרבעונים, לאחוזונים ולחלוקות בסגנון A/B.
מתן שם לחלון עם WINDOW
כשכמה עמודות חולקות את אותה פסוקית OVER (...), החזרה נהיית מייגעת. SQLite מאפשר לתת שם לחלון פעם אחת ולהשתמש בו שוב:
אותה שאילתה, פחות רעש. פסוקית WINDOW באה אחרי WHERE/GROUP BY/HAVING ולפני ORDER BY.
Window Functions מול GROUP BY
שתיהן כוללות צבירה, אבל הן עונות על שאלות שונות:
GROUP BYמצמצם. שורה אחת לכל קבוצה. השתמשו בו כשרוצים רק את הסיכום.- Window functions משמרות. כל שורת קלט שורדת, עם עמודות מחושבות נוספות לצידה.
אם אי פעם תמצאו את עצמכם עושים GROUP BY ואז מצרפים את הצבירות בחזרה לטבלה המקורית, זה סימן חזק ש-window function הייתה עושה את העבודה בשאילתה אחת.
כמה מלכודות
WHEREלא יכולה להתייחס ל-window functions. הסינון קורה לפני שהחלונות מחושבים. עטפו את השאילתה בתת שאילתה או ב-CTE וסננו ברמה החיצונית.- מסגרות מובלעות נושכות.
SUM(x) OVER (ORDER BY y)הוא סכום מצטבר כי מסגרת ברירת המחדל היאRANGE UNBOUNDED PRECEDING. אם רציתם את הסכום של כל המחיצה, כתבוOVER (PARTITION BY ...)בליORDER BY, או ציינו את המסגרת במפורש. LAST_VALUEמפתיעה את כולם בפעם הראשונה. כשמסגרת ברירת המחדל מסתיימת בשורה הנוכחית, היא מחזירה את הערך הנוכחי, ולא את האחרון במחיצה. דרסו את המסגרת.- Window functions דורשות SQLite 3.25 ומעלה (שוחרר ב-2018). בכל התקנה מודרנית באופן סביר הן קיימות, אבל חלק מהסביבות המשובצות מפגרות מאחור.
הבא בתור: עמודות מחושבות
Window functions הן חישוב בזמן השאילתה. הדף הבא מכסה חישוב בזמן האחסון: עמודות מחושבות (generated columns), שבהן הערך של העמודה מוגדר על ידי ביטוי ומתעדכן אוטומטית כשהנתונים שמתחתיו משתנים.
שאלות נפוצות
מה הן window functions ב-SQLite?
Window functions (פונקציות חלון) מחשבות ערך על פני קבוצת שורות שקשורות לשורה הנוכחית, בלי לכווץ אותן כמו ש-GROUP BY עושה. מצמידים פסוקית OVER (...) לפונקציות כמו ROW_NUMBER(), RANK(), SUM() או LAG() כדי להגדיר את החלון. כל שורת קלט נשארת בתוצאה: פשוט מקבלים עמודה מחושבת נוספת.
מה ההבדל בין RANK ל-DENSE_RANK ב-SQLite?
שתיהן נותנות דירוג לפי ORDER BY, אבל הן מטפלות בתיקו בצורה שונה. RANK() משאירה פערים אחרי תיקו: אחרי שתי שורות בתיקו בדירוג 1 בא דירוג 3. DENSE_RANK() לא: השורה הבאה מקבלת דירוג 2. השתמשו ב-DENSE_RANK() כשרוצים דירוגים רצופים, וב-RANK() כשהפער משמעותי.
איך מחשבים סכום מצטבר ב-SQLite?
השתמשו ב-SUM(column) OVER (ORDER BY ...) עם מסגרת חלון. כברירת מחדל, ORDER BY בתוך OVER משתמש במסגרת RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, שנותנת סכום מצטבר. הוסיפו PARTITION BY כדי לאפס את הסכום המצטבר לכל קבוצה.