מסד נתונים והקמת MySQL: טבלאות, מפתחות, Docker ו-Driver

מטרות השיעור
  • להסביר למה צריך Database ומה הבעיות של שמירה בקובץ JSON במערכת אמיתית
  • להגדיר Table, Row, Column ו-Primary Key ולזהות אותם בדוגמה
  • להכיר את טיפוסי הנתונים הבסיסיים — INT, VARCHAR, DECIMAL — ואת NOT NULL
  • לתרגם מודל Python (Class או dict) לתכנון של טבלה
  • להסביר מה זה שרת מסד נתונים, ולמה מריצים אותו בקונטיינר של Docker
  • להריץ את MySQL עם docker run, ולהפעיל אותו מחדש עם docker start
  • להתחבר למסד מ-VS Code עם התוסף Database Client ולהריץ בו SQL
  • להתקין את ה-Driver, להתחבר מ-Python, ולדעת למה הסיסמה לא נכתבת בקוד

מה בשיעור

השיעור הזה בנוי משני פרקים, באותו סדר כמו במצגת:

  1. פרק ראשון — מהו Database: טבלה, שורה, עמודה, Primary Key וטיפוסי נתונים
  2. פרק שני — הקמת MySQL: Docker, Database Client ו-Driver

פרק ראשון: מהו Database — טבלה, שורה, עמודה, Primary Key וטיפוסי נתונים

מה נלמד?

  • למה צריך Database (מסד נתונים).
  • מה זה Relational Database (מסד נתונים רלציוני).
  • Table (טבלה), Row (שורה), Column (עמודה).
  • Primary Key (מפתח ראשי).
  • Data Types (טיפוסי נתונים) בסיסיים.
מה לא נלמד השבוע (בכוונה)

השבוע לומדים רק את הבסיס הדרוש ל-CRUD: חיבור, טבלה אחת, ויצירה, קריאה, עדכון ומחיקה. לא נלמד JOIN, GROUP BY, HAVING, Sub-Queries, נרמול מתקדם, אינדקסים או Stored Procedures. את אלה תלמדו לעומק בהמשך המסלול.

למה צריך את זה?

בנושא הקבצים שמרנו נתונים בקובץ JSON. זה עבד לתוכנית אחת שרצה אצלנו. בשרת אמיתי, זה נשבר:

בעיה בקובץ JSONמה קורה
הכול או כלום בקריאהכדי למצוא סטודנט אחד מתוך מיליון, טוענים את כל הקובץ ל-RAM.
הכול או כלום בכתיבהכדי לשנות שדה אחד, כותבים את כל הקובץ מחדש.
שני משתמשים בבת אחתשתי בקשות קוראות את הקובץ, כל אחת משנה משהו, וכל אחת כותבת. השינוי של הראשונה נמחק.
קריסה באמצע כתיבההקובץ נשאר חצי כתוב, ופגום.
אין כלליםשום דבר לא מונע שני סטודנטים עם אותו אימייל, או שדה חסר.
שאילתות“כל הסטודנטים מחיפה מעל גיל 20” = לולאה בקוד שלנו, על כל הנתונים.

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

הסבר מהבסיס

מהו Database?

Database (מסד נתונים) = מערכת לשמירה מאורגנת של נתונים, שמאפשרת לשמור, למצוא, לעדכן ולמחוק אותם ביעילות ובאמינות. התוכנה שמנהלת אותו נקראת DBMS (Database Management System): MySQL, PostgreSQL, SQLite…

Relational Database — טבלאות

הסוג הנפוץ ביותר הוא מסד נתונים רלציוני: הנתונים מאורגנים בטבלאות, כמו גיליון Excel עם כללים נוקשים.

Table: students
┌────┬────────┬─────┬────────────────┐
│ id │ name   │ age │ email          │   ← columns (the structure)
├────┼────────┼─────┼────────────────┤
│  1 │ Dana   │  24 │ dana@mail.com  │   ← row = one student
│  2 │ Avi    │  31 │ avi@mail.com   │   ← row
│  3 │ Noa    │  22 │ noa@mail.com   │   ← row
└────┴────────┴─────┴────────────────┘
  ↑
  primary key
