MySQL: شرح مفصل للإجراءات المخزنة والوظائف في MySQL

آخر تحديث: 2026-08-26

الإجراءات المخزنة هي مجموعات من عبارات SQL تم تجميعها مسبقًا — تُكتب مرة واحدة، وتُستدعى عدة مرات.

يتناول هذا الدرس إنشاء واستخدام الإجراءات المخزنة والوظائف.

100%
graph TB
    A[CALL Stored Procedures] --> B[Parameter Passing IN/OUT/INOUT]
    B --> C[DECLARE Declare a variable]
    C --> D{Process Control}
    D --> E[IF / CASE Conditions]
    D --> F[WHILE / LOOP Loop]
    D --> G[Cursor CURSOR Iterate]
    E --> H[ExecuteSQL]
    F --> H
    G --> H
    H --> I[OUTParameter Return]
    I --> J[End]

1. ما ستتعلمه



2. سيناريوهات واقعية

(1) المشكلة: الكتابة المتكررة للمنطق المعقد

نقوم كل شهر بحساب إجمالي رواتب كل قسم، لكن استعلام SQL هذا طويل جدًّا ويتم تنفيذه بشكل متكرر.

(2) الحل باستخدام الإجراءات المخزنة

SQL
CREATE PROCEDURE sp_dept_salary_stats()
BEGIN
    SELECT department, SUM(salary) AS total_salary, AVG(salary) AS avg_salary
    FROM employees
    GROUP BY department;
END;

-- Call
CALL sp_dept_salary_stats();


3. إنشاء إجراء مخزّن

▶ مثال: إجراء مخزّن أساسي

SQL
-- Create a Stored Procedure
DELIMITER //
CREATE PROCEDURE sp_get_user(IN user_id INT)
BEGIN
    SELECT * FROM users WHERE id = user_id;
END //
DELIMITER ;

-- Call
CALL sp_get_user(1);
▶ جرّب الكود

▶ مثال: باستخدام معلمة OUT

SQL
DELIMITER //
CREATE PROCEDURE sp_count_users(OUT total INT)
BEGIN
    SELECT COUNT(*) INTO total FROM users;
END //
DELIMITER ;

-- Call
CALL sp_count_users(@total);
SELECT @total;
▶ جرّب الكود

▶ مثال: باستخدام معلمات INOUT

SQL
DELIMITER //
CREATE PROCEDURE sp_double(INOUT num INT)
BEGIN
    SET num = num * 2;
END //
DELIMITER ;

-- Call
SET @num = 5;
CALL sp_double(@num);
SELECT @num;  -- 10
▶ جرّب الكود

4. إعلان المتغيرات

▶ مثال: DECLARE متغير

SQL
DELIMITER //
CREATE PROCEDURE sp_variable_demo()
BEGIN
    DECLARE total INT DEFAULT 0;
    DECLARE avg_salary DECIMAL(10,2);
    DECLARE user_name VARCHAR(50);
    
    -- Assignment
    SELECT COUNT(*) INTO total FROM employees;
    SELECT AVG(salary) INTO avg_salary FROM employees;
    
    -- Using Variables
    SELECT total, avg_salary;
END //
DELIMITER ;
▶ جرّب الكود

5. التحكم في العمليات

▶ مثال: عبارة IF

SQL
DELIMITER //
CREATE PROCEDURE sp_check_salary(IN emp_id INT)
BEGIN
    DECLARE emp_salary DECIMAL(10,2);
    
    SELECT salary INTO emp_salary FROM employees WHERE id = emp_id;
    
    IF emp_salary > 10000 THEN
        SELECT 'High salary' AS level;
    ELSEIF emp_salary > 5000 THEN
        SELECT 'Medium salary' AS level;
    ELSE
        SELECT 'Low salary' AS level;
    END IF;
END //
DELIMITER ;
▶ جرّب الكود

▶ مثال: عبارة CASE

SQL
DELIMITER //
CREATE PROCEDURE sp_order_status(IN order_id INT)
BEGIN
    DECLARE order_status VARCHAR(20);
    
    SELECT status INTO order_status FROM orders WHERE id = order_id;
    
    CASE order_status
        WHEN 'pending' THEN SELECT 'Order is pending' AS message;
        WHEN 'paid' THEN SELECT 'Order is paid' AS message;
        WHEN 'shipped' THEN SELECT 'Order is shipped' AS message;
        ELSE SELECT 'Unknown status' AS message;
    END CASE;
END //
DELIMITER ;
▶ جرّب الكود

▶ مثال: حلقة WHILE

