פרויקט מסכם: הקמת מסד נתונים

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


תוכן העניינים

  1. שלב התכנון - מה לפני הקוד
  2. יצירת מסד הנתונים
  3. יצירת טבלאות
  4. יצירת קשרים בין טבלאות
  5. אינדקסים לביצועים
  6. הוספת נתונים ראשוניים
  7. ניהול משתמשים והרשאות
  8. גיבוי ושחזור
  9. בדיקות ואימות
  10. צ'קליסט וטיפים

שלב 1: תכנון מסד הנתונים

למה צריך לתכנן לפני לכתוב קוד?

תכנון נכון חוסך זמן, מונע טעויות ומבטיח שהמערכת תעבוד כמו שצריך. אל תדלגו על שלב זה!


1.1 הגדרת דרישות המערכת

שאלות שחייבים לענות עליהן:

  1. מה המטרה של מסד הנתונים?
  2. דוגמה: "לנהל חנות אינטרנט עם מוצרים, משתמשים והזמנות"

  3. אילו נתונים נצטרך לשמור?

  4. רשמו רשימה: משתמשים, מוצרים, הזמנות, כתובות, תשלומים...

  5. מי ישתמש במערכת?

  6. לקוחות, מנהלים, ספקים...

  7. אילו פעולות המערכת תצטרך לבצע?

  8. דוגמה: רישום משתמשים, הוספת מוצר לסל, ביצוע הזמנה, צפייה בהיסטוריית הזמנות...

1.2 זיהוי ישויות (Entities)

כל ישות = טבלה במסד הנתונים.

דוגמה למערכת חנות אינטרנט:

ישות מה היא מייצגת תהפוך לטבלה
Users משתמשים רשומים במערכת users
Products מוצרים למכירה products
Orders הזמנות שבוצעו orders
Order Items פריטים בתוך הזמנה order_items
Categories קטגוריות מוצרים categories
Addresses כתובות משלוח addresses

1.3 זיהוי תכונות (Attributes)

לכל ישות יש תכונות שיהפכו לעמודות בטבלה.

דוגמה: טבלת משתמשים (users)

תכונה סוג נתונים למה צריך
id INT מזהה ייחודי
name VARCHAR(100) שם המשתמש
email VARCHAR(255) כתובת אימייל (לכניסה)
password VARCHAR(255) סיסמה מוצפנת
phone VARCHAR(20) מספר טלפון
birth_date DATE תאריך לידה
is_active BOOLEAN האם החשבון פעיל
created_at TIMESTAMP מתי נרשם

1.4 שרטוט דיאגרמת קשרים (ER Diagram)

דיאגרמה פשוטה למערכת חנות:

        ┌─────────────────┐
        │     Users       │
        │─────────────────│
        │ id (PK)         │
        │ name            │
        │ email (UNIQUE)  │
        │ password        │
        │ phone           │
        │ created_at      │
        └────────┬────────┘
                 │
                 │ 1:N (משתמש אחד ← הזמנות רבות)
                 │
        ┌────────▼────────┐
        │     Orders      │
        │─────────────────│
        │ id (PK)         │
        │ user_id (FK)────┼─────┐
        │ total_amount    │     │
        │ status          │     │
        │ order_date      │     │
        └────────┬────────┘     │
                 │              │
                 │ 1:N          │
                 │              │
        ┌────────▼────────┐     │
        │  Order_Items    │     │
        │─────────────────│     │
        │ id (PK)         │     │
        │ order_id (FK)   │     │
        │ product_id (FK)─┼──┐  │
        │ quantity        │  │  │
        │ price_at_order  │  │  │
        └─────────────────┘  │  │
                             │  │
        ┌────────────────┐   │  │
        │   Products     │   │  │
        │────────────────│   │  │
        │ id (PK)        │◄──┘  │
        │ name           │      │
        │ description    │      │
        │ price          │      │
        │ stock          │      │
        │ category_id(FK)│      │
        └────────────────┘      │
                                │
              קשר מסוג 1:N ─────┘

קיצורים: - PK = Primary Key (מפתח ראשי) - FK = Foreign Key (מפתח זר)


שלב 2: יצירת מסד הנתונים

2.1 התחברות ל-MySQL

דרך 1: דרך שורת הפקודה (Terminal/CMD)

# התחברות כ-root
mysql -u root -p

דרך 2: דרך MySQL Workbench 1. פתחו MySQL Workbench 2. לחצו על החיבור (Connection) 3. הזינו סיסמה


2.2 יצירת מסד נתונים בסיסי

-- יצירת מסד נתונים חדש
CREATE DATABASE shop_db;

הסבר: - CREATE DATABASE - פקודה ליצירת מסד נתונים - shop_db - שם מסד הנתונים (אפשר לבחור כל שם)


2.3 יצירה עם בדיקה (מומלץ!)

-- יצירה רק אם המסד עדיין לא קיים
CREATE DATABASE IF NOT EXISTS shop_db;