מושגמה זהב-Python (מה שכבר מכירים)
Table (טבלה)אוסף של רשומות מאותו סוגClass (התבנית) + רשימה של כל האובייקטים
Column (עמודה)שדה אחד, עם שם וטיפוס קבועיםAttribute
Row (שורה / רשומה / Record)פריט אחדאובייקט אחד / dict אחד
Primary Keyעמודה שמזהה כל שורה באופן ייחודיstudent_id
אנלוגיה: ארון תיקים במשרד

Database = הארון. Table = מגירה אחת (“סטודנטים”). Columns = הטופס המודפס שכל תיק בנוי עליו (שם, גיל, אימייל), זהה לכל התיקים. Row = תיק אחד, של סטודנט אחד. Primary Key = המספר על גב התיק: אף פעם לא חוזר, ולפיו מוציאים תיק בלי לעבור על כולם.

Primary Key — מפתח ראשי

Primary Key = עמודה (בדרך כלל id) שערכה:

  1. ייחודי — אין שתי שורות עם אותו ערך.
  2. לא ריק — לכל שורה חייב להיות ערך.
  3. לא משתנה — מזהה הוא לכל חיי השורה.

למה לא להשתמש בשם? כי יש שתי “דנה”. ובאימייל? כי אנשים מחליפים אימייל. מזהה מספרי שהמסד מייצר לבד הוא הבחירה הבטוחה. (זוכרים את Shop שייצרה מזהים עם max + 1? המסד עושה את זה בשבילנו, ובצורה בטוחה יותר.)

וזה בדיוק ה-{student_id} שבנתיב של ה-API: GET /students/7 = “תביא את השורה שה-Primary Key שלה הוא 7”.

Data Types — טיפוסי נתונים בסיסיים

לכל עמודה יש טיפוס. ב-MySQL (המסד שנשתמש בו) יש הרבה טיפוסים, אבל זה מה שנצטרך:

טיפוס SQLמה שומריםPythonדוגמה
INTמספר שלםintid, age, quantity
DECIMAL(10, 2)מספר עשרוני מדויק — 2 ספרות אחרי הנקודהDecimal (כמו float, אבל מדויק)price
VARCHAR(100)טקסט — עד 100 תוויםstrname, email
(אין BOOLEAN אמיתי)שומרים 0/1 ב-INTboolis_active
NULL“אין ערך”Noneשדה אופציונלי ריק

אילוצים (Constraints) בסיסיים

כללים שהמסד אוכף. גם אם יש באג בקוד, המסד יסרב:

אילוץמשמעות
PRIMARY KEYמזהה ייחודי של השורה.
AUTO_INCREMENTהמסד מייצר את ה-id לבד, בסדר עולה.
NOT NULLחובה למלא.
UNIQUEאין שתי שורות עם אותו ערך (למשל אימייל).

זה כמו ולידציה ב-__init__ (נושא OOP), רק שהמסד עצמו אוכף אותה: שכבת הגנה נוספת.

SQL — השפה

SQL (Structured Query Language, נקרא “סיקוול” או “אס-קיו-אל”) = השפה שבה מדברים עם מסד רלציוני. Query (שאילתה) = פקודה אחת ב-SQL. נכיר רק ארבע-חמש פקודות:

CREATE TABLE   → create the structure      (day 3)
INSERT         → C - add a row             (day 3)
SELECT         → R - read rows             (day 3)
UPDATE         → U - change rows           (day 4)
DELETE         → D - remove rows           (day 4)

תחביר

תכנון טבלה (תוצר של שלב התכנון, לפני כל קוד). כך נראה תכנון של טבלת students:

