في هذا الدرس 6تصفّح المواضيع
الدالة المخزنة (Stored Function) في محرك MySQL 8.4 هي وحدة برمجية مخصصة تُنشأ بالأمر CREATE FUNCTION لتغليف معادلة حسابية أو تحويل نصي أو فحص منطقي وإرجاع قيمة مفردة واحدة فقط (Scalar Value) إلى الاستعلام الذي استدعاها. على خلاف الإجراء المخزن الذي يُستدعى بأمر CALL مستقل وقد يُرجع عدة جداول نتائج، تعمل الدالة المخزنة تماماً مثل الدوال المدمجة في المحرك (مثل ROUND(x, 2) أو UPPER(str)) بحيث يمكن دمجها داخل تعبيرات SELECT وWHERE وORDER BY.
يشترط تعريف الدالة المخزنة ثلاثة عناصر إلزامية: أولاً، جميع معاملاتها تكون معاملات إدخال فقط (بدون كتابة كلمة IN أو OUT)؛ ثانياً، تحديد نوع البيانات المُرجع في رأس التعريف عبر الكلمة المفتاحية RETURNS type (بـحرف S في النهاية)؛ وثالثاً، إنهاء مسار التنفيذ داخل جسم الدالة بجملة RETURN expression; التي تعيد القيمة المحسوبة الفعلية.
الدوال المخزنة والتنفيذ الحتمي DETERMINISTIC في MySQL
DELIMITER $$
CREATE FUNCTION fn_price_with_vat(p_amount DECIMAL(10,2))
RETURNS DECIMAL(10,2)
DETERMINISTIC
NO SQL
BEGIN
RETURN ROUND(p_amount * 1.15, 2);
END$$
DELIMITER ;
SELECT fn_price_with_vat(200.00) AS gross_price;
توصَف الدالة المخزنة بأنها حتمية التنفيذ (DETERMINISTIC) إذا كانت تُنتج دائماً نفس النتيجة تماماً عند تزويدها بنفس قيم المدخلات، بغض النظر عن وقت التشغيل أو حالة الخادم (مثل معادلة حساب الضريبة أو تنسيق رمز العملة). أما إذا كانت نتيجة الدالة تتغير بتغير الوقت أو تعتمد على قيم متغيرة أو قراءة بيانات تتبدل باستمرار، فتوصَف بأنها غير حتمية (NOT DETERMINISTIC).
في محرك MySQL 8.4 يكون السجل الثنائي (Binary Logging) مفعّلاً افتراضياً لأغراض النسخ المتماثل (Replication) والاستعادة الزمنية. لذلك يرفض الخادم إنشاء أي دالة مخزنة لا تصرّح صراحةً بخصائصها (مثل DETERMINISTIC أو READS SQL DATA أو NO SQL) ويعيد الخطأ الشهير Error 1418 لضمان تطابق البيانات بين الخادم الرئيسي والخوادم التابعة.
DELIMITER $$
CREATE FUNCTION fn_shipping_band(p_weight_kg DECIMAL(6,2))
RETURNS VARCHAR(20)
DETERMINISTIC
NO SQL
BEGIN
DECLARE v_band VARCHAR(20) DEFAULT 'Standard';
IF p_weight_kg > 20.00 THEN SET v_band = 'Heavy Freight';
ELSEIF p_weight_kg > 5.00 THEN SET v_band = 'Express Parcel';
END IF;
RETURN v_band;
END$$
محاكي الدوال المخزنة التفاعلي
الميزة العملية الكبرى للدالة المخزنة هي إمكانية استدعائها مباشرةً داخل أي تعبير SQL قياسي دون الحاجة للأمر CALL. يمكنك وضع اسم الدالة وتمرير أعمدة الجدول إليها داخل قائمة أعمدة SELECT لحساب عمود مشتق لكل صف، أو داخل شرط WHERE لتصفية الصفوف بناءً على نتيجة الدالة، أو حتى داخل جملة ORDER BY لترتيب النتائج.
مع ذلك، انتبه للأثر الأدائي عند استدعاء دالة مخزنة داخل شرط WHERE على عمود مفهرس (مثل WHERE fn_net_tax(amount) > 500): سيضطر محرك InnoDB لتشغيل الدالة على كل صف في الجدول (Full Table Scan) متجاهلاً الفهرس، لذلك يُفضَّل دائماً وضع الدالة في قائمة SELECT بعد تصفية الصفوف بشرط مباشر على العمود الأصلي.
-- Use stored functions directly inside SELECT and WHERE
SELECT
order_id,
amount AS net_amount,
fn_price_with_vat(amount) AS gross_amount,
fn_shipping_band(weight_kg) AS shipping_type
FROM orders
WHERE status = 'paid' AND amount >= 100.00
ORDER BY gross_amount DESC;
إلى جانب تحديد الحتمية، يتطلب تعريف الدالة في MySQL 8.4 تحديد علاقتها ببيانات الجداول عبر إحدى العبارات الأربع التالية: NO SQL عندما تخلو الدالة من أي أوامر SQL وتقتصر على الحسابات؛ وCONTAINS SQL عندما تتضمن أوامر لا تقرأ ولا تعدّل جداول البيانات؛ وREADS SQL DATA عندما تنفذ استعلام SELECT ... INTO لقراءة قيم من الجداول؛ وMODIFIES SQL DATA إذا كانت تنفذ تعديلاً داخلياً.
تفرض بنية الدوال المخزنة قيدين جوهريين يجب الانتباه لهما: أولاً، لا يجوز للدالة تنفيذ استعلام SELECT مفتوح يعيد جدول نتائج للعميل (بل يجب دائماً استخدام SELECT ... INTO local_var)؛ ثانياً، يمنع محرك MySQL الدالة من تعديل نفس الجدول الذي يقرأ منه أو يعدله الاستعلام الخارجي المستدعي للدالة لمنع التعارض الدائري (Error 1442).
DELIMITER $$
CREATE FUNCTION fn_customer_spent(p_cust_id INT)
RETURNS DECIMAL(12,2)
NOT DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE v_spent DECIMAL(12,2) DEFAULT 0.00;
SELECT COALESCE(SUM(amount), 0.00) INTO v_spent
FROM orders WHERE customer_id = p_cust_id AND status = 'paid';
RETURN v_spent;
END$$
DELIMITER ;
يوفر محرك MySQL 8.4 أدوات فحص وإدارة متكاملة لمراجعة الدوال المخزنة المسجلة في قاعدة البيانات. يمكنك استخدام الأمر SHOW FUNCTION STATUS LIKE 'pattern' لعرض قائمة الدوال ومنشئها وتاريخ آخر تعديل، أو استخدام SHOW CREATE FUNCTION fn_name لاستخراج النص البرمجي الكامل للدالة، أو الاستعلام البرمجي من جدول النظام information_schema.ROUTINES.
وعند الرغبة في تعديل منطق دالة مخزنة أو معاملاتها، لا يدعم MySQL تغيير جسم الدالة بأمر ALTER FUNCTION (الذي يقتصر على تعديل الخصائص الوصفية كالتعليقات)، بل نتبع نمط التحديث الآمن بحذف الدالة القديمة عبر DROP FUNCTION IF EXISTS fn_name; ثم إعادة إنشائها بـ CREATE FUNCTION.
-- Query function metadata and safely drop an old function
SELECT ROUTINE_NAME, DATA_TYPE, IS_DETERMINISTIC, SQL_DATA_ACCESS
FROM information_schema.ROUTINES
WHERE ROUTINE_TYPE = 'FUNCTION' AND ROUTINE_SCHEMA = 'shop';
SHOW CREATE FUNCTION fn_price_with_vat;
DROP FUNCTION IF EXISTS fn_shipping_band;
يوضح الجدول المقارن التالي الفروق الهندسية الدقيقة بين الدوال المخزنة (Stored Functions) والإجراءات المخزنة (Stored Procedures) في MySQL 8.4 لاختيار الأداة الأنسب لكل سيناريو:
| وجه المقارنة | الدالة المخزنة (Stored Function) | الإجراء المخزن (Stored Procedure) |
|---|---|---|
| طريقة الاستدعاء | تُستدعى داخل تعبيرات SELECT وWHERE وSET. | يُستدعى بأمر مستقل CALL sp_name(args); فقط. |
| القيمة المُرجعة | إلزامية: تعيد قيمة مفردة واحدة عبر RETURNS وRETURN. | اختيارية: يعيد صفر أو أكثر من جداول النتائج أو معاملات OUT. |
| أنواع المعاملات | معاملات إدخال فقط (بدون كتابة IN أو OUT). | يدعم IN وOUT وINOUT بالكامل. |
إرجاع جدول SELECT | ممنوع (تُستخدم SELECT ... INTO للمتغيرات فقط). | مدعوم بالكامل لإرجاع مجموعات نتائج للتطبيق. |
التصريح بـ DETERMINISTIC | إلزامي مع السجل الثنائي (أو تحديد READS SQL DATA / NO SQL). | اختياري ولا يمنع السجل الثنائي إنشاء الإجراء. |
ملخص الدرس
حصاد الدرسمراجعة مركّزة لأهم المفاهيم والقواعد الهندسية التي أتقنتها في هذا الدرس قبل التطبيق العملي
ماذا تعلّمت في هذا الدرس؟
تعلمت في هذا الدرس إنشاء الدوال المخزنة بـ CREATE FUNCTION، والتمييز بين RETURNS وRETURN، وضبط خصائص الحتمية DETERMINISTIC والوصول للبيانات READS SQL DATA وNO SQL، واستدعاء الدوال داخل جمل SELECT، ومقارنتها بالإجراءات المخزنة.
الخطوة التالية والدرس القادم
في الدرس القادم سنتعرف على المشغلات التلقائية (Triggers) التي تعمل تلقائياً عند تعديل الصفوف، والأحداث المجدولة (Scheduled Events) لأتمتة مهام الصيانة الدورية في الخلفية.
سجّل الدخول لحفظ تقدمك
تابع إنجاز المواضيع والمصطلحات والمهام، وكمّل من آخر خطوة على أي جهاز.
المصطلحات4
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 01 Stored Function الدالة المخزنة (Stored Function)
- وحدة برمجية مخصصة تُنشأ بأمر CREATE FUNCTION وتُعيد قيمة مفردة واحدة ويمكن استدعاؤها مباشرةً داخل تعبيرات SELECT وWHERE.
- للمراجعة
- 02 DETERMINISTIC Execution التنفيذ الحتمي (DETERMINISTIC)
- خاصية تعني أن الدالة تعيد دائماً نفس النتيجة لنفس المدخلات دون الاعتماد على عوامل متغيرة، مما يضمن أمان السجل الثنائي وتحسين الاستعلامات.
- للمراجعة
- 03 READS SQL DATA خاصية قراءة البيانات (READS SQL DATA)
- خاصية توضع في تعريف الدالة للإشارة إلى أنها تقرأ البيانات عبر SELECT...INTO دون إجراء أي تعديل على صفوف الجداول.
- للمراجعة
- 04 Scalar Return (RETURNS / RETURN) الإرجاع المفرد (RETURNS / RETURN)
- الجمع بين كلمة RETURNS في رأس الدالة لتحديد نوع البيانات وكلمة RETURN داخل جسم الدالة لإرجاع القيمة المحسوبة.
- للمراجعة
لا توجد بطاقات تطابق البحث والتصفية.
اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.
الاختصارات2
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 01 UDF UDF
- User-Defined Function
- الدالة المعرَّفة من قِبل المستخدم لإجراء حسابات مخصصة وإرجاع قيمة مفردة داخل الاستعلامات
- للمراجعة
- 02 BINLOG BINLOG
- Binary Log
- السجل الثنائي في MySQL الذي يحفظ تغييرات البيانات لأغراض النسخ المتماثل والاستعادة
- للمراجعة
لا توجد بطاقات تطابق البحث والتصفية.
اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.
مهامك قبل الاختبار
خطوة صغيرة كل مرة. وإنجاز تقدر ترجع له.
-
عرّف الدالة fn_price_with_vat باستخدام CREATE FUNCTION مع DETERMINISTIC وNO SQL، ثم استدعها داخل جملة SELECT على جدول الطلبات.
-
بدّل بين السيناريوهات الثلاثة في المحاكي التفاعلي للدوال المخزنة وافحص النتائج والبيانات الوصفية.
تأشير المهمة إقرار منك بإتمامها، وليس تصحيحًا آليًا للكود. يُحفظ تقدمك في حسابك.
اختبر فهمك.
ووصلها للإتقان.
كل سؤال خطوة لقدّام. اختار، اكتب كود، وشوف نتيجتك فورًا.
أكمل المهام، واجمع كل الإجابات الصحيحة. أقل من 100%؟ راجع وأعد المحاولة.
الأسئلة متاحة للاطلاع. سجّل الدخول للتفاعل وحفظ تقدمك.
لمحة عن التحدي4 أسئلة تنتظرك +
ما الكلمة المفتاحية التي توضع في رأس تعريف CREATE FUNCTION لتحديد نوع البيانات الذي ستعيده الدالة؟
اختر الإجابة الأدق وفق قواعد محرك MySQL 8.4.
ماذا تعني الخاصية DETERMINISTIC عند تعريف دالة مخزنة في MySQL 8.4؟
اختر الإجابة الأدق وفق قواعد محرك MySQL 8.4.
كيف يتم استدعاء الدالة المخزنة عادةً في MySQL مقارنةً بالإجراء المخزن؟
اختر الإجابة الأدق وفق قواعد محرك MySQL 8.4.
أي عبارة وصول للبيانات يجب تحديدها لدالة مخزنة تنفذ SELECT ... INTO لقراءة صفوف جدول دون تعديلها؟
اختر الإجابة الأدق وفق قواعد محرك MySQL 8.4.