למה זה חשוב? - מונע שגיאות אם המסד כבר קיים - שימושי בסקריפטים שרצים כמה פעמים


2.4 יצירה עם הגדרות קידוד (חובה לעברית!)

CREATE DATABASE shop_db
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;

הסבר על הקידוד:

הגדרה מה זה למה חשוב
CHARACTER SET utf8mb4 ערכת תווים תמיכה מלאה בעברית, אימוג'י ושפות אחרות
COLLATE utf8mb4_unicode_ci כללי השוואה מאפשר חיפוש ומיון נכון בעברית

⚠️ חשוב: תמיד השתמשו ב-utf8mb4 ולא ב-utf8 הישן!


2.5 שימוש במסד הנתונים

-- עבור למסד הנתונים שיצרת
USE shop_db;

מה זה עושה? - מעתה כל הפקודות שתריצו יבוצעו על shop_db


2.6 בדיקת מסדי נתונים קיימים

-- הצגת כל מסדי הנתונים
SHOW DATABASES;

-- בדיקת איזה מסד בשימוש כרגע
SELECT DATABASE();

שלב 3: יצירת טבלאות

3.1 הבנת סוגי נתונים (Data Types)

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

📝 סוגי נתונים נפוצים

מספרים שלמים:

סוג טווח שימוש
TINYINT -128 עד 127 גיל, כמות קטנה
SMALLINT -32,768 עד 32,767 מלאי מוצרים
INT -2 מיליארד עד 2 מיליארד מזהים (ID), כמויות
BIGINT מספרים ענקיים מזהים גלובליים

טיפ: הוסיפו UNSIGNED למספרים חיוביים בלבד (מכפיל את הטווח)

age TINYINT UNSIGNED  -- 0 עד 255

מספרים עשרוניים:

סוג הסבר שימוש
DECIMAL(p, s) מדויק! מחירים, כסף - תמיד להשתמש!
FLOAT קירוב מדידות מדעיות
DOUBLE קירוב מדויק יותר חישובים מתמטיים

דוגמה:

price DECIMAL(10, 2)  -- 10 ספרות, 2 אחרי הנקודה
-- יכול לשמור: 99999999.99

טקסט:

סוג גודל מקסימלי שימוש
CHAR(n) n תווים קבוע קודים קצרים (SKU, קוד מדינה)
VARCHAR(n) עד n תווים שמות, כתובות, אימיילים
TEXT 64KB תיאורים ארוכים
MEDIUMTEXT 16MB מאמרים
LONGTEXT 4GB תוכן גדול מאוד

דוגמאות:

name VARCHAR(100)        -- שם מוצר
email VARCHAR(255)       -- אימייל (תקן)
description TEXT         -- תיאור מוצר

תאריכים ושעות:

סוג פורמט שימוש
DATE YYYY-MM-DD תאריך לידה, תאריך תוקף
TIME HH:MM:SS שעת פתיחה
DATETIME YYYY-MM-DD HH:MM:SS תאריך ושעת הזמנה
TIMESTAMP אוטומטי created_at, updated_at

דוגמה:

birth_date DATE                            -- 1990-05-15
order_date DATETIME                        -- 2026-01-04 14:30:00
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP

אחרים:

סוג הסבר דוגמה
BOOLEAN TRUE/FALSE is_active BOOLEAN
ENUM רשימה קבועה status ENUM('pending', 'paid')
JSON נתוני JSON settings JSON

3.2 יצירת טבלה ראשונה - משתמשים

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    password VARCHAR(255) NOT NULL,
    phone VARCHAR(20),
    birth_date DATE,
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

הסבר שורה אחר שורה:

id INT PRIMARY KEY AUTO_INCREMENT
- INT - מספר שלם - PRIMARY KEY - מזהה ייחודי (לא יכול להיות אותו ID פעמיים) - AUTO_INCREMENT - MySQL יעלה את המספר אוטומטית (1, 2, 3...)
name VARCHAR(100) NOT NULL
- VARCHAR(100) - טקסט עד 100 תווים - NOT NULL - חובה למלא, לא יכול להיות ריק
email VARCHAR(255) UNIQUE NOT NULL
- UNIQUE - לא יכולים להיות 2 משתמשים עם אותו אימייל - NOT NULL - חובה למלא
is_active BOOLEAN DEFAULT TRUE
- DEFAULT TRUE - אם לא מציינים ערך, יהיה TRUE
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
- CURRENT_TIMESTAMP - התאריך והשעה הנוכחיים אוטומטית
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
- מתעדכן אוטומטית כל פעם ששורה משתנה

3.3 יצירת טבלת מוצרים