Table: students
| column | type         | constraints                 |
|--------|--------------|-----------------------------|
| id     | INT          | AUTO_INCREMENT PRIMARY KEY  |
| name   | VARCHAR(100) | NOT NULL                    |
| age    | INT          | NOT NULL                    |
| email  | VARCHAR(100) | NOT NULL UNIQUE             |

וזו פקודת ה-SQL שמייצרת אותה (נכתוב ונריץ אותה בנושא מסדי נתונים ו-MySQL, כאן רק לראות לאן מתכנסים):

CREATE TABLE students (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    age INT NOT NULL,
    email VARCHAR(100) NOT NULL UNIQUE
);

דוגמת קוד ראשונה

מ-Class של Python לתכנון טבלה. ה-Class Product מנושא OOP:

class Product:
    def __init__(self, sku, name, price, stock=0):
        ...

התרגום:

Attribute ב-Pythonעמודהטיפוסאילוציםלמה
—idINTAUTO_INCREMENT PRIMARY KEYמזהה פנימי יציב
skuskuVARCHAR(20)NOT NULL UNIQUEייחודי, חובה
namenameVARCHAR(100)NOT NULLחובה
pricepriceDECIMAL(10, 2)NOT NULLמספר עשרוני מדויק
stockstockINTNOT NULL DEFAULT 0מספר שלם, ברירת מחדל 0

פירוק הקוד שורה אחר שורה

החלטהנימוק
הוספת id למרות שיש skusku הוא מזהה עסקי שעלול להשתנות (מיתוג מחדש). id הוא מזהה טכני שלא משתנה לעולם. מקובל להחזיק את שניהם.
sku עם UNIQUEהכלל “SKU ייחודי” נאכף במסד, גם אם קוד אחר ישכח לבדוק.
price כ-DECIMAL(10, 2)מחיר עם אגורות. DECIMAL שומר את המספר בדיוק — בלי בעיות העיגול של float (0.1 + 0.2).
מה לא נכנס לטבלהמתודות (take, restock) הן התנהגות, וחיות בקוד. המסד שומר נתונים בלבד.

דוגמה נוספת

מ-dicts לטבלה, וטיפוסים “מבולגנים”. נתונים שהגיעו מקובץ JSON:

students = [
    {"name": "Dana", "age": 24, "email": "dana@mail.com", "phone": "050-1234567"},
    {"name": "Avi", "age": "31", "email": "avi@mail.com"},
    {"name": "Noa", "age": 22, "email": "dana@mail.com", "phone": None},
]

שאלות תכנון:

  1. phone מופיע רק בחלק מהשורות. ‏←‏ עמודה בלי NOT NULL. כשאין ערך, נשמר NULL (None ב-Python).
  2. age של אבי הוא "31" (מחרוזת). ‏←‏ העמודה INT. בקוד נמיר int(...) לפני השמירה (או ש-Pydantic ימיר, בנושא Students API – שרת עם מסד נתונים).
  3. לנועה ולדנה אותו אימייל. ‏←‏ עם UNIQUE על email, המסד יסרב לשמור את נועה. זה בדיוק מה שאנחנו רוצים (ונטפל בשגיאה הזו בנושא מסדי נתונים ו-MySQL).
  4. אין id. → AUTO_INCREMENT: המסד ייתן 1, 2, 3.
| column | type         | constraints                |
|--------|--------------|----------------------------|
| id     | INT          | AUTO_INCREMENT PRIMARY KEY |
| name   | VARCHAR(100) | NOT NULL                   |
| age    | INT          | NOT NULL                   |
| email  | VARCHAR(100) | NOT NULL UNIQUE            |
| phone  | VARCHAR(20)  | (nullable)                 |

