• בלוג
  • היום למדתי: למה חשוב לכתוב distinct כשסופרים ב SQL

היום למדתי: למה חשוב לכתוב distinct כשסופרים ב SQL

07/02/2020

הפוסט הבא הוא יותר תזכורת בשבילי מאשר בשבילכם, למרות שאם אתם כותבים SQL יכול להיות שגם לכם זה יבוא בהפתעה. מסתבר שהשאילתה הבאה לא עובדת טוב בכלל:

SELECT  courses.*, count(lessons.id) as lessons_count,
    count(course_labels.id) as labels_count
    FROM "courses"
    LEFT OUTER JOIN "course_labels" ON "course_labels"."course_id" = "courses"."id"
    LEFT OUTER JOIN "lessons" ON "lessons"."course_id" = "courses"."id"
    GROUP BY courses.id

סיפור הרקע הוא שיש לנו טבלת קורסים, טבלת שיעורים וטבלת תוויות. בכל קורס יש הרבה שיעורים ולכל קורס יש גישה להרבה תוויות דרך טבלת חיבור בשם course_labels. המטרה של השאילתה היא להוציא במכה אחת את כל הקורסים, יחד עם מספר התוויות שיש לכל קורס, יחד עם מספר השיעורים שיש בכל קורס.

הבעיה עם השאילתה היא שה join הכפול גורם לשכפול של כל השורות בטבלה השניה - במילים אחרות כך נראית התוצאה כשאני מוריד את ה count וה group by:

sqlite> SELECT courses.id, lessons.id as lessons_count, course_labels.id as labels_count_1 FROM "courses" LEFT OUTER JOIN "course_labels" ON "course_labels"."course_id" = "courses"."id" LEFT OUTER JOIN "lessons" ON "lessons"."course_id" = "courses"."id";
id|lessons_count|labels_count_1
1|1|6
1|2|6
1|3|6
1|5|6
2|4|
3||

ה id של ה course_label היחיד במערכת הבדיקה שלי הוא 6, והוא מופיע 4 פעמים כדי להתאים ל-4 שיעורים שיש בקורס.

הפיתרון במקרה של ספירה הוא ממש פשוט ומורכב מהמילה היחידה distinct. הנה השאילתה המתוקנת:

SELECT 
    courses.*,
    count(distinct lessons.id) as lessons_count,
    count(distinct course_labels.id) as labels_count_1
FROM "courses"
    LEFT OUTER JOIN "course_labels" ON "course_labels"."course_id" = "courses"."id"
    LEFT OUTER JOIN "lessons" ON "lessons"."course_id" = "courses"."id"
GROUP BY courses.id