CREATE TABLE products (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(200) NOT NULL,
    description TEXT,
    price DECIMAL(10, 2) NOT NULL CHECK (price >= 0),
    stock INT UNSIGNED DEFAULT 0,
    sku VARCHAR(50) UNIQUE,
    is_available BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

שימו לב: - CHECK (price >= 0) - מוודא שהמחיר לא שלילי - stock INT UNSIGNED - מלאי לא יכול להיות שלילי - sku - מק"ט ייחודי למוצר


3.4 הבנת אילוצים (Constraints)

אילוצים = כללים שמבטיחים תקינות הנתונים.

אילוץ מה הוא עושה דוגמה
PRIMARY KEY מזהה ייחודי, לא ריק, אחד בלבד לטבלה id INT PRIMARY KEY
FOREIGN KEY קשר לטבלה אחרת user_id INT, FOREIGN KEY...
UNIQUE ערך ייחודי (אבל יכול להיות NULL) email VARCHAR(255) UNIQUE
NOT NULL חובה למלא name VARCHAR(100) NOT NULL
DEFAULT ערך ברירת מחדל status VARCHAR(20) DEFAULT 'active'
CHECK תנאי מותאם אישית CHECK (age >= 18)
AUTO_INCREMENT עלייה אוטומטית id INT AUTO_INCREMENT

דוגמאות מעשיות:

-- חובה למלא ולא יכול להיות ריק
email VARCHAR(255) NOT NULL

-- ייחודי - לא יכולים להיות 2 זהים
email VARCHAR(255) UNIQUE

-- שילוב: חובה למלא + ייחודי
email VARCHAR(255) UNIQUE NOT NULL

-- תנאי מותאם אישית
age TINYINT CHECK (age >= 18 AND age <= 120)

-- ערך ברירת מחדל
country VARCHAR(50) DEFAULT 'Israel'

-- עדכון אוטומטי
updated_at TIMESTAMP ON UPDATE CURRENT_TIMESTAMP

שלב 4: יצירת קשרים בין טבלאות

4.1 הבנת סוגי קשרים

קשר One-to-Many (1:N) - הנפוץ ביותר

דוגמה: משתמש אחד יכול לבצע הרבה הזמנות.

Users (1) ←→ (N) Orders
משתמש 1 → הזמנה 1, הזמנה 2, הזמנה 3

איך מיישמים: - בטבלת orders נוסיף עמודה user_id (מפתח זר)


קשר Many-to-Many (N:N)

דוגמה: הזמנה יכולה להכיל הרבה מוצרים, ומוצר יכול להיות בהרבה הזמנות.

Orders (N) ←→ (N) Products

איך מיישמים: - יוצרים טבלת קשר (Junction Table) בשם order_items - הטבלה מכילה: order_id + product_id


קשר One-to-One (1:1) - פחות נפוץ

דוגמה: כל משתמש יש לו פרופיל אחד בלבד.

Users (1) ←→ (1) Profiles

4.2 יישום קשר One-to-Many

יצירת טבלת הזמנות:

CREATE TABLE orders (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    total_amount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
    status ENUM('pending', 'paid', 'shipped', 'delivered', 'cancelled') DEFAULT 'pending',
    shipping_address TEXT,
    order_date DATETIME DEFAULT CURRENT_TIMESTAMP,

    -- יצירת הקשר לטבלת users
    FOREIGN KEY (user_id) REFERENCES users(id)
        ON DELETE RESTRICT
        ON UPDATE CASCADE
);

הסבר על המפתח הזר:

FOREIGN KEY (user_id) REFERENCES users(id)
- user_id בטבלה הזו מתייחס ל-id בטבלת users - כל הזמנה חייבת להיות משויכת למשתמש קיים

4.3 הבנת Referential Actions

מה קורה כשמוחקים או מעדכנים רשומה שקשורה לרשומות אחרות?

ON DELETE RESTRICT    -- מה קורה כשמוחקים משתמש
ON UPDATE CASCADE     -- מה קורה כשמעדכנים את ה-ID
פעולה מה קורה מתי להשתמש
RESTRICT מונע מחיקה/עדכון אם יש רשומות קשורות ברירת מחדל - הכי בטוח
CASCADE מוחק/מעדכן אוטומטית את כל הרשומות הקשורות כשרוצים מחיקה מלאה
SET NULL מציב NULL ברשומות הקשורות כשהקשר אופציונלי
NO ACTION כמו RESTRICT

דוגמאות מעשיות:

-- אם מוחקים משתמש, מחק את כל ההזמנות שלו
FOREIGN KEY (user_id) REFERENCES users(id)
    ON DELETE CASCADE

-- אם מוחקים משתמש, לא תן למחוק אם יש לו הזמנות
FOREIGN KEY (user_id) REFERENCES users(id)
    ON DELETE RESTRICT

-- אם מוחקים קטגוריה, שים NULL במוצרים
FOREIGN KEY (category_id) REFERENCES categories(id)
    ON DELETE SET NULL

4.4 יישום קשר Many-to-Many

יצירת טבלת קשר (Junction Table):

CREATE TABLE order_items (
    id INT PRIMARY KEY AUTO_INCREMENT,
    order_id INT NOT NULL,
    product_id INT NOT NULL,
    quantity INT UNSIGNED NOT NULL DEFAULT 1,
    price_at_order DECIMAL(10, 2) NOT NULL,

    -- קשר להזמנה
    FOREIGN KEY (order_id) REFERENCES orders(id)
        ON DELETE CASCADE,

    -- קשר למוצר
    FOREIGN KEY (product_id) REFERENCES products(id)
        ON DELETE RESTRICT,

    -- מניעת כפילויות - אותו מוצר באותה הזמנה
    UNIQUE KEY unique_order_product (order_id, product_id)
);

למה price_at_order? - שומרים את המחיר בזמן ההזמנה - אם המחיר במוצר משתנה, ההזמנות הישנות לא ישתנו

למה CASCADE ב-order_id? - אם מוחקים הזמנה, נמחק גם את הפריטים שלה

למה RESTRICT ב-product_id? - לא נותנים למחוק מוצר שיש לו הזמנות


4.5 דוגמה מלאה: יצירת כל הטבלאות ביחד

-- 1. טבלת משתמשים
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    password VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 2. טבלת קטגוריות
CREATE TABLE categories (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,
    description TEXT
);

-- 3. טבלת מוצרים
CREATE TABLE products (
    id INT PRIMARY KEY AUTO_INCREMENT,
    category_id INT,
    name VARCHAR(200) NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    stock INT UNSIGNED DEFAULT 0,
    FOREIGN KEY (category_id) REFERENCES categories(id)
        ON DELETE SET NULL
);

-- 4. טבלת הזמנות
CREATE TABLE orders (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    total_amount DECIMAL(10, 2) DEFAULT 0.00,
    status ENUM('pending', 'paid', 'shipped', 'delivered') DEFAULT 'pending',
    order_date DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id)
        ON DELETE RESTRICT
);