למה phone הוא VARCHAR ולא INT? כי 050-1234567 מכיל מקף, ומספר טלפון שמתחיל ב-0 יאבד את האפס כ-INT. כלל: “מספר” שלא עושים עליו חשבון (טלפון, מיקוד, ת"ז) ‏←‏ VARCHAR.

טעות נפוצה

הטעות 1: Primary Key “טבעי” שעלול להשתנות או לחזור. שם, אימייל, מספר טלפון. ביום שדנה מחליפה אימייל, כל מי שהצביע עליה לפי אימייל “מאבד” אותה. id מספרי של המסד.

הטעות 2: לשמור רשימה בתוך עמודה אחת.

| id | name | grades     |
|  1 | Dana | "90,85,70" |

אי אפשר לחשב ממוצע ב-SQL, אי אפשר לחפש “כל מי שקיבל 90”, וקשה לעדכן ציון אחד. בעולם הרלציוני, ציונים הם טבלה נפרדת (grades, עם student_id). את זה תלמדו לעומק בהמשך המסלול (קשרים בין טבלאות ו-JOIN). השבוע נעבוד עם טבלה אחת ושדות פשוטים.

הטעות 3: לוותר על NOT NULL ו-UNIQUE, “כי הקוד בודק”. הקוד בודק היום. מחר מישהו יכתוב סקריפט ייבוא שעוקף את הקוד, או שיהיה באג. המסד הוא קו ההגנה האחרון.

הטעות 4: לחשוב ש-SQL “תוכנית”. SQL הוא שפה הצהרתית (Declarative): אומרים מה רוצים (“כל הסטודנטים מחיפה”), ולא איך למצוא (בלי לולאות). המסד מחליט איך לבצע ביעילות.

סיכום

  • Database פותר את בעיות קובץ ה-JSON: קריאה וכתיבה ממוקדות, הרבה משתמשים בו-זמנית, עמידות לקריסות, כללים ושאילתות.
  • מסד רלציוני = טבלאות. Table ≈ Class + כל האובייקטים. Column ≈ Attribute (עם טיפוס). Row ≈ אובייקט אחד.
  • Primary Key = מזהה ייחודי, לא ריק ולא משתנה. בדרך כלל id INT AUTO_INCREMENT PRIMARY KEY.
  • טיפוסים ב-MySQL: INT, DECIMAL, VARCHAR, ו-NULL (= None). bool נשמר כ-0/1. טלפון ומיקוד נשמרים כ-VARCHAR.
  • אילוצים: NOT NULL, UNIQUE. המסד אוכף כללים גם כשהקוד טועה.
  • SQL = שפה הצהרתית. השבוע: CREATE TABLE, INSERT, SELECT, UPDATE, DELETE, וזהו.

דוקומנטציה רשמית

פרק שני: הקמת MySQL — Docker, Database Client ו-Driver

מה נלמד?

  • מה זה שרת מסד נתונים, ולמה נעבוד עם MySQL.
  • מה זה קונטיינר, ואיך מריצים את MySQL ב-Docker — בפקודה אחת.
  • איך רואים את המסד מ-VS Code עם התוסף Database Client.
  • מה זה Driver, ואיך Python מתחבר ל-MySQL.
  • איפה שומרים את פרטי החיבור — ולמה לא בקוד.

למה צריך את זה?

לפני שכותבים INSERT אחד, צריך לענות על שאלות “צנרת”: איפה המסד רץ? איך הקוד שלנו מגיע אליו? מה פרטי החיבור? בעבודה אמיתית, הדבר הראשון שמקבלים בפרויקט חדש הוא בדיוק זה: “הנה פרטי החיבור למסד”. צריך לדעת מה לעשות איתם.

הסבר מהבסיס

מסד נתונים הוא שרת

MySQL הוא שרת מסד נתונים: תוכנה נפרדת (תהליך — Process, נושא מחשב, מערכת הפעלה ורשת!) שרצה כל הזמן ומאזינה על פורט — ב-MySQL זה פורט 3306. הקוד שלנו לא פותח קובץ — הוא מתחבר ברשת לשרת, בדיוק כמו דפדפן שמתחבר לאתר (נושא רשתות: IP + Port).

MySQL (שרת מסד נתונים)
איפהתהליך נפרד שמאזין על פורט 3306
איך מתחבריםברשת: Host, Port, משתמש, סיסמה ושם המסד
מי מתחברכמה תוכנות ושרתים — בבת אחת
איפה זה בעולםאתרים ומערכות בכל גודל. גם PostgreSQL ו-SQL Server עובדים כך

למה Docker?

להתקין MySQL ישירות על המחשב זה תהליך ארוך: אשף התקנה, סיסמאות, שירות שרץ ברקע, והגדרות ששונות בין Windows ל-Mac. ומה שעובד אצל אחד — לא תמיד עובד אצל השני.

Docker (שהתקנו בשבוע הראשון) פותר את זה: הוא מריץ תוכנות מוכנות בתוך קונטיינר.

  • Image — התבנית: “MySQL בגרסה 8.4, מוכן לעבודה”. מורידים אותו פעם אחת.
  • Container — עותק שרץ מה-Image. מבודד משאר המחשב, עם כל מה שהתוכנה צריכה.
אנלוגיה: מכולה בנמל

מכולה (Container) נראית אותו דבר בכל אונייה ובכל נמל — לא משנה מה בתוכה. כך גם קונטיינר של Docker: אותו MySQL, עם אותן הגדרות, רץ בדיוק אותו דבר אצל כל הסטודנטים, בכל מערכת הפעלה.

Database Client — המסד עם ממשק

את MySQL אפשר לראות ולתפעל בלי שורת קוד אחת, עם תוסף ל-VS Code בשם Database Client (של Weijan Chen). הוא מציג עץ של מסדים וטבלאות, פותח טבלה כמו גיליון, ומריץ SQL ומציג את התוצאה כטבלה. נשתמש בו כל השבוע כדי לראות מה הקוד שלנו עשה במסד.

Driver — המתורגמן

הקוד שלנו כתוב ב-Python. MySQL מדבר ב"פרוטוקול" משלו (נושא רשתות: פרוטוקול = כללי תקשורת). צריך מתווך שמתרגם: Driver (נקרא גם Database Connector) = ספרייה שיודעת:

  1. להתחבר לשרת מסד מסוג מסוים.
  2. לשלוח אליו פקודות SQL.
  3. להחזיר את התוצאות כנתונים של Python (tuples, int, str).

ל-MySQL נשתמש ב-Driver הרשמי: mysql-connector-python. מתקינים אותו עם pip, ובקוד כותבים import mysql.connector.

אנלוגיה: מתאם חשמל בחוץ לארץ

המכשיר שלכם (קוד Python) עם תקע ישראלי, והשקע בקיר (המסד) אמריקאי. Driver = המתאם. לכל סוג שקע מתאם משלו, אבל המכשיר עצמו לא משתנה.

פרטי החיבור — ולא בקוד!

כדי להתחבר ל-MySQL צריך חמישה פרטים:

פרטאצלנומה זה
hostlocalhostאיפה השרת — המחשב שלנו
port3306הפורט של MySQL
userrootשם המשתמש
passwordkodkodהסיסמה שהגדרנו בקונטיינר
databaseschoolאיזה מסד בתוך השרת

בפרויקט אמיתי יש כאן סיסמה אמיתית. אם היא כתובה בקוד והקוד עלה ל-GitHub, היא דלפה (נושא Python!). הנוהג המקצועי:

  • פרטי החיבור נשמרים במשתני סביבה (Environment Variables), או בקובץ .env שנמצא ב-.gitignore.
  • הקוד קורא אותם: os.environ.get("DB_PASSWORD").

תחביר

הרצת MySQL בקונטיינר — פקודה אחת, פעם אחת (ב-PowerShell; ב-Mac מחליפים את ` ב-\):

docker run --name kodkod-mysql `
  -e MYSQL_ROOT_PASSWORD=kodkod `
  -e MYSQL_DATABASE=school `
  -p 3306:3306 -d mysql:8.4
חלקמשמעות
docker runיוצר ומפעיל קונטיינר חדש
--name kodkod-mysqlשם לקונטיינר, כדי שנוכל לפנות אליו
-e MYSQL_ROOT_PASSWORD=kodkodמשתנה סביבה: הסיסמה של המשתמש root
-e MYSQL_DATABASE=schoolמשתנה סביבה: מסד בשם school שנוצר אוטומטית
-p 3306:3306הפורט 3306 במחשב שלנו מוביל ל-MySQL שבתוך הקונטיינר
-dרץ ברקע (detached), וה-Terminal נשאר פנוי
mysql:8.4ה-Image: MySQL בגרסה 8.4

פקודות שימושיות:

docker ps                     # which containers are running now
docker start kodkod-mysql     # start it again (after a restart of the computer)
docker stop kodkod-mysql      # stop it (the data is kept)

חיבור מ-Python:

import mysql.connector

connection = mysql.connector.connect(
    host="localhost",
    port=3306,
    user="root",
    password="kodkod",
    database="school",
)
connection.close()
  • mysql.connector.connect(...) — פותח חיבור רשת לשרת. אם השרת לא רץ — שגיאה.
  • port=3306 הוא ברירת המחדל, ואפשר לוותר עליו.

דוגמת קוד ראשונה

הקמת מסד הנתונים של השבוע, צעד אחר צעד.

1. מריצים את MySQL (Docker Desktop צריך להיות פתוח):

docker run --name kodkod-mysql `
  -e MYSQL_ROOT_PASSWORD=kodkod `
  -e MYSQL_DATABASE=school `
  -p 3306:3306 -d mysql:8.4
docker ps

בהרצה הראשונה Docker מוריד את ה-Image (כמה דקות). docker ps מראה שהקונטיינר רץ:

CONTAINER ID   IMAGE       STATUS         PORTS                    NAMES
ffaa661f5047   mysql:8.4   Up 10 seconds  0.0.0.0:3306->3306/tcp   kodkod-mysql

2. מתחברים מ-VS Code:

  1. לשונית Extensions ‏←‏ מחפשים Database Client (של Weijan Chen) ‏←‏ Install.
  2. בסרגל הצד מופיע אייקון של מסד נתונים. לוחצים עליו ‏←‏ + (Create Connection) ‏←‏ בוחרים MySQL.
  3. ממלאים: Host 127.0.0.1, Port 3306, Username root, Password kodkod, Database school → Connect.
  4. בעץ מופיע החיבור, ובתוכו המסד school — עדיין בלי טבלאות.

3. פרויקט Python:

mkdir ~\dev\kodkod6\week-4\school-db
cd ~\dev\kodkod6\week-4\school-db
git init
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install mysql-connector-python

.gitignore:

__pycache__/
.venv/
.env

setup_db.py:

import os

import mysql.connector

connection = mysql.connector.connect(
    host="localhost",
    port=3306,
    user="root",
    password=os.environ.get("DB_PASSWORD", "kodkod"),
    database="school",
)
print("Connected:", connection.is_connected())
print("Server version:", connection.server_info)
connection.close()
print("Connected after close:", connection.is_connected())

פלט:

Connected: True
Server version: 8.4.11
Connected after close: False

פירוק הקוד שורה אחר שורה

קודהסבר
import mysql.connectorה-Driver. הותקן עם pip install mysql-connector-python.
host="localhost", port=3306איפה השרת: המחשב שלנו, בפורט שפתחנו עם -p 3306:3306.
user="root"המשתמש הראשי של MySQL.
os.environ.get("DB_PASSWORD", "kodkod")הסיסמה — ממשתנה סביבה אם הוגדר, ואחרת ברירת המחדל של סביבת הלימוד.
database="school"המסד שנוצר עם -e MYSQL_DATABASE=school.
connection.is_connected()True — החיבור פתוח.
connection.server_infoגרסת שרת ה-MySQL. אצלכם המספר עשוי להיות שונה.
connection.close()סוגר את החיבור (בשיעור הבא: למה זה חשוב).
אין *.db ב-.gitignoreהנתונים יושבים בקונטיינר, לא בתיקיית הפרויקט. ל-Git נכנס רק הקוד שיוצר את המבנה.

דוגמה נוספת

הקונטיינר אחרי הפעלה מחדש של המחשב

אחרי Restart, Docker Desktop עולה — אבל הקונטיינר לא רץ. מריצים שוב docker run? לא — השם kodkod-mysql כבר תפוס:

docker: Error response from daemon: Conflict. The container name "/kodkod-mysql" is already in use by container ...

הקונטיינר קיים, רק עצור. מפעילים אותו:

docker start kodkod-mysql

כל הנתונים נשמרו — הטבלאות והשורות נשארות בקונטיינר עד שמוחקים אותו (docker rm).

כשהשרת לא רץ

אם Docker Desktop סגור או הקונטיינר עצור, החיבור מ-Python נכשל:

mysql.connector.errors.InterfaceError: 2003: Can't connect to MySQL server on 'localhost:3306' (Errno 10061: No connection could be made because the target machine actively refused it)

Can't connect = אין מי שמאזין בפורט 3306. בודקים עם docker ps, ומפעילים עם docker start kodkod-mysql.

טעות נפוצה

הטעות 1: Docker Desktop לא פתוח.

failed to connect to the docker API ... The system cannot find the file specified.

פקודות docker מדברות עם Docker Desktop. פותחים אותו, מחכים שיסיים לעלות, ומנסים שוב.

הטעות 2: הפורט תפוס.

Bind for 0.0.0.0:3306 failed: port is already allocated

כבר רץ משהו בפורט 3306 — בדרך כלל MySQL שהותקן בעבר ישירות על המחשב. אפשר להשתמש בפורט אחר במחשב שלנו: -p 3307:3306, ואז מתחברים עם port=3307.

הטעות 3: סיסמה של מסד בקוד שעולה ל-GitHub.

connection = mysql.connector.connect(host="prod-db.company.com", user="admin", password="MyRealPassword123")

גם אם מוחקים ב-Commit הבא, הסיסמה נשארת בהיסטוריה (נושא Python!). משתני סביבה / .env ב-.gitignore. (הסיסמה kodkod שלנו היא של סביבת לימוד מקומית בלבד.)

סיכום

  • MySQL = שרת מסד נתונים: תהליך שמאזין על פורט 3306, ומתחברים אליו ברשת.
  • Docker מריץ את MySQL בתוך קונטיינר — בפקודה אחת, ואצל כולם אותו דבר. docker run פעם אחת, ואחר כך docker start kodkod-mysql.
  • Database Client (תוסף ל-VS Code) = ממשק למסד: עץ טבלאות, הרצת SQL ותוצאות כטבלה.
  • Driver = ספרייה שמתרגמת בין Python למסד. ל-MySQL: mysql-connector-python (import mysql.connector).
  • חמישה פרטי חיבור: host, port, user, password, database.
  • סיסמאות לא נכתבות בקוד: משתני סביבה או .env ב-.gitignore.

דוקומנטציה רשמית

תרגול

התרגילים מבוססים רק על מה שנלמד בשיעור הזה ובשיעורים שלפניו. נסו לבד, ורק אז פתחו את הפתרון.

תרגיל 1: טבלה, שורה, עמודה

id | name | age | email
---+------+-----+---------------
 1 | Dana |  24 | dana@mail.com
 2 | Avi  |  31 | avi@mail.com
  1. כמה שורות וכמה עמודות יש בטבלה?
  2. מה מתאר כל שורה, ומה מתארת כל עמודה?
  3. איזו עמודה מתאימה להיות Primary Key, ולמה לא name?
הצג פתרון
  1. 2 שורות ו-4 עמודות.
  2. כל שורה (Row) היא סטודנט אחד — רשומה. כל עמודה (Column) היא שדה אחד, עם טיפוס קבוע, שיש לכל הסטודנטים.
  3. id — הוא ייחודי, לא משתנה אף פעם, ונוצר אוטומטית. name לא מתאים: יכולים להיות שני סטודנטים בשם דנה, ושם יכול להשתנות.

תרגיל 2: טיפוסים ואילוצים

כתבו את פקודת ה-SQL שיוצרת טבלת students עם:

  • id — מספר שלם, Primary Key, שנוצר אוטומטית;
  • name — טקסט, חובה;
  • age — מספר שלם, חובה;
  • email — טקסט, חובה וייחודי.
הצג פתרון
CREATE TABLE students (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    age INT NOT NULL,
    email VARCHAR(100) NOT NULL UNIQUE
);
  • INT / VARCHAR(100) — הטיפוס של כל עמודה: מספר שלם, וטקסט של עד 100 תווים.
  • NOT NULL — אי אפשר להשאיר ריק.
  • UNIQUE — אין שני סטודנטים עם אותו אימייל.
  • AUTO_INCREMENT — המסד בוחר את ה-id לבד.

תרגיל 3: מה המסד היה תופס?

students = [
    {"name": "Dana", "age": 24, "email": "d@x.com"},
    {"name": "Avi", "age": 24, "email": None},
    {"name": "Noa", "age": 22, "email": "d@x.com"},
]

אילו מהנתונים האלה טבלת students מהתרגיל הקודם הייתה דוחה, ובגלל איזה אילוץ? ולמה ברשימה של Python אף אחד לא עצר אותם?

הצג פתרון
  • Avi — אימייל None ‏←‏ נדחה בגלל NOT NULL.
  • Noa — אותו אימייל כמו דנה ‏←‏ נדחה בגלל UNIQUE.
  • Dana — תקינה.

ברשימה של Python אין כללים: כל ערך נכנס. במסד הנתונים הכללים מוגדרים פעם אחת, בטבלה, וחלים על כל מי שכותב אליה — מכל תוכנה.

תרגיל 4: להרים את MySQL

  1. מה עושה כל חלק בפקודה הבאה?
  2. איך בודקים שהקונטיינר רץ?
  3. איך מפעילים אותו שוב אחרי הפעלה מחדש של המחשב — בלי ליצור חדש?
docker run --name kodkod-mysql -e MYSQL_ROOT_PASSWORD=kodkod -e MYSQL_DATABASE=school -p 3306:3306 -d mysql:8.4
הצג פתרון
חלקמשמעות
docker runיוצר ומפעיל קונטיינר חדש
--name kodkod-mysqlשם לקונטיינר, כדי שנוכל לפנות אליו
-e MYSQL_ROOT_PASSWORD=kodkodהסיסמה של המשתמש root
-e MYSQL_DATABASE=schoolמסד בשם school שנוצר אוטומטית
-p 3306:3306הפורט 3306 במחשב שלנו מוביל ל-MySQL שבקונטיינר
-dרץ ברקע, וה-Terminal נשאר פנוי
mysql:8.4ה-Image: MySQL בגרסה 8.4
  1. docker ps — מציג את הקונטיינרים שרצים כרגע.
  2. docker start kodkod-mysql — מפעיל את הקונטיינר הקיים. הנתונים שלו נשמרים. docker run שוב היה נכשל, כי השם כבר תפוס.

תרגיל 5: חיבור ראשון

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

פלט צפוי:

Connected: True
Connected after close: False
הצג פתרון
import mysql.connector

connection = mysql.connector.connect(
    host="localhost",
    port=3306,
    user="root",
    password="kodkod",
    database="school",
)
print("Connected:", connection.is_connected())
connection.close()
print("Connected after close:", connection.is_connected())
Connected: True
Connected after close: False

mysql.connector.connect פותח חיבור רשת לשרת ה-MySQL שבקונטיינר. אם הקונטיינר לא רץ — מקבלים שגיאה: Can't connect to MySQL server.