# מערך שיעור: SQL בסיסי — Create ו-Read<no value>

**משך:** יום לימוד מלא — 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 + INSERT | 45 דק' | `run_sql.py`. הפרת אילוצים בלייב (UNIQUE, NOT NULL). צפייה בטבלה בכלי. |
| SELECT + WHERE + ORDER BY | 40 דק' | תרגול על הלוח: "כתבו שאילתה ל...". תוצאה ריקה ≠ שגיאה. `IS NULL`. |
| הפסקה | 15 דק' | |
| Query מ-Python | 50 דק' | המסלול על הלוח. `%s` ו-tuple. **הדגמת SQL Injection**. שכחת commit בכוונה. |
| הפסקה | 15 דק' | |
| cursor(dictionary=True), dict, db.py | 45 דק' | tuple מול dict, והחיבור ל-JSON של מחר. בניית `db.py` יחד, ואז `check_db.py`. |
| תרגול + Mini Project | 50 דק' | פתקים: "הוספת פריט ושליפת כל הפריטים". |
| בדוק את עצמך | 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). משפט סיום: "**קוד הוא קוד, ונתונים הם נתונים. לעולם אל תתנו לנתונים להפוך לקוד.**"