-- 5. טבלת פריטי הזמנה
CREATE TABLE order_items (
    id INT PRIMARY KEY AUTO_INCREMENT,
    order_id INT NOT NULL,
    product_id INT NOT NULL,
    quantity INT UNSIGNED NOT NULL DEFAULT 1,
    price_at_order DECIMAL(10, 2) NOT NULL,
    FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
    FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE RESTRICT
);

שלב 5: יצירת אינדקסים

5.1 מה זה אינדקס ולמה צריך אותו?

אינדקס = תוכן עניינים של הטבלה

בלי אינדקס:

חיפוש "[email protected]" בטבלה עם מיליון שורות
→ MySQL צריך לעבור על כל שורה = איטי!

עם אינדקס:

MySQL קופץ ישר לשורה הנכונה = מהיר!

אנלוגיה: - בלי אינדקס = לחפש מילה בספר ללא תוכן עניינים (עוברים עמוד אחר עמוד) - עם אינדקס = יש תוכן עניינים שמראה בדיוק באיזה עמוד המילה נמצאת


5.2 מתי ליצור אינדקס?

✅ כדאי ליצור אינדקס על:

  1. עמודות ב-WHERE

    -- אם רץ הרבה:
    SELECT * FROM users WHERE email = '[email protected]';
    -- צור אינדקס על email
  2. עמודות ב-JOIN

    -- אם רץ הרבה:
    SELECT * FROM orders o JOIN users u ON o.user_id = u.id;
    -- צור אינדקס על user_id
  3. עמודות ב-ORDER BY

    -- אם רץ הרבה:
    SELECT * FROM products ORDER BY price;
    -- צור אינדקס על price

❌ לא כדאי ליצור אינדקס על:

  1. טבלאות קטנות (פחות מ-1000 שורות)
  2. עמודות שמשתנות תדיר (כל שינוי מעדכן את האינדקס)
  3. עמודות עם ערכים זהים רבים (TRUE/FALSE)

5.3 סוגי אינדקסים

אינדקס בסיסי (Index)
-- אינדקס על עמודה אחת
CREATE INDEX idx_users_email ON users(email);

-- אינדקס על כמה עמודות (Composite Index)
CREATE INDEX idx_orders_user_date ON orders(user_id, order_date);
אינדקס ייחודי (Unique Index)
-- מבטיח ייחודיות + מאיץ חיפושים
CREATE UNIQUE INDEX idx_products_sku ON products(sku);

שימו לב: PRIMARY KEY ו-UNIQUE יוצרים אינדקס אוטומטית!


5.4 דוגמאות מעשיות

-- חיפושים לפי אימייל (נפוץ מאוד)
CREATE INDEX idx_users_email ON users(email);

-- סינון לפי סטטוס
CREATE INDEX idx_orders_status ON orders(status);

-- חיפוש הזמנות של משתמש ספציפי
CREATE INDEX idx_orders_user_id ON orders(user_id);

-- מיון לפי תאריך
CREATE INDEX idx_orders_date ON orders(order_date);

-- חיפוש במוצרים לפי שם (Full-Text Search)
CREATE FULLTEXT INDEX idx_products_name ON products(name, description);