SQL
DELIMITER //
CREATE PROCEDURE sp_sum_1_to_n(IN n INT, OUT total INT)
BEGIN
    DECLARE i INT DEFAULT 1;
    SET total = 0;
    
    WHILE i <= n DO
        SET total = total + i;
        SET i = i + 1;
    END WHILE;
END //
DELIMITER ;

-- Call
CALL sp_sum_1_to_n(100, @total);
SELECT @total;  -- 5050
▶ جرّب الكود

6. المؤشر

▶ مثال: تكرار المؤشر

SQL
DELIMITER //
CREATE PROCEDURE sp_list_users()
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE user_name VARCHAR(50);
    DECLARE user_email VARCHAR(100);
    
    -- Declare cursor
    DECLARE cur_users CURSOR FOR 
        SELECT username, email FROM users;
    
    -- End of cursor handler
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    
    -- Open cursor
    OPEN cur_users;
    
    -- Iterate
    read_loop: LOOP
        FETCH cur_users INTO user_name, user_email;
        IF done THEN
            LEAVE read_loop;
        END IF;
        SELECT user_name, user_email;
    END LOOP;
    
    -- Close cursor
    CLOSE cur_users;
END //
DELIMITER ;
▶ جرّب الكود

7. الدوال المخزنة

▶ مثال: إنشاء دالة

SQL
DELIMITER //
CREATE FUNCTION fn_calc_bonus(salary DECIMAL(10,2))
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
    DECLARE bonus DECIMAL(10,2);
    
    IF salary > 10000 THEN
        SET bonus = salary * 0.2;
    ELSEIF salary > 5000 THEN
        SET bonus = salary * 0.1;
    ELSE
        SET bonus = salary * 0.05;
    END IF;
    
    RETURN bonus;
END //
DELIMITER ;

-- Using Functions
SELECT name, salary, fn_calc_bonus(salary) AS bonus FROM employees;
▶ جرّب الكود

8. إدارة الإجراءات المخزنة

▶ مثال: العرض والحذف

SQL
-- View All Stored Procedures
SHOW PROCEDURE STATUS WHERE Db = 'mydb';

-- View the CREATE statement
SHOW CREATE PROCEDURE sp_get_user;

-- Delete a Stored Procedure
DROP PROCEDURE IF EXISTS sp_get_user;

-- Delete Function
DROP FUNCTION IF EXISTS fn_calc_bonus;
▶ جرّب الكود

❓ أسئلة شائعة

س ما الفرق بين الإجراء المخزن والدالة؟
ج يتم استدعاء الإجراء المخزن باستخدام الأمر CALL ولا يُرجع أي قيمة؛ أما الدالة فيتم استدعاؤها باستخدام الأمر SELECT ويجب أن تُرجع قيمة.
س ما المقصود بـ DELIMITER؟
ج إنه علامة إنهاء الجملة. ونظرًا لأن الإجراء المخزن يحتوي على ;، فيجب استبداله بـ // لمنع انتهاء الجملة قبل الأوان.
س هل يمكن للإجراءات المخزنة تحسين الأداء؟
ج يتم تجميع الإجراءات المخزنة مسبقًا، مما يقلل من حركة مرور الشبكة. ومع ذلك، فإن مُحسِّن MySQL الحديث جيد جدًّا بالفعل، لذا فإن تحسن الأداء محدود.
س ما الفرق بين الإجراء المخزن والدالة؟
ج يتم استدعاء الإجراء باستخدام عبارة CALL وقد يحتوي على معلمات OUT دون إرجاع قيمة، بينما يجب أن تُرجع الدالة المستخدمة في عبارة SELECT قيمةً ما.
س هل يمكن أن تكون الإجراءات المخزنة عرضة لخطر حقن SQL؟
ج قد يؤدي استخدام الأمر PREPARE مع الأمر CONCAT لإنشاء عبارات SQL إلى حدوث حقن؛ أما تمرير المعلمات مباشرةً فهو آمن.

📖 ملخص


📝 تمارين

  1. سؤال أساسي (مستوى الصعوبة: ⭐): أنشئ إجراءً مخزّنًا يأخذ معرّف المستخدم كمدخل ويُرجع معلومات المستخدم.

  2. مشكلة متقدمة (درجة الصعوبة ⭐⭐): أنشئ إجراءً مخزّنًا يتضمن جملة IF تُرجع درجةً بناءً على النتيجة.

  3. مشكلة التحدي (الصعوبة: ⭐⭐⭐): استخدم مؤشرًا للتنقل عبر جدول المستخدمين، واحسب نقاط كل مستخدم وقم بتحديثها.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%