מסדי נתונים ו-MySQL מערך שיעור למרצה

מערך שיעור: SQL בסיסי — Create ו-Read

משך: יום לימוד מלא — 6 שעות אקדמיות (270 דקות).

מטרת השיעור: הסטודנטים יכתבו SQL בסיסי ליצירה, הוספה וקריאה, ויריצו אותו מ-Python בצורה בטוחה, עד שכבת גישה למסד (db.py) שמחזירה dicts מוכנים לשרת.

בסוף השיעור הסטודנטים צריכים:

  1. לכתוב CREATE TABLE (עם IF NOT EXISTS ואילוצים), INSERT, ו-SELECT עם WHERE ו-ORDER BY.
  2. להריץ את המסלול Python → Connection → Query → Database → Result → Python עם %s, commit, lastrowid ו-fetch, ולהסביר SQL Injection.
  3. לבנות db.py עם init_db, create_student, get_all_students ו-get_student, שמחזירות dict או list או None.

דגש קריטי: פרמטרים, תמיד. הדגמת ה-SQL Injection חייבת להיות חיה ומרשימה. זה הרגל שהסטודנטים צריכים לקחת לכל הקריירה.

לא בסקופ: JOIN, GROUP BY, HAVING, Sub-queries, אינדקסים ופונקציות צבירה מעבר ל-COUNT. אם נשאלים, “בהמשך המסלול”.


מהלך השיעור

חלק בשיעורמשךהערות ודגשים
CREATE TABLE + INSERT45 דק'run_sql.py. הפרת אילוצים בלייב (UNIQUE, NOT NULL). צפייה בטבלה בכלי.
SELECT + WHERE + ORDER BY40 דק'תרגול על הלוח: “כתבו שאילתה ל…”. תוצאה ריקה ≠ שגיאה. IS NULL.
הפסקה15 דק'
Query מ-Python50 דק'המסלול על הלוח. %s ו-tuple. הדגמת SQL Injection. שכחת commit בכוונה.
הפסקה15 דק'
cursor(dictionary=True), dict, db.py45 דק'tuple מול dict, והחיבור ל-JSON של מחר. בניית db.py יחד, ואז check_db.py.
תרגול + Mini Project50 דק'פתקים: “הוספת פריט ושליפת כל הפריטים”.
בדוק את עצמך10 דק'

דגשים להעברת השיעור

  • SQL כשפה נפרדת. להדגיש את ההבדלים מ-Python: = להשוואה, גרש בודד למחרוזות, -- להערות, <> ל"שונה". סטודנטים מתבלבלים כשעוברים בין השפות.
  • “Python → Connection → Query → Database → Result → Python” — לכתוב את המסלול על הלוח בתחילת חלק ה-Python, ולהצביע עליו בכל שורת קוד.
  • SQL Injection בלייב: חיפוש “תמים”, ואז x' OR '1'='1 שמחזיר הכול. ואז הדוגמה של login בתרגול. לשאול: “מי עוד חושב שלשרשר זה בסדר?”
  • commit: לגרום בכוונה לבאג (INSERT בלי commit, הפעלה מחדש, הנתון נעלם). הבאג השקט הזה יחזור אצל רבים.
  • (x,) — הפסיק. לכתוב על הלוח ולהזכיר שוב ושוב.
  • dict ולא tuple — להראות את ה-JSON שייצא מכל אחד (רשימה בלי שמות לעומת אובייקט עם שמות). זה ה"למה" של cursor(dictionary=True).
  • db.py לא מכיר HTTP — None ולא 404. ההפרדה הזו היא היסוד של נושא Students API – שרת עם מסד נתונים.

מושגים

מושגהסבר קצר
CREATE TABLE / IF NOT EXISTSיצירת מבנה / רק אם לא קיים.
INSERT INTO … VALUESהוספת שורה (Create).
SELECT … FROMקריאת שורות (Read).
WHEREסינון שורות לפי תנאי.
ORDER BY [DESC]מיון.
IS NULLבדיקה של “אין ערך”.
LIKEהתאמת תבנית טקסט (% = כל רצף).
IntegrityErrorהפרת אילוץ (UNIQUE / NOT NULL).
execute / executescriptפקודה אחת עם פרמטרים / קובץ SQL שלם.
Placeholder (%s)מקום לערך שעובר בנפרד.
SQL Injectionקלט שמשנה את משמעות השאילתה.
Transaction / commitקבוצת שינויים / שמירה סופית שלהם.
lastrowidה-id של השורה החדשה.
cursor(dictionary=True)Cursor שמחזיר כל שורה כ-dict, עם גישה לפי שם עמודה.
Data Access Layerשכבה אחת (db.py) שמכילה את כל ה-SQL.

תרגיל חשיבה

שאלת פתיחה (לפני חלק ה-Python): “בואו נבנה חיפוש סטודנטים לפי שם. הכי פשוט: "SELECT * FROM students WHERE name = '" + name + "'". מה יכול להשתבש?”

מהלך:

  1. לתת לכיתה לחשוב. תשובות צפויות: “שגיאת כתיב”, “אותיות גדולות”.
  2. לשאול: “מה אם השם של הסטודנט הוא O'Brien?” → הגרש שובר את השאילתה (Syntax Error). “אז הקלט יכול לשנות את המבנה של ה-SQL?”
  3. “ואם מישהו מקליד בכוונה x' OR '1'='1?” → להריץ, ולראות את כל הסטודנטים חוזרים.
  4. “ואם x'; DROP TABLE students; --?” (להסביר בלי להריץ על מסד אמיתי. execute של mysql.connector מסרב כברירת מחדל להריץ שתי פקודות, אבל במערכות אחרות זה עבד היסטורית.)
  5. להוביל לפתרון: להפריד בין הקוד (SQL) לבין הנתונים (פרמטרים).

מה המרצה מראה בסוף: את שתי הגרסאות זו לצד זו, עם O'Brien ועם ההתקפה, כשהגרסה עם %s עובדת נכון בשני המקרים. אפשר להציג את הקומיקס המפורסם של xkcd על “Little Bobby Tables” (xkcd.com/327). משפט סיום: “קוד הוא קוד, ונתונים הם נתונים. לעולם אל תתנו לנתונים להפוך לקוד.”