5.5 איך לדעת אם אינדקס עוזר?

בדיקה עם EXPLAIN:

-- ראה איך MySQL מריץ את השאילתה
EXPLAIN SELECT * FROM users WHERE email = '[email protected]';

תוצאה:

| type  | possible_keys    | key              | rows |
|-------|------------------|------------------|------|
| ref   | idx_users_email  | idx_users_email  | 1    |
  • key מציג את האינדקס שבשימוש
  • rows = כמה שורות נבדקו (רוצים מספר קטן)

5.6 ניהול אינדקסים

-- הצגת כל האינדקסים בטבלה
SHOW INDEX FROM users;

-- מחיקת אינדקס
DROP INDEX idx_users_email ON users;

-- הוספת אינדקס לטבלה קיימת
ALTER TABLE products ADD INDEX idx_price (price);

שלב 6: הכנסת נתונים ראשוניים

6.1 הכנסת נתונים בסיסית

-- הכנסת משתמש אחד
INSERT INTO users (name, email, password)
VALUES ('ישראל ישראלי', '[email protected]', 'hashed_password_123');

-- הכנסת כמה משתמשים בבת אחת (יעיל יותר!)
INSERT INTO users (name, email, password) VALUES
    ('שרה כהן', '[email protected]', 'hashed_password_456'),
    ('דוד לוי', '[email protected]', 'hashed_password_789'),
    ('מיכל אברהם', '[email protected]', 'hashed_password_012');

6.2 הכנסת קטגוריות ומוצרים

-- קטגוריות
INSERT INTO categories (name, description) VALUES
    ('אלקטרוניקה', 'מוצרי חשמל ואלקטרוניקה'),
    ('ספרים', 'ספרים בעברית ובאנגלית'),
    ('ביגוד', 'בגדים לגברים ונשים');

-- מוצרים (שים לב ל-category_id)
INSERT INTO products (category_id, name, price, stock, sku) VALUES
    (1, 'לפטופ Dell XPS 13', 4500.00, 10, 'DELL-XPS-13'),
    (1, 'עכבר אלחוטי Logitech', 150.00, 50, 'LOG-MX-MASTER'),
    (1, 'מסך 27 אינץ Samsung', 1200.00, 15, 'SAM-27-4K'),
    (2, 'הארי פוטר - סט מלא', 350.00, 20, 'BOOK-HP-SET'),
    (3, 'חולצה כחולה', 99.90, 100, 'SHIRT-BLUE-M');

6.3 הכנסת הזמנות ופריטים

-- הזמנה ראשונה (משתמש 1)
INSERT INTO orders (user_id, total_amount, status)
VALUES (1, 4650.00, 'paid');

-- קבלת ה-ID של ההזמנה שיצרנו
SET @order_id = LAST_INSERT_ID();

-- הכנסת פריטים להזמנה
INSERT INTO order_items (order_id, product_id, quantity, price_at_order) VALUES
    (@order_id, 1, 1, 4500.00),  -- לפטופ
    (@order_id, 2, 1, 150.00);   -- עכבר

הסבר: - LAST_INSERT_ID() - מחזיר את ה-ID האחרון שנוסף - @order_id - משתנה שמחזיק את ה-ID


6.4 סקריפט מלא להכנסת נתונים לדוגמה

-- משתמשים
INSERT INTO users (name, email, password) VALUES
    ('ישראל ישראלי', '[email protected]', 'pass123'),
    ('שרה כהן', '[email protected]', 'pass456'),
    ('דוד לוי', '[email protected]', 'pass789');

-- קטגוריות
INSERT INTO categories (name) VALUES
    ('אלקטרוניקה'),
    ('ספרים'),
    ('ביגוד');

-- מוצרים
INSERT INTO products (category_id, name, price, stock) VALUES
    (1, 'לפטופ', 4500.00, 10),
    (1, 'עכבר', 150.00, 50),
    (2, 'ספר SQL למתחילים', 120.00, 30),
    (3, 'חולצה', 99.90, 100);

-- הזמנה לדוגמה
INSERT INTO orders (user_id, total_amount, status) VALUES (1, 4650.00, 'paid');
INSERT INTO order_items (order_id, product_id, quantity, price_at_order) VALUES
    (1, 1, 1, 4500.00),
    (1, 2, 1, 150.00);

שלב 7: ניהול משתמשים והרשאות

7.1 למה צריך משתמשים נפרדים?

סיבות אבטחה: - אפליקציה לא צריכה להתחבר כ-root (יותר מדי הרשאות = סיכון) - כל אפליקציה תתחבר עם משתמש משלה - אם מישהו פורץ לאפליקציה, הוא לא יוכל למחוק את כל המסד


7.2 יצירת משתמש חדש

-- יצירת משתמש לאפליקציה
CREATE USER 'shop_app'@'localhost' IDENTIFIED BY 'SecurePassword123!';

הסבר: - shop_app - שם המשתמש - localhost - המשתמש יכול להתחבר רק מהמחשב המקומי - IDENTIFIED BY - הסיסמה

