מה פונקציית צבירה עושה בפועל
רוב הפונקציות ב-SQL שפגשתם עד עכשיו פועלות שורה אחר שורה: UPPER(name) רצה פעם אחת לכל שורה, ROUND(price, 2) רצה פעם אחת לכל שורה. פונקציות צבירה שונות. הן מסתכלות על קבוצה שלמה של שורות ומכווצות אותה לערך יחיד.
בנו טבלה קטנה להתנסות בה:
חמש שורות נכנסות, שורה אחת יוצאת. זה כל המודל המחשבתי: פונקציות צבירה דוחסות שורות לסיכום. בלי GROUP BY, הסיכום מכסה כל שורה בתוצאה.
COUNT: שורות מול ערכים
ל-COUNT יש שלוש צורות, וההבדל ביניהן חשוב:
COUNT(*)סופר שורות. NULL נכללים. תמיד מחזיר מספר.COUNT(column)סופר ערכים שאינם NULL בעמודה הזו.COUNT(DISTINCT column)סופר ערכים ייחודיים שאינם NULL.
חמש שורות, בשלוש מהן יש amount, שלושה לקוחות שונים. אם אי פעם תראו ש-COUNT(amount) קטן מ-COUNT(*) ותתהו למה, זו הסיבה: NULL לא נספרים.
SUM, AVG, MIN, MAX
פונקציות הצבירה החשבוניות עובדות כמו שהייתם מצפים, עם כלל שקט אחד: כולן מדלגות על NULL:
AVG היא (10 + 20 + 30) / 3 = 20.0, לא 60 / 4 = 15.0. המכנה הוא מספר הערכים שאינם NULL. אם זה לא מה שאתם רוצים, למשל אם אתם מעדיפים להתייחס לנתונים חסרים כאפס, כתבו את זה במפורש:
MIN ו-MAX עובדות גם על טקסט ותאריכים: טקסט מושווה לפי סדר מילוני, ותאריכים בפורמט הסטנדרטי מושווים כמחרוזות ISO.
SUM מול TOTAL
ל-SQLite יש פונקציית צבירה נוספת שדומה לסכום, TOTAL, שפותרת שני מטרדים של SUM:
SUMעל אפס שורות מחזירהNULL.TOTALמחזירה0.0.SUMכשכל הערכים הם NULL מחזירהNULL.TOTALמחזירה0.0.TOTALמחזירה תמיד מספר נקודה צפה, ולכן היא אף פעם לא גולשת כמו חשבון שלמים.
המחיר: TOTAL אינה חלק מהתקן, והתוצאה שהיא תמיד REAL עלולה להפתיע אם ציפיתם למספר שלם. השתמשו בה כש"אין שורות פירושו אפס" היא התשובה הנכונה לאפליקציה שלכם, והישארו עם SUM כשאתם רוצים את ההתנהגות של תקן SQL.
DISTINCT בתוך פונקציות צבירה
אפשר לשים DISTINCT בתוך כל פונקציית צבירה, לא רק ב-COUNT. הוא מסיר ערכים כפולים לפני שהצבירה רצה:
SUM(amount) מחברת את הסכום של כל שורה. SUM(DISTINCT amount) מחברת כל סכום ייחודי פעם אחת בלבד: שימושי למשהו כמו "סך סכומי החשבוניות הייחודיים", אבל לרוב זה לא מה שאתם רוצים. COUNT(DISTINCT customer) היא הנפוצה.
FILTER: צבירה של תת קבוצה
כשרוצים לצבור רק חלק מהשורות, הצעד המתבקש הוא WHERE. אבל WHERE מסנן את הכול, כך שאי אפשר לשלב באותה שאילתה "ספירת הזמנות ששולמו" ו"ספירת החזרים". FILTER פותר את זה:
כל פסוקית FILTER (WHERE ...) חלה רק על פונקציית הצבירה הזו. מעבר אחד על הטבלה, כמה פרוסות מסוכמות. לפני ש-FILTER הייתה קיימת, כתבו SUM(CASE WHEN status = 'paid' THEN amount END): אותו רעיון, יותר הקלדה.
GROUP_CONCAT: חיבור מחרוזות
GROUP_CONCAT היא יוצאת הדופן. במקום להחזיר מספר, היא משרשרת את הערכים למחרוזת אחת:
המפריד ברירת המחדל הוא פסיק. העבירו ארגומנט שני כדי להשתמש במשהו אחר. הסדר לא מובטח, אלא אם כותבים את הקריאה כ-GROUP_CONCAT(tag ORDER BY tag), וזה שימושי כשהפלט מוצג בממשק ואתם רוצים שיהיה יציב.
צבירה בלי GROUP BY
כל דוגמה עד עכשיו שהשתמשה בפונקציות צבירה בלי GROUP BY הפיקה בדיוק שורה אחת. זה הכלל: SELECT עם פונקציות צבירה ובלי GROUP BY הוא סיכום של שורה אחת לכל הטבלה (אחרי WHERE).
אפשר לשלב פונקציות צבירה בחופשיות:
מה ש_אי אפשר_ לעשות זה לשלב עמודות שאינן מצורפות עם פונקציות צבירה ולצפות לתוצאות הגיוניות:
-- מותר ב-SQLite, אבל הערך של `customer` שרירותי.
SELECT customer, SUM(amount) FROM orders;
SQLite לא תזרוק כאן שגיאה (מסדי נתונים אחרים כן), אבל היא תבחר שם של לקוח אקראי כלשהו להציג לצד הסכום. אם אתם רוצים סכום לכל לקוח, צריך GROUP BY, וזה הנושא של העמוד הבא.
הבא בתור: GROUP BY ו-HAVING
פונקציות צבירה על כל הטבלה עונות על "כמה בסך הכול". פונקציות צבירה לכל קבוצה, לכל לקוח, לכל חודש, לכל סטטוס, עונות על השאלות המעניינות יותר. GROUP BY היא הדרך לפצל את השורות לדליים לפני הצבירה, ו-HAVING היא הדרך לסנן לפי התוצאה המצורפת. זה מה שמחכה בעמוד הבא.
שאלות נפוצות
מה הן פונקציות צבירה ב-SQLite?
פונקציות צבירה מקבלות שורות רבות ומחזירות ערך סיכום יחיד. הפונקציות המובנות הן COUNT, SUM, AVG, MIN, MAX, TOTAL ו-GROUP_CONCAT. בלי GROUP BY, הן מכווצות את כל התוצאה לשורה אחת.
מה ההבדל בין SUM ל-TOTAL ב-SQLite?
שתיהן מחברות מספרים, אבל SUM מחזירה NULL כשכל הקלטים הם NULL ומשתמשת בחשבון שלמים כשאפשר (מה שעלול לגלוש). TOTAL מחזירה תמיד מספר נקודה צפה ומחזירה 0.0 כשאין שורות. השתמשו ב-TOTAL כשאתם רוצים תוצאה מספרית מובטחת, וב-SUM כשחשובה לכם ההתנהגות של תקן SQL.
איך סופרים ערכים ייחודיים ב-SQLite?
שימו DISTINCT בתוך הקריאה: COUNT(DISTINCT customer_id). כך נספרים ערכים ייחודיים שאינם NULL. COUNT(column) רגיל סופר ערכים שאינם NULL כולל כפילויות, ו-COUNT(*) סופר כל שורה בלי קשר ל-NULL.
האם פונקציות צבירה ב-SQLite מתעלמות מ-NULL?
כן: כל פונקציות הצבירה חוץ מ-COUNT(*) מדלגות על קלטים שהם NULL. AVG מחלקת במספר הערכים שאינם NULL, לא במספר השורות הכולל. COUNT(*) היא החריגה: היא סופרת שורות, לא ערכים, ולכן גם NULL נכללים.