משתמש שיכול להתחבר מכל מקום:

CREATE USER 'shop_app'@'%' IDENTIFIED BY 'SecurePassword123!';

7.3 הענקת הרשאות

הרשאות בסיסיות לאפליקציה
-- הרשאות קריאה וכתיבה על כל הטבלאות
GRANT SELECT, INSERT, UPDATE, DELETE ON shop_db.* TO 'shop_app'@'localhost';

הסבר: - SELECT - קריאת נתונים - INSERT - הוספת נתונים - UPDATE - עדכון נתונים - DELETE - מחיקת נתונים - shop_db.* - כל הטבלאות במסד shop_db


הרשאות לטבלה ספציפית
-- הרשאת קריאה בלבד לטבלת מוצרים
GRANT SELECT ON shop_db.products TO 'readonly_user'@'localhost';

-- הרשאות מלאות (כולל DROP, CREATE)
GRANT ALL PRIVILEGES ON shop_db.* TO 'admin_user'@'localhost';

הרשאות מתקדמות
-- הרשאה ליצור טבלאות
GRANT CREATE ON shop_db.* TO 'developer'@'localhost';

-- הרשאה לשנות מבנה טבלאות
GRANT ALTER ON shop_db.* TO 'developer'@'localhost';

-- הרשאה ליצור אינדקסים
GRANT INDEX ON shop_db.* TO 'developer'@'localhost';

7.4 שלילת הרשאות

-- שלילת הרשאת מחיקה
REVOKE DELETE ON shop_db.* FROM 'shop_app'@'localhost';

-- שלילת כל ההרשאות
REVOKE ALL PRIVILEGES ON shop_db.* FROM 'shop_app'@'localhost';

7.5 הפעלת השינויים

-- חובה להריץ לאחר כל שינוי בהרשאות!
FLUSH PRIVILEGES;

7.6 בדיקת הרשאות

-- הצגת כל המשתמשים
SELECT User, Host FROM mysql.user;

-- הצגת הרשאות של משתמש ספציפי
SHOW GRANTS FOR 'shop_app'@'localhost';

7.7 מחיקת משתמש

DROP USER 'shop_app'@'localhost';

7.8 דוגמה מלאה: הגדרת משתמשים למערכת

-- 1. משתמש לאפליקציה (קריאה + כתיבה בלבד)
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'AppPass123!';
GRANT SELECT, INSERT, UPDATE, DELETE ON shop_db.* TO 'app_user'@'localhost';

-- 2. משתמש לקריאה בלבד (דוחות, Analytics)
CREATE USER 'readonly_user'@'localhost' IDENTIFIED BY 'ReadOnlyPass456!';
GRANT SELECT ON shop_db.* TO 'readonly_user'@'localhost';

-- 3. משתמש למפתח (הרשאות מלאות מלבד DROP)
CREATE USER 'developer'@'localhost' IDENTIFIED BY 'DevPass789!';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX
ON shop_db.* TO 'developer'@'localhost';

-- הפעלת ההרשאות
FLUSH PRIVILEGES;

שלב 8: גיבוי ושחזור

8.1 למה חשוב לגבות?

  • מניעת אובדן נתונים במקרה של תקלה
  • שחזור אחרי טעות (למשל מחיקה בטעות)
  • העתקת המסד לסביבת פיתוח/בדיקה
  • העברה לשרת אחר

8.2 גיבוי מסד נתונים מלא (mysqldump)

דרך שורת הפקודה (CMD/Terminal):

# גיבוי של מסד נתונים אחד
mysqldump -u root -p shop_db > shop_db_backup_2026-01-04.sql

# גיבוי של כל מסדי הנתונים
mysqldump -u root -p --all-databases > all_databases_backup.sql

# גיבוי עם דחיסה (חוסך מקום)
mysqldump -u root -p shop_db | gzip > shop_db_backup.sql.gz

הסבר: - -u root - משתמש - -p - תבקש סיסמה - shop_db - שם המסד - > - שמירה לקובץ


8.3 גיבוי טבלה ספציפית

# גיבוי של טבלה אחת בלבד
mysqldump -u root -p shop_db users > users_backup.sql

# גיבוי של כמה טבלאות
mysqldump -u root -p shop_db users orders products > tables_backup.sql

8.4 גיבוי ללא נתונים (רק מבנה)

# גיבוי המבנה בלבד (ללא הנתונים)
mysqldump -u root -p --no-data shop_db > shop_db_structure.sql

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


8.5 שחזור מגיבוי

שחזור מסד נתונים מלא:

# שחזור (המסד חייב להיות קיים!)
mysql -u root -p shop_db < shop_db_backup_2026-01-04.sql

שחזור כולל יצירת המסד:

# 1. יצירת מסד חדש
mysql -u root -p -e "CREATE DATABASE shop_db_restored;"

# 2. שחזור הנתונים
mysql -u root -p shop_db_restored < shop_db_backup.sql

שחזור מקובץ דחוס:

gunzip < shop_db_backup.sql.gz | mysql -u root -p shop_db

8.6 גיבוי אוטומטי (Cron Job ב-Linux)

יצירת סקריפט גיבוי:

#!/bin/bash
# backup_db.sh

# הגדרות
DB_USER="root"
DB_PASS="your_password"
DB_NAME="shop_db"
BACKUP_DIR="/backups/mysql"
DATE=$(date +%Y-%m-%d_%H-%M-%S)

# יצירת גיבוי
mysqldump -u $DB_USER -p$DB_PASS $DB_NAME | gzip > $BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz

# מחיקת גיבויים ישנים (מעל 7 ימים)
find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete

הפעלה אוטומטית כל יום ב-2:00 בלילה:

# פתיחת עורך cron
crontab -e

# הוספת השורה:
0 2 * * * /path/to/backup_db.sh

8.7 גיבוי ב-Windows (Task Scheduler)

יצירת קובץ .bat:

@echo off
REM backup_db.bat

set DB_USER=root
set DB_PASS=your_password
set DB_NAME=shop_db
set BACKUP_DIR=C:\backups\mysql
set DATE=%date:~-4,4%-%date:~-7,2%-%date:~-10,2%

"C:\Program Files\MySQL\MySQL Server 8.0\bin\mysqldump.exe" -u %DB_USER% -p%DB_PASS% %DB_NAME% > %BACKUP_DIR%\%DB_NAME%_%DATE%.sql

לאחר מכן, צרו Task ב-Task Scheduler שירוץ את הקובץ מדי יום.


שלב 9: בדיקות ואימות

9.1 בדיקת מבנה הטבלאות

-- רשימת כל הטבלאות במסד הנתונים
SHOW TABLES;

-- מבנה מפורט של טבלה
DESCRIBE users;
-- או
SHOW COLUMNS FROM users;

-- הצגת פקודת CREATE המלאה
SHOW CREATE TABLE users;

9.2 בדיקת מפתחות זרים

-- הצגת כל המפתחות הזרים בטבלה
SELECT
    TABLE_NAME,
    COLUMN_NAME,
    CONSTRAINT_NAME,
    REFERENCED_TABLE_NAME,
    REFERENCED_COLUMN_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = 'shop_db'
AND REFERENCED_TABLE_NAME IS NOT NULL;

9.3 בדיקת אינדקסים

-- הצגת כל האינדקסים בטבלה
SHOW INDEX FROM users;

-- בדיקה אם אינדקס משמש בשאילתה
EXPLAIN SELECT * FROM users WHERE email = '[email protected]';

9.4 בדיקת קשרים בין טבלאות

-- בדיקה שההזמנות קשורות למשתמשים
SELECT
    u.name AS user_name,
    COUNT(o.id) AS total_orders
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name;

-- בדיקה שפריטי הזמנה קשורים נכון
SELECT
    o.id AS order_id,
    p.name AS product_name,
    oi.quantity,
    oi.price_at_order
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id;

9.5 בדיקת תקינות נתונים

-- בדיקה שאין מחירים שליליים
SELECT * FROM products WHERE price < 0;

-- בדיקה שאין אימיילים כפולים
SELECT email, COUNT(*)
FROM users
GROUP BY email
HAVING COUNT(*) > 1;

-- בדיקה שכל ההזמנות קשורות למשתמשים קיימים
SELECT o.*
FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE u.id IS NULL;

9.6 בדיקת ביצועים

-- כמה שורות בכל טבלה
SELECT
    TABLE_NAME,
    TABLE_ROWS
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'shop_db';

-- גודל הטבלאות
SELECT
    TABLE_NAME,
    ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS size_mb
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'shop_db'
ORDER BY (DATA_LENGTH + INDEX_LENGTH) DESC;

צ'קליסט סופי

✅ תכנון

  • הגדרתי את דרישות המערכת
  • זיהיתי את כל הישויות (טבלאות)
  • שרטטתי דיאגרמת ER
  • הגדרתי את הקשרים בין הטבלאות
  • בחרתי סוגי נתונים מתאימים לכל עמודה

✅ יצירה

  • יצרתי את מסד הנתונים עם UTF8MB4
  • יצרתי את כל הטבלאות
  • הוספתי Primary Keys לכל טבלה
  • יצרתי Foreign Keys (מפתחות זרים)
  • הוספתי אילוצים (NOT NULL, UNIQUE, CHECK)
  • הגדרתי ערכי ברירת מחדל (DEFAULT)
  • הוספתי שדות created_at ו-updated_at

✅ אופטימיזציה

  • יצרתי אינדקסים על עמודות בשימוש תכוף
  • בדקתי עם EXPLAIN ששאילתות משתמשות באינדקסים
  • הוספתי אינדקסים על מפתחות זרים

✅ נתונים

  • הוספתי נתונים ראשוניים לבדיקה
  • בדקתי שהקשרים בין הטבלאות עובדים

✅ אבטחה

  • יצרתי משתמש נפרד לאפליקציה (לא root)
  • הענקתי רק את ההרשאות הנדרשות
  • הרצתי FLUSH PRIVILEGES

✅ גיבוי ותחזוקה

  • ביצעתי גיבוי ראשוני
  • בדקתי ששחזור מגיבוי עובד
  • הגדרתי גיבוי אוטומטי

✅ בדיקות

  • בדקתי את מבנה כל הטבלאות (DESCRIBE)
  • בדקתי שהמפתחות הזרים עובדים
  • הרצתי שאילתות JOIN לבדיקת קשרים
  • בדקתי שאין נתונים לא תקינים

טיפים וכללי אצבע

כללי שמות

-- ✅ טוב
users, user_id, created_at

-- ❌ לא טוב
Users, userId, CreatedAt, tbl_users

המלצות: - שמות טבלאות ברבים באנגלית (users, products) - שמות עמודות ב-snake_case (user_id, created_at) - מפתחות זרים עם סיומת _id (user_id, product_id) - בוליאנים עם קידומת is_ (is_active, is_verified)


ערכי ברירת מחדל חשובים

-- תמיד הוסף:
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP

-- לבוליאנים:
is_active BOOLEAN DEFAULT TRUE

-- למספרים:
balance DECIMAL(10,2) DEFAULT 0.00
quantity INT DEFAULT 0

טיפים לביצועים

  1. אל תשתמשו ב-SELECT *

    -- ❌ לא טוב
    SELECT * FROM products;
    
    -- ✅ טוב
    SELECT id, name, price FROM products;
  2. השתמשו ב-LIMIT בשאילתות גדולות

    SELECT * FROM orders ORDER BY created_at DESC LIMIT 100;
  3. אינדקס על עמודות ב-WHERE ו-JOIN

    CREATE INDEX idx_orders_user_id ON orders(user_id);

טיפים לאבטחה

  1. לעולם אל תשמרו סיסמאות בטקסט פשוט!

    -- ❌ מסוכן!
    INSERT INTO users (password) VALUES ('123456');
    
    -- ✅ מוצפן
    INSERT INTO users (password) VALUES ('$2y$10$...');  -- bcrypt hash
  2. השתמשו ב-Prepared Statements באפליקציה

  3. מונע SQL Injection

  4. הגבל הרשאות משתמשים

    -- האפליקציה לא צריכה DROP או CREATE
    GRANT SELECT, INSERT, UPDATE, DELETE ON shop_db.* TO 'app_user'@'localhost';

טיפים לתחזוקה

  1. גבה באופן קבוע
  2. יומי: גיבוי מלא
  3. שבועי: גיבוי לשרת חיצוני
  4. חודשי: גיבוי ארכיון

  5. עקוב אחרי גודל הטבלאות

    SELECT
        TABLE_NAME,
        ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS size_mb
    FROM INFORMATION_SCHEMA.TABLES
    WHERE TABLE_SCHEMA = 'shop_db'
    ORDER BY size_mb DESC;
  6. נקה נתונים ישנים

    -- מחיקת הזמנות מבוטלות מעל 90 יום
    DELETE FROM orders
    WHERE status = 'cancelled'
    AND order_date < DATE_SUB(NOW(), INTERVAL 90 DAY);

סיכום

סדר פעולות ליצירת מסד נתונים:

  1. תכנון - הגדרה של ישויות, תכונות וקשרים
  2. יצירת מסד - CREATE DATABASE עם UTF8MB4
  3. יצירת טבלאות - עם סוגי נתונים ואילוצים מתאימים
  4. קשרים - הוספת Foreign Keys
  5. אינדקסים - לשיפור ביצועים
  6. נתונים - הכנסת נתונים ראשוניים
  7. משתמשים - יצירת משתמשים עם הרשאות
  8. גיבוי - ביצוע גיבוי ראשוני
  9. בדיקות - אימות שהכל עובד

זכרו: תכנון טוב בהתחלה חוסך הרבה כאב ראש בהמשך!


← רשומות מוגדרות (Views) | המשך למדריכים נוספים →

בדקו את עצמכם

נסו לענות לבד לפני שאתם פותחים את התשובה.

  1. מה השלב הראשון בהקמת מסד נתונים?

    1. CREATE TABLE מיד
    2. הכנסת נתונים
    3. תכנון: אילו ישויות יש, אילו שדות לכל אחת ומה הקשרים
    4. גיבוי
    הצגת התשובה

    תשובה ג. שינוי מבנה אחרי שיש נתונים יקר בהרבה מתכנון מראש.

  2. איזה קידוד מומלץ ל-MySQL עם עברית ואימוג'י?

    1. utf8mb4
    2. utf16
    3. ascii
    4. latin1
    הצגת התשובה

    תשובה א. utf8 הישן של MySQL לא תומך בכל התווים. utf8mb4 כן.