<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: SQL</title><link>https://academy.hsoub.com/programming/sql/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: SQL</description><language>ar</language><item><title>&#x643;&#x62A;&#x627;&#x628;&#x629; &#x627;&#x633;&#x62A;&#x639;&#x644;&#x627;&#x645;&#x627;&#x62A; SQL &#x628;&#x633;&#x647;&#x648;&#x644;&#x629; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; Code Llama</title><link>https://academy.hsoub.com/programming/sql/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85%D8%A7%D8%AA-sql-%D8%A8%D8%B3%D9%87%D9%88%D9%84%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-code-llama-r2537/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_03/13.---SQL----.png.a3de0564db842b2bc364a362178f1bc1.png" /></p>
<p>
	أصبح الذكاء الاصطناعي أداةً قويةً لمساعدة المطورين في كتابة الأكواد البرمجية بسرعة ودقة، لا سيما بعد ظهور نماذج متخصصة مدرّبة على المحتوى البرمجي. سنشرح في مقال اليوم طريقة الاستفادة من نموذج كود لاما Code Llama القوي لكتابة أكواد SQL متنوعة بسهولة باستخدام أوامر بلغة طبيعية، مما يعزز الإنتاجية ويوفر وقت المطورين، وختامًا سنوضح أهم التحديات التي ستواجهنا عند الاعتماد على الذكاء الاصطناعي لكتابة الكود البرمجي وكيفية التعامل معها.
</p>

<h2>
	ما هو Code Llama
</h2>

<p>
	كود لاما Code Llama هو نموذج ذكاء اصطناعي مفتوح المصدر طورته شركة Meta في أغسطس 2023، وهو يعتمد على نموذج <a href="https://www.llama.com/llama2/" rel="external nofollow">Llama 2</a> مع تحسينات مخصصة لمعالجة وتوليد الأكواد البرمجية. فقد دُرّب هذا النموذج على مجموعات بيانات كبيرة في مختلف <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AC%D8%A7%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">مجالات البرمجة</a>، ما جعل منه أداة قوية لدعم المطورين في كتابة الأكواد وتحسينها وتسريع عمليات التطوير، يعتمد هذا النموذج على بنية <a href="https://academy.hsoub.com/programming/artificial-intelligence/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-%D8%A7%D9%84%D9%85%D8%AD%D9%88%D9%91%D9%84%D8%A7%D8%AA-transformers-%D9%85%D9%86-%D9%85%D9%86%D8%B5%D8%A9-hugging-face-r2340/" rel="">المحوّلات Transformer</a> لفهم السياق الكامل للكود البرمجي بدقة، ومعرفة العلاقة بين كل كلمة أو جزء في الكود، مما يساعده على إنشاء أكواد برمجية متوافقة مع مختلف المتطلبات. 
</p>

<p>
	يدعم نموذج Code Llama تطوير البرمجيات باستخدام عدة لغات برمجة من بينها بايثون وجافا و PHP و Bash و SQL وغيرها، مما يجعله مناسبًا لمجالات وتطبيقات متنوعة كما يستطيع فهم المدخلات النصية، بما في ذلك تعليمات مكتوبة بلغة طبيعية على غرار: اكتب لي استعلام بلغة SQL يجلب جميع المستخدمين الذين سجلوا في الدورات خلال الشهر الماضي، كما يمكن أن نوفر للنموذج أجزاء من كود SQL غير مكتمل ونطلب منه إكماله بطريقة صحيحة، أو تحسينه وجعله أكثر احترافية.
</p>

<h2>
	إصدارات Code Llama
</h2>

<p>
	يوفر Code Llama عدة إصدارات لتلبية احتياجات مختلفة في مجال البرمجة وفيما يلي شرح موجز لكل إصدار:
</p>

<ul>
	<li>
		<a href="https://huggingface.co/codellama/CodeLlama-7b-hf" rel="external nofollow">Code Llama</a>: الإصدار الأساسي العام المصمم لتوليد الأكواد البرمجية بمختلف لغات البرمجة
	</li>
	<li>
		<a href="https://huggingface.co/codellama/CodeLlama-7b-Python-hf" rel="external nofollow">Code Llama - Python</a>: إصدار متخصص في لغة كتابة أكواد للغة بايثون Python
	</li>
	<li>
		<a href="https://huggingface.co/codellama/CodeLlama-7b-Instruct-hf" rel="external nofollow">Code Llama - Instruct</a>: إصدار مُدرَّب لفهم التعليمات النصية الطبيعية بدقة كبيرة
	</li>
</ul>

<p>
	توضح الصورة التالية مراحل تدريب وتطوير الإصدارات المختلفة لنموذج Code Llama بالاعتماد على Llama 2 كنموذج أساسي، واستخدم أحجام معاملات parameters مختلفة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169608" href="https://academy.hsoub.com/uploads/monthly_2025_03/001___.png.31d6537800fd6ce2601f8e90e1e326c0.png" rel=""><img alt="01 مراحل تدريب النموذج" class="ipsImage ipsImage_thumbnailed" data-fileid="169608" data-ratio="38.43" data-unique="tusbibk9k" style="width: 800px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_03/001___.thumb.png.166963b83fb4c65bbc657e6d92ab4b67.png"></a>
</p>

<p>
	سنشرح في الفقرات التالية خطوات استخدام نموذج Code Llama لتوليد أكواد SQL، مع توفير أمثلة عملية توضح حالات استخدامه.
</p>

<h2 id="codellama">
	طريقة إعداد واستخدام Code Llama
</h2>

<p>
	يمكن تشغيل نموذج كود لاما Code Llama محليًا على جهازنا، ومن الجدير بالذكر أن تشغيل النموذج قد يتطلب موارد حاسوبية عالية، لذا إذا كان جهازنا المحلي لا يدعم ذلك، فيمكننا استخدام منصات سحابية مثل <a href="https://academy.hsoub.com/apps/productivity/%D9%86%D8%A8%D8%B0%D8%A9-%D8%AA%D8%B9%D8%B1%D9%8A%D9%81%D9%8A%D8%A9-%D8%B9%D9%86-google-colab-%D9%88%D9%85%D8%A7%D8%B0%D8%A7-%D9%8A%D9%82%D8%AF%D9%85-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86-r741/" rel="">Google Colab</a> أو <a href="https://www.kaggle.com/code/" rel="external nofollow">Kaggle Notebook</a> والتي توفر بيئة عمل بموارد قوية.
</p>

<p>
	كما يمكننا الاستعانة بالواجهة البرمجية التي توفرها منصة <a href="https://academy.hsoub.com/programming/artificial-intelligence/%D9%85%D8%A7-%D9%87%D9%8A-%D9%85%D9%86%D8%B5%D8%A9-hugging-face-%D9%84%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A-r2449/" rel="">Hugging Face</a> والتي تتيح لنا استخدام نماذج الذكاء الاصطناعي لتوليد وتحسين الأكواد البرمجية بسهولة وفعالية كما سنوضح في الفقرات التالية.
</p>

<h3 id="codellamaapi">
	استخدام Code Llama عبر Hugging Face <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>
</h3>

<p>
	يمكننا استخدام النموذج بسهولة من خلال الواجهة البرمجية  <a href="https://huggingface.co/inference-api/" rel="external nofollow">Hugging Face <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> التي تتيح استدعاء النموذج وتوليد الأكواد المطلوبة. لإعداد واستخدام Hugging Face <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>، نحتاج بدايةً للحصول على مفتاح الوصول Token من منصة Hugging Face. لذا ننتقل إلى صفحة <a href="https://huggingface.co/settings/tokens" rel="external nofollow">Hugging Face Tokens</a>، ثم نضغط على Create new token كما توضح في الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169585" href="https://academy.hsoub.com/uploads/monthly_2025_03/002___.png.988e874614a214781f41734a069b1ba2.png" rel=""><img alt="002 إنشاء مفتاح الوصول" class="ipsImage ipsImage_thumbnailed" data-fileid="169585" data-unique="ztlfuwmsm" style="width: 800px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/002___.thumb.png.4b1da8b9c9f0fea978bc56b765b26dc7.png"> </a>
</p>

<p>
	بعد ذلك، نحدد الصلاحية Read ثم نُدخل اسمًا مخصصًا في خانة Token name كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169586" href="https://academy.hsoub.com/uploads/monthly_2025_03/003___.png.d28c41cc136759c173635d83f8a046a4.png" rel=""><img alt="003 تسمية مفتاح الوصول" class="ipsImage ipsImage_thumbnailed" data-fileid="169586" data-unique="8iaoh2k92" style="width: 800px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/003___.thumb.png.b0ac0cf06a1a6d486d910f2fcc8c1b5b.png"> </a>
</p>

<p>
	بعد ذلك نضغط على زر Create token لتوليد مفتاح الواجهة البرمجية، وننسخه بالنقر على زر Cpoy ونحفظ المفتاح الناتج في مكان آمن.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169597" href="https://academy.hsoub.com/uploads/monthly_2025_03/004___.png.b02fca57dab0f879684fbb8e0ec57fa5.png" rel=""><img alt="حفظ مفتاح الوصول" class="ipsImage ipsImage_thumbnailed" data-fileid="169597" data-ratio="31.00" data-unique="jqzk547o4" style="width: 800px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_03/004___.thumb.png.3d5d4dd7cc99481437a196f6266d5a64.png"></a>
</p>

<p>
	<strong>ملاحظة</strong>: من الضروري عدم مشاركة هذا المفتاح مع أي شخص لضمان أمان حسابنا. 
</p>

<p>
	بعد الحصول على المفتاح <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> Token، يمكننا الآن إرسال طلب إلى الواجهة البرمجية، سنستخدم على سبيل المثال مكتبة requests في بايثون لنرسل طلب POST متضمنًا المُوجّه prompt المطلوب. كما نحتاج لتحديد عنوان النموذج الذي سنرسل إليه الطلب API_URL، مع تضمين مفتاح الوصول في الطلب كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2110_18" style=""><span class="com">#  استيراد المكتبة</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> requests
</span><span class="com"># رابط النموذج</span><span class="pln">
API_URL </span><span class="pun">=</span><span class="pln"> </span><span class="str">"https://api-inference.huggingface.co/models/codellama/CodeLlama-34b-Instruct-hf"</span><span class="pln">
 </span><span class="com"># نستبدل المفتاح التالي بالمفتاح الذي حصلنا عليه </span><span class="pln">
headers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="str">"Authorization"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Bearer hf_XXXXXXXXXXXXXXXXXXXX"</span><span class="pun">}</span></pre>

<p>
	بعد ذلك، سنُعرّف المُوجّه prompt الذي سنرسله للنموذج والمكتوب بلغة طبيعية كي ينتج لنا استعلام بلغة <a href="https://wiki.hsoub.com/SQL" rel="external">SQL</a>. على سبيل المثال سنجرب كتابة استعلام SQL يجلب لنا جميع المستخدمين المسجلين في الأيام السبعة الأخيرة، مع ترتيبهم تنازليًا حسب تاريخ التسجيل على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2110_20" style=""><span class="pln">prompt </span><span class="pun">=</span><span class="pln"> </span><span class="str">"""
[SYSTEM]: أنت نموذج ذكاء اصطناعي متخصص في تحويل استعلامات اللغة الطبيعية إلى SQL.
سيتم إعطاؤك استفسارات بلغة طبيعية، ومهمتك هي توليد استعلام SQL.
 [USER]: لدي قاعدة بيانات تحتوي على جدول باسم users به الأعمدة التالية:
- user_id
- user_name
- email
- created_at 
اكتب استعلام يجلب جميع المستخدمين المسجلين في آخر 7 أيام، مع ترتيبهم حسب تاريخ التسجيل من الأحدث إلى الأقدم.
 [ASSISTANT]:
"""</span></pre>

<p>
	لإرسال هذا الطلب إلى النموذج واستقبال الرد، سننشئ دالة <code>query </code>تتضمن المُوجّه ثم تعيد الاستجابة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2110_24" style=""><span class="pln"> </span><span class="com"># دالة لإرسال الطلب إلى النموذج واستقبال الرد</span><span class="pln">
</span><span class="kwd">def</span><span class="pln"> query</span><span class="pun">(</span><span class="pln">payload</span><span class="pun">):</span><span class="pln">
    response </span><span class="pun">=</span><span class="pln"> requests</span><span class="pun">.</span><span class="pln">post</span><span class="pun">(</span><span class="pln">API_URL</span><span class="pun">,</span><span class="pln"> headers</span><span class="pun">=</span><span class="pln">headers</span><span class="pun">,</span><span class="pln"> json</span><span class="pun">=</span><span class="pln">payload</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">()</span><span class="pln">
</span><span class="com"># استدعاء الدالة والحصول على الاستجابة</span><span class="pln">
output </span><span class="pun">=</span><span class="pln"> query</span><span class="pun">({</span><span class="str">"inputs"</span><span class="pun">:</span><span class="pln"> prompt</span><span class="pun">})</span><span class="pln">
output </span><span class="pun">=</span><span class="pln"> output</span><span class="pun">[</span><span class="lit">0</span><span class="pun">][</span><span class="str">'generated_text'</span><span class="pun">]</span><span class="pln">
 </span><span class="com"># طباعة الكود الذي تم توليده</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">output</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">"[ASSISTANT]:"</span><span class="pun">)[</span><span class="lit">1</span><span class="pun">])</span></pre>

<p>
	بعد إرسال الطلب، سيُحلل النموذج الطلب ويُولد لنا استعلام SQL كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2110_37" style=""><span class="pln">SELECT </span><span class="pun">*</span><span class="pln"> FROM users
WHERE created_at </span><span class="pun">&gt;=</span><span class="pln"> DATE_SUB</span><span class="pun">(</span><span class="pln">NOW</span><span class="pun">(),</span><span class="pln"> INTERVAL </span><span class="lit">7</span><span class="pln"> DAY</span><span class="pun">)</span><span class="pln">
ORDER BY created_at DESC</span><span class="pun">;</span></pre>

<p>
	توضح الصورة التالية نتيجة تنفيذ الكود ضمن المنصة السحابية <a href="https://www.kaggle.com/code" rel="external nofollow">Kaggle Notebook</a>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169588" href="https://academy.hsoub.com/uploads/monthly_2025_03/005___.png.2248a3a37235193ccbdacd651b9f3dca.png" rel=""><img alt="05 نتيجة تنفيذ الكود" class="ipsImage ipsImage_thumbnailed" data-fileid="169588" data-ratio="46.17" data-unique="kujswkfzk" style="width: 800px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_03/005___.thumb.png.4a711b25905f2ed0007d8f90eef62de8.png"></a>
</p>

<h3 id="codellama-1">
	استخدام Code Llama عبر التثبيت المباشر
</h3>

<p>
	يمكننا الاستفادة من إمكانيات النموذج واستخدامه مباشرةً دون الحاجة إلى الاتصال بواجهة برمجية خارجية مما يضمن لنا التحكم الكامل بالنموذج وضبطه بالطريقة المناسبة لنا.
</p>

<p id="">
	سنحتاج أولًا لاستيراد المكتبات اللازمة لتحميل واستخدام النموذج، حيث سنستخدم هنا النموذج <code>CodeLlama-7b-Instruct-hf</code>، ثم سنعرّف كائن <code>tokenizer </code>لتحميل محوًلات للنموذج المدرب كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2110_30" style=""><span class="com"># استيراد المكتبات اللازمة</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> torch
</span><span class="kwd">import</span><span class="pln"> transformers
</span><span class="kwd">from</span><span class="pln"> transformers </span><span class="kwd">import</span><span class="pln"> </span><span class="typ">AutoTokenizer</span><span class="pln">  
</span><span class="com"># تحديد اسم النموذج المراد تحميله</span><span class="pln">
model </span><span class="pun">=</span><span class="pln"> </span><span class="str">"codellama/CodeLlama-7b-Instruct-hf"</span><span class="pln">
</span><span class="com"># تحميل المحول الخاص بالنموذج</span><span class="pln">
tokenizer </span><span class="pun">=</span><span class="pln"> </span><span class="typ">AutoTokenizer</span><span class="pun">.</span><span class="pln">from_pretrained</span><span class="pun">(</span><span class="pln">model</span><span class="pun">)</span></pre>

<p>
	الخطوة التالية هي إنشاء خط أنابيب pipeline لإعداد نموذجنا واستخدامه في توليد الأكواد. سنحدد بدايةً المهمة الأساسية التي نريد من النموذج تنفيذها وهي توليد النصوص <code>text-generation</code>، ثم نحدد ما النموذج المراد استخدامه، ونستخدم بيانات بدقة منخفضة <code>float16</code> لتسريع الحسابات وتقليل استهلاك الذاكرة، ونقرر ما هو الجهاز المناسب الذي سيعمل عليه النموذج مثل <a href="https://academy.hsoub.com/programming/advanced/%D8%A7%D9%84%D9%81%D8%B1%D9%82-%D8%A8%D9%8A%D9%86-%D9%88%D8%AD%D8%AF%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B1%D9%83%D8%B2%D9%8A%D8%A9-cpu-%D9%88%D9%88%D8%AD%D8%AF%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-gpu-r2527/" rel="">GPU أو CPU</a> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2110_40" style=""><span class="pln">pipeline </span><span class="pun">=</span><span class="pln"> transformers</span><span class="pun">.</span><span class="pln">pipeline</span><span class="pun">(</span><span class="pln">
    </span><span class="str">"text-generation"</span><span class="pun">,</span><span class="pln">  
    model</span><span class="pun">=</span><span class="pln">model</span><span class="pun">,</span><span class="pln"> 
    torch_dtype</span><span class="pun">=</span><span class="pln">torch</span><span class="pun">.</span><span class="pln">float16</span><span class="pun">,</span><span class="pln">  
    device_map</span><span class="pun">=</span><span class="str">"auto"</span><span class="pun">,</span><span class="pln">  
</span><span class="pun">)</span></pre>

<p>
	<strong>ملاحظة</strong>: تعني القيمة <code>auto</code>  الممررة إلى نوع الجهاز <code>device_map</code> بأن النظام هو من سيحدد الجهاز الأفضل بشكل تلقائي بناءً على الموارد المتاحة.
</p>

<p id="-2">
	لنحدد الآن المُوجّه الذي سنمرره للنموذج كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2110_44" style=""><span class="pln">prompt </span><span class="pun">=</span><span class="pln"> </span><span class="str">'''
[SYSTEM]: أنت نموذج ذكاء اصطناعي متخصص في تحويل استعلامات اللغة الطبيعية إلى SQL. 
سيتم إعطاؤك استفسارات بلغة طبيعية، ومهمتك هي توليد استعلام SQL الصحيح.
لا تضف أي معلومات أخرى بعد توليد الاستعلام
[USER]: لدي قاعدة بيانات تحتوي على جدول باسم orders به الأعمدة التالية:
- order_id
- user_id
- product_name
- order_date
- quantity
اكتب استعلامًا يعرض مجموع الكميات المطلوبة لكل منتج، مرتبة حسب الكمية من الأكبر إلى الأصغر.
[ASSISTANT]:
'''</span></pre>

<p id="-3">
	ثم نكتب كود يستخدم خط الأنابيب pipeline لتنفيذ النموذج:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2110_48" style=""><span class="com"># تنفيذ النموذج لتوليد الأكواد بناءً على المُوجّه المعطى</span><span class="pln">
sequences </span><span class="pun">=</span><span class="pln"> pipeline</span><span class="pun">(</span><span class="pln">
    text_inputs</span><span class="pun">=</span><span class="pln">prompt</span><span class="pun">,</span><span class="pln">  
    do_sample</span><span class="pun">=</span><span class="kwd">True</span><span class="pun">,</span><span class="pln">  
    top_k</span><span class="pun">=</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> 
    temperature</span><span class="pun">=</span><span class="lit">0.1</span><span class="pun">,</span><span class="pln"> 
    top_p</span><span class="pun">=</span><span class="lit">1</span><span class="pun">,</span><span class="pln">  
    num_return_sequences</span><span class="pun">=</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> 
    eos_token_id</span><span class="pun">=</span><span class="pln">tokenizer</span><span class="pun">.</span><span class="pln">eos_token_id</span><span class="pun">,</span><span class="pln"> 
    max_length</span><span class="pun">=</span><span class="lit">1000</span><span class="pun">,</span><span class="pln">  
</span><span class="pun">)</span></pre>

<p>
	استخدمنا في الكود السابق عدة معاملات لضبط النموذج، دعونا نشرح دور كل متغير وأهميته لتوليد استعلام SQL:
</p>

<ul>
	<li>
		<code>text_inputs=prompt</code>: تمرير الموجّه المكتوب بلغة طبيعية للنموذج لتحويله إلى استعلام SQL
	</li>
	<li>
		<code>do_sample=True</code>: تمكين التوليد العشوائي للنصوص لتوليد استعلامات متنوعة
	</li>
	<li>
		<code>top_k=1</code>: اختيار أعلى احتمالية للكلمة التالية
	</li>
	<li>
		<code>temperature=0.1</code>: تقليل العشوائية لجعل النتائج أكثر دقة
	</li>
	<li>
		<code>top_p=1</code>: استخدام طريقة التصفية العليا لضبط التوليد، وهو يساعد في اختيار الكلمات الأكثر احتمالاً بناءً على المعطيات
	</li>
	<li>
		<code>num_return_sequences=1</code>: تعيين عدد النصوص أو الاستجابات المولدة
	</li>
	<li>
		<code>eostokenid=tokenizer.eostokenid</code>: تحديد رمز نهاية الجملة لمنع التوليد غير المحدود للنصوص
	</li>
	<li>
		<code>max_length=1000</code>: يحدد الحد الأقصى لطول النص المولد
	</li>
</ul>

<p id="-4">
	أخيرًا لنطبع الأكواد المولدة بكتابة الكود التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9406_9" style=""><span class="kwd">for</span><span class="pln"> seq </span><span class="kwd">in</span><span class="pln"> sequences</span><span class="pun">:</span><span class="pln">
    output </span><span class="pun">=</span><span class="pln"> seq</span><span class="pun">[</span><span class="str">'generated_text'</span><span class="pun">].</span><span class="pln">split</span><span class="pun">(</span><span class="str">"[ASSISTANT]:"</span><span class="pun">)[</span><span class="lit">1</span><span class="pun">]</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">output</span><span class="pun">)</span></pre>

<p>
	سيولد النموذج استعلام SQL بناءً على المُوجّه prompt المُمرّر له كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9406_11" style=""><span class="pln">SELECT product_name</span><span class="pun">,</span><span class="pln"> SUM</span><span class="pun">(</span><span class="pln">quantity</span><span class="pun">)</span><span class="pln"> AS total_quantity
FROM orders
GROUP BY product_name
ORDER BY total_quantity DESC</span><span class="pun">;</span></pre>

<p>
	فيما يلي صورة توضح نتيجة التنفيذ:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169589" href="https://academy.hsoub.com/uploads/monthly_2025_03/006___.png.aae2824eaf9e084291664be3feace706.png" rel=""><img alt="06 نتيحة تنفيذ الكود" class="ipsImage ipsImage_thumbnailed" data-fileid="169589" data-ratio="45.83" data-unique="lqg0h4hvh" style="width: 800px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_03/006___.thumb.png.a1b6672a639d7952bdb1648474a294da.png"></a>
</p>

<h2 id="sql-1">
	توليد استعلامات SQL وتنفيذها على قاعد بيانات حقيقية
</h2>

<p>
	دعونا الآن نستخدم Code Llama عبر الواجهة البرمجية Hugging Face <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> لتحويل استفسارات اللغة الطبيعية إلى استعلامات SQL، ثم تنفيذها على قاعدة بيانات حقيقية. سنبدأ بتنزيل قاعدة بيانات <a href="https://github.com/lerocha/chinook-database/raw/master/ChinookDatabase/DataSources/Chinook_Sqlite.sqlite" rel="external nofollow">Chinook</a> من GitHub، ومن ثم سنستخرج معلومات الجداول المتوفرة. بعد ذلك، سنُمرّر هيكل قاعدة البيانات مع استفسار محدد إلى النموذج، والذي سيعمل على توليد استعلام SQL تلقائيًا. أخيرًا، سننفذ الاستعلام على قاعدة البيانات ونستعرض النتائج.
</p>

<p>
	في البداية، سنثبت المكتبات الضرورية:
</p>

<pre class="ipsCode">!pip install langchain_community
</pre>

<p>
	بعدها، سنعمل على إعداد Code Llama عبر  Hugging Face <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr>:</abbr>
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2110_51" style=""><span class="com">#  استيراد المكتبة</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> requests
</span><span class="com"># رابط النموذج</span><span class="pln">
API_URL </span><span class="pun">=</span><span class="pln"> </span><span class="str">"https://api-inference.huggingface.co/models/codellama/CodeLlama-34b-Instruct-hf"</span><span class="pln">
</span><span class="com"># استبدل المفتاح التالي بالمفتاح الذي حصلت عليه</span><span class="pln">
API_KEY </span><span class="pun">=</span><span class="pln"> </span><span class="str">"hf_XXXXXXXXXXXXXXXXXXXX"</span><span class="pln">  
headers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="str">"Authorization"</span><span class="pun">:</span><span class="pln"> f</span><span class="str">"Bearer {API_KEY}"</span><span class="pun">}</span><span class="pln">
</span><span class="com"># دالة لاستدعاء النموذج عبر API</span><span class="pln">
</span><span class="kwd">def</span><span class="pln"> query_llama</span><span class="pun">(</span><span class="pln">prompt</span><span class="pun">):</span><span class="pln">
    response </span><span class="pun">=</span><span class="pln"> requests</span><span class="pun">.</span><span class="pln">post</span><span class="pun">(</span><span class="pln">API_URL</span><span class="pun">,</span><span class="pln"> headers</span><span class="pun">=</span><span class="pln">headers</span><span class="pun">,</span><span class="pln"> json</span><span class="pun">={</span><span class="str">"inputs"</span><span class="pun">:</span><span class="pln"> prompt</span><span class="pun">})</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">()[</span><span class="lit">0</span><span class="pun">][</span><span class="str">'generated_text'</span><span class="pun">]</span></pre>

<p>
	نبدأ بعدها بإعداد قاعدة البيانات واستخراج هيكلها:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9406_17" style=""><span class="com"># استيراد المكتبات اللازمة</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> requests
</span><span class="kwd">from</span><span class="pln"> langchain_community</span><span class="pun">.</span><span class="pln">utilities </span><span class="kwd">import</span><span class="pln"> </span><span class="typ">SQLDatabase</span><span class="pln">
</span><span class="com"># رابط قاعدة البيانات على GitHub</span><span class="pln">
url </span><span class="pun">=</span><span class="pln"> </span><span class="str">"https://github.com/lerocha/chinook-database/raw/master/ChinookDatabase/DataSources/Chinook_Sqlite.sqlite"</span><span class="pln">
</span><span class="com"># تحديد مسار حفظ قاعدة البيانات</span><span class="pln">
db_path </span><span class="pun">=</span><span class="pln"> </span><span class="str">"/content/Chinook.db"</span><span class="pln">
</span><span class="com"># تنزيل قاعدة البيانات من GitHub</span><span class="pln">
response </span><span class="pun">=</span><span class="pln"> requests</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">status_code </span><span class="pun">==</span><span class="pln"> </span><span class="lit">200</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">with</span><span class="pln"> open</span><span class="pun">(</span><span class="pln">db_path</span><span class="pun">,</span><span class="pln"> </span><span class="str">"wb"</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> f</span><span class="pun">:</span><span class="pln">
        f</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">content</span><span class="pun">)</span><span class="pln">
</span><span class="com"># الاتصال بقاعدة البيانات عبر LangChain</span><span class="pln">
db </span><span class="pun">=</span><span class="pln"> </span><span class="typ">SQLDatabase</span><span class="pun">.</span><span class="pln">from_uri</span><span class="pun">(</span><span class="pln">f</span><span class="str">"sqlite:///{db_path}"</span><span class="pun">)</span><span class="pln">
</span><span class="com"># عرض أسماء الجداول المتاحة</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"<span class="ipsEmoji">📌</span> الجداول المتاحة في قاعدة البيانات:"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">db</span><span class="pun">.</span><span class="pln">get_usable_table_names</span><span class="pun">())</span><span class="pln">
</span><span class="com"># استخراج معلومات الجداول من قاعدة البيانات</span><span class="pln">
db_schema </span><span class="pun">=</span><span class="pln"> db</span><span class="pun">.</span><span class="pln">table_info</span></pre>

<p>
	سنحصل على الجداول التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4215_10" style=""><span class="pun"><span class="ipsEmoji">📌</span></span><span class="pln"> </span><span class="pun">الجداول</span><span class="pln"> </span><span class="pun">المتاحة</span><span class="pln"> </span><span class="pun">في</span><span class="pln"> </span><span class="pun">قاعدة</span><span class="pln"> </span><span class="pun">البيانات:</span><span class="pln">
</span><span class="pun">[</span><span class="str">'Album'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Artist'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Customer'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Employee'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Genre'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Invoice'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'InvoiceLine'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'MediaType'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Playlist'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'PlaylistTrack'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Track'</span><span class="pun">]</span></pre>

<p>
	نحن جاهزون الأن لتوليد استعلام SQL وتطبيقه على قاعدة البيانات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9406_19" style=""><span class="com"># تحديد الإدخال الذي سيتم تمريره للنموذج</span><span class="pln">
user_query </span><span class="pun">=</span><span class="pln"> </span><span class="str">"اكتب استعلامًا يجلب أسماء 5 عملاء من قاعدة البيانات"</span><span class="pln">
prompt </span><span class="pun">=</span><span class="pln"> f</span><span class="str">'''
[SYSTEM]: أنت نموذج ذكاء اصطناعي متخصص في تحويل استعلامات اللغة الطبيعية إلى SQL. 
سيتم إعطاؤك استفسارات بلغة طبيعية، ومهمتك هي توليد استعلام SQL.
[USER]: 
لدي قاعدة بيانات تحتوي على
{db_schema}
{user_query}
[ASSISTANT]:
'''</span><span class="pln">
</span><span class="com"># توليد استعلام SQL</span><span class="pln">
sql_query </span><span class="pun">=</span><span class="pln"> query_llama</span><span class="pun">(</span><span class="pln">prompt</span><span class="pun">)</span><span class="pln">
sql_query </span><span class="pun">=</span><span class="pln"> sql_query</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">"[ASSISTANT]:"</span><span class="pun">)[</span><span class="lit">1</span><span class="pun">]</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"<span class="ipsEmoji">🔹</span> الاستعلام الذي تم توليده:"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">sql_query</span><span class="pun">)</span><span class="pln">
</span><span class="com"># تنفيذ الاستعلام على قاعدة البيانات</span><span class="pln">
result </span><span class="pun">=</span><span class="pln"> db</span><span class="pun">.</span><span class="pln">run</span><span class="pun">(</span><span class="pln">sql_query</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"\n<span class="ipsEmoji">🔹</span> النتيجة:\n"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">result</span><span class="pun">)</span></pre>

<p>
	سنحصل على استعلام بلغة SQL يعرض أول خمسة أسماء من جدول العملاء، وتعرض النتيجة الموافقة لهذا الاستعلام وفق قاعدة البيانات:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4215_8" style=""><span class="pun"><span class="ipsEmoji">🔹</span></span><span class="pln"> </span><span class="pun">الاستعلام</span><span class="pln"> </span><span class="pun">الذي</span><span class="pln"> </span><span class="pun">تم</span><span class="pln"> </span><span class="pun">توليده:</span><span class="pln">
SELECT firstname FROM customer LIMIT </span><span class="lit">5</span><span class="pun">;</span><span class="pln">
</span><span class="pun"><span class="ipsEmoji">🔹</span></span><span class="pln"> </span><span class="pun">النتيجة:</span><span class="pln">
</span><span class="pun">[(</span><span class="str">'Luís'</span><span class="pun">,),</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Leonie'</span><span class="pun">,),</span><span class="pln"> </span><span class="pun">(</span><span class="str">'François'</span><span class="pun">,),</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Bjørn'</span><span class="pun">,),</span><span class="pln"> </span><span class="pun">(</span><span class="str">'František'</span><span class="pun">,)]</span></pre>

<p>
	نلاحظ في الصورة التالية نتيجة التنفيذ بالكامل:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169590" href="https://academy.hsoub.com/uploads/monthly_2025_03/007___.png.415c86d9ee6ea20666043db6ce7db742.png" rel=""><img alt="07 نتيجة تنفيذ الكود" class="ipsImage ipsImage_thumbnailed" data-fileid="169590" data-ratio="46.17" data-unique="q8aknds5u" style="width: 800px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_03/007___.thumb.png.43b6f985f8b0708553183c62078890b4.png"></a>
</p>

<h2 id="sql-2">
	تحديات وحلول توليد استعلامات SQL بالذكاء الاصطناعي
</h2>

<p>
	يواجه توليد استعلامات SQL باستخدام تقنيات الذكاء الاصطناعي عددًا من التحديات، ولكن يمكن تجاوزها من خلال تبني حلول مبتكرة وتخصيص النموذج وفقًا للمتطلبات. فيما يلي أبرز التحديات والحلول المقترحة:
</p>

<h3 id="sql-3">
	تنوع صياغة SQL
</h3>

<p>
	توجد بعض الاختلافات في أسلوب كتابة أكواد SQL المستخدمة في أنظمة إدارة قواعد البيانات العلاقية مثل MySQL وOracle وPostgreSQL، لذا نحتاج لتخصيص عملية توليد الاستعلامات بحيث تتناسب مع النظام المستهدف، وتلتزم باستخدام الصيغة الصحيحة. يمكن تحقيق ذلك من خلال تدريب النماذج على عينات من كل صيغة، أو إضافة مدخلات سياقية تحدد النظام المطلوب.
</p>

<h3 id="-5">
	نقص في سياق البيانات
</h3>

<p>
	تفتقر بعض الاستعلامات المولدة من إدخالات اللغة الطبيعية إلى المعلومات السياقية الضرورية، مثلًا لو طلبنا كتابة استعلام عن المبيعات لهذا العام فقد لا يعرف النموذج ما هو اسم جدول المبيعات بالضبط، واسم العمود الذي يحتوي على إجمالي المبيعات، ولحل هذه المشكلة، يمكن تعزيز السياق عن طريق إدراج معلومات إضافية مثل البيانات الوصفية لقاعدة البيانات metadata وسجل جلسة المستخدم لمساعدة النموذج على فهم السياق وتوليد استعلامات صحيحة.
</p>

<h3 id="-6">
	معالجة الأخطاء
</h3>

<p>
	من المحتمل أن ينتج عن توليد استعلامات SQL بعض الأخطاء في الكود، مما يتطلب وجود آلية لتقديم تغذية راجعة للمستخدم لتصحيحها. لحل هذه المشكلة، يمكن إنشاء واجهة مستخدم تفاعلية تشرح الأخطاء المحتملة وتقدم اقتراحات للتصحيح. بالإضافة إلى ذلك، يمكن استخدام ملاحظات المستخدم لتحسين النموذج وتدريبه على تجنب الأخطاء في المستقبل.
</p>

<h2 id="-7">
	الخاتمة
</h2>

<p>
	إلى هنا نكون قد وصلنا لنهاية مقالنا الذي شرحنا فيه كيف يمكن للذكاء الاصطناعي من خلال نموذج Code Llama، أن يسهم في تبسيط وتسريع عملية كتابة استعلامات SQL بدقة وكفاءة عالية. واستعرضنا كيفية إعداد النموذج سواء عبر واجهة برمجة التطبيقات <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> أو بالتثبيت المباشر على الأجهزة أو عبر بيئات الحوسبة السحابية، مما يوفر مرونة كبيرة للمطورين في اختيار البيئة التي تناسب احتياجاتهم. كما ناقشنا التحديات المتعلقة بتنوع لهجات SQL وتعقيد الاستعلامات، وكيف يمكن تجاوزها باستخدام أساليب متقدمة لتخصيص الاستعلامات ومعالجة الأخطاء.
</p>

<h2 id="-8">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/artificial-intelligence/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A-%D9%84%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D9%88%D8%AA%D8%B5%D8%AD%D9%8A%D8%AD-%D8%A3%D9%83%D9%88%D8%A7%D8%AF-css-r2531/" rel="">استخدام الذكاء الاصطناعي لكتابة وتصحيح أكواد CSS</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/artificial-intelligence/%D9%83%D9%8A%D9%81-%D8%A3%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%AA%D9%82%D9%86%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%9F-r2416/" rel="">كيف أستخدم تقنيات الذكاء الاصطناعي في عملي؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/artificial-intelligence/%D8%AA%D8%B9%D8%B2%D9%8A%D8%B2-%D8%A5%D9%86%D8%AA%D8%A7%D8%AC%D9%8A%D8%A9-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D8%B1%D9%8A%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A-%D8%A7%D9%84%D8%AA%D9%88%D9%84%D9%8A%D8%AF%D9%8A-r2473/" rel="">تعزيز إنتاجية المطورين باستخدام الذكاء الاصطناعي التوليدي</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/artificial-intelligence/%D8%A8%D9%86%D8%A7%D8%A1-%D8%B1%D9%88%D8%A8%D9%88%D8%AA-%D8%AF%D8%B1%D8%AF%D8%B4%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%88-openai-api-r2483/" rel="">بناء روبوت دردشة باستخدام بايثون و OpenAI <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2537</guid><pubDate>Tue, 18 Mar 2025 12:00:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x625;&#x62C;&#x631;&#x627;&#x621;&#x627;&#x62A; &#x627;&#x644;&#x645;&#x62E;&#x632;&#x646;&#x629; Stored Procedures &#x641;&#x64A; MySQL</title><link>https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%A5%D8%AC%D8%B1%D8%A7%D8%A1%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AE%D8%B2%D9%86%D8%A9-stored-procedures-%D9%81%D9%8A-mysql-r2536/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_03/MySQL.png.21cd395359bc15afb8a3997b1955eaef.png" /></p>
<p>
	عند استخدام قاعدة بيانات علاقية Relational Database، سنحتاج إلى استخدام استعلامات فردية باستخدام لغة الاستعلام البنيوية Structured Query Language -أو SQL اختصارًا- لاسترجاع البيانات أو معالجتها مثل استعلامات <code>SELECT</code> أو <code>INSERT</code> أو <code>UPDATE</code> أو <code>DELETE</code> من شيفرة التطبيق مباشرةً، إذ تعمل هذه التعليمات مع جداول قاعدة البيانات الأساسية وتعالجها فورًا.
</p>

<p>
	لكن إذا استخدمنا التعليمات أو مجموعة التعليمات نفسها ضمن تطبيقات متعددة يمكنها الوصول إلى قاعدة البيانات نفسها، وتكررت هذه التعليمات عدة مرات فيمكننا في هذه الحالة استخدام الإجراءات المخزَّنة Stored Procedures التي يدعمها MySQL كحال العديد من أنظمة إدارة قواعد البيانات العلاقية الأخرى، حيث تساعد هذه الإجراءات المخزنة في تجميع تعليمة SQL واحدة أو أكثر لإعادة استخدامها باسم مشترك من خلال تغليف منطق العمل المشترك ضمن قاعدة البيانات نفسها، ويمكن استدعاء مثل هذه الإجراءات من التطبيق الذي يصل إلى قاعدة البيانات لاسترجاع البيانات أو معالجتها.
</p>

<p>
	تساعدنا الإجراءات المخزنة على إنشاء برامج قابلة لإعادة الاستخدام للمهام الشائعة التي سنستخدمها عبر تطبيقات متعددة، كما توفر طريقة للتحقق من صحة البيانات، أو تقديم طبقة إضافية من أمان الوصول إلى البيانات من خلال تقييد مستخدمي قاعدة البيانات من الوصول إلى الجداول الأساسية مباشرةً وإنشاء استعلامات عشوائية.
</p>

<p>
	سنتعلّم في هذا المقال ما هي الإجراءات المخزَّنة وكيفية إنشاء إجراءات مخزنة بسيطة تعيد البيانات، وإجراءات تستخدم كلًا من معاملات الدخل والخرج.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	يجب أن يكون لدينا حاسوب يشغّل نظام إدارة قواعد البيانات العلاقية <a href="https://academy.hsoub.com/programming/sql/%D9%81%D9%87%D9%85-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D9%82%D9%8A%D8%A9-r2216/" rel="">RDBMS</a> المستند إلى لغة SQL. وقد اختبرنا التعليمات والأمثلة الواردة في هذا المقال باستخدام البيئة التالية:
</p>

<ul>
	<li>
		خادم عامل على توزيعة أوبنتو مع مستخدم بصلاحيات مسؤول مختلف عن المستخدم الجذر وجدار حماية مضبوط باستخدام أداة UFW كما هو موضح في <a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04" rel="external nofollow" target="_blank">دليل الإعداد الأولي للخادم مع الإصدار 20.04 من أوبنتو</a>
	</li>
	<li>
		نظام MySQL مُثبَّت على الخادم كما هو موضح في مقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبنتو</a>، وقد نفذنا خطوات هذا المقال باستخدام مستخدم MySQL مختلف عن المستخدم الجذر
	</li>
	<li>
		معرفة أساسية بتنفيذ <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%B9%D9%86-%D8%A7%D9%84%D8%B3%D8%AC%D9%84%D8%A7%D8%AA-%D9%85%D9%86-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r2249/" rel="">استعلامات SELECT</a> لاسترجاع البيانات من قاعدة البيانات
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: تجدر الإشارة لأنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية RDBMS لها تقديماتها الفريدة من <a href="https://academy.hsoub.com/devops/servers/databases/%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-database/" rel="">لغة SQL</a>، ولا تُعَد صيغة الإجراءات المخزنة جزءًا من معيار SQL الرسمي. حيث ستعمل الأوامر المُقدمة في هذا المقال بنجاح مع معظم هذه الأنظمة، ولكن تُعَد الإجراءات المخزنة خاصة بقاعدة البيانات، وبالتالي قد نجد بعض الاختلافات في الصيغة أو الناتج عند اختبارها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<p>
	سنحتاج أيضًا إلى قاعدة بيانات فارغة يمكن من خلالها <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r2224/" rel="">إنشاء جداول</a> توضّح استخدام الإجراءات المخزنة، ويمكن مطالعة القسم التالي للحصول على تفاصيل حول الاتصال بخادم MySQL وإنشاء قاعدة بيانات تجريبية، والتي سنستخدمها في أمثلة هذا المقال.
</p>

<h2 id="mysql">
	الاتصال بخادم MySQL وإعداد قاعدة بيانات تجريبية
</h2>

<p>
	سنتتصل بخادم MySQL وننشئ قاعدة بيانات تجريبية لاتباع الأمثلة الواردة في هذا المقال، حيث سنستخدم مجموعة سيارات افتراضية، ونخزّن تفاصيل السيارات المملوكة حاليًا مع نوعها وطرازها وسنة بنائها وقيمتها.
</p>

<p>
	إذا كان نظام قاعدة بيانات SQL الخاص بنا يعمل على خادم بعيد، نتصل بالخادم باستخدام بروتوكول <a href="https://academy.hsoub.com/devops/security/ssh/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr></a> من جهازنا المحلي كما يلي:
</p>

<pre class="ipsCode">$ ssh user@your_server_ip
</pre>

<p>
	ثم نفتح واجهة سطر أوامر خادم MySQL مع وضع اسم حساب مستخدم MySQL الخاص بنا مكان <code>user</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2968_7" style=""><span class="pln">$ mysql </span><span class="pun">-</span><span class="pln">u user </span><span class="pun">-</span><span class="pln">p</span></pre>

<p>
	ننشئ قاعدة بيانات باسم <code>procedures</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2968_9" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE DATABASE procedures</span><span class="pun">;</span></pre>

<p>
	إذا أُنشئِت قاعدة البيانات بنجاح، فسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	يمكن اختيار قاعدة البيانات <code>procedures</code> من خلال تنفيذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2968_11" style=""><span class="pln">$ USE procedures</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	اخترنا قاعدة البيانات، ويمكنك الآن إنشاء جداول تجريبية ضمنها. سيحتوي الجدول <code>cars</code> على بيانات مبسَّطة حول السيارات الموجودة في قاعدة البيانات، حيث سيحتوي على الأعمدة التالية:
</p>

<ul>
	<li>
		<strong><code>make</code></strong>: نوع كل سيارة مملوكة، ونمثّل باستخدام نوع البيانات <code>varchar</code> بحد أقصى 100 محرف
	</li>
	<li>
		<strong><code>model</code></strong>: اسم طراز السيارة، ونمثّله باستخدام نوع البيانات <code>varchar</code> بحد أقصى 100 محرف
	</li>
	<li>
		<strong><code>year</code></strong>: سنة صنع السيارة باستخدام نوع البيانات <code>int</code> للاحتفاظ بالقيم العددية
	</li>
	<li>
		<strong><code>value</code></strong>: قيمة السيارة باستخدام نوع البيانات <code>decimal</code> بحد أقصى 10 أرقام ورقمين بعد الفاصلة العشرية
	</li>
</ul>

<p>
	أنشئ هذا الجدول التجريبي باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2968_13" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE cars </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     make varchar</span><span class="pun">(</span><span class="lit">100</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     model varchar</span><span class="pun">(</span><span class="lit">100</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     year </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="kwd">value</span><span class="pln"> </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	إذا كان الخرج كما يلي، فهذا يعني إنشاء الجدول بنجاح:
</p>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.00 sec)
</pre>

<p>
	<a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D8%AF%D8%B1%D8%A7%D8%AC-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r2226/" rel="">سندرج بعض البيانات</a> التجريبية في الجدول <code>cars</code> من خلال تنفيذ عملية <code>INSERT INTO</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2968_15" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO cars
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Porsche'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'911 GT3'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2020</span><span class="pun">,</span><span class="pln"> </span><span class="lit">169700</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Porsche'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Cayman GT4'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2018</span><span class="pun">,</span><span class="pln"> </span><span class="lit">118000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Porsche'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Panamera'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2022</span><span class="pun">,</span><span class="pln"> </span><span class="lit">113200</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Porsche'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Macan'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2019</span><span class="pun">,</span><span class="pln"> </span><span class="lit">27400</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Porsche'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'718 Boxster'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2017</span><span class="pun">,</span><span class="pln"> </span><span class="lit">48880</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Ferrari'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'488 GTB'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2015</span><span class="pun">,</span><span class="pln"> </span><span class="lit">254750</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Ferrari'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'F8 Tributo'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2019</span><span class="pun">,</span><span class="pln"> </span><span class="lit">375000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Ferrari'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'SF90 Stradale'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2020</span><span class="pun">,</span><span class="pln"> </span><span class="lit">627000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Ferrari'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'812 Superfast'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2017</span><span class="pun">,</span><span class="pln"> </span><span class="lit">335300</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Ferrari'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'GTC4Lusso'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2016</span><span class="pun">,</span><span class="pln"> </span><span class="lit">268000</span><span class="pun">);</span></pre>

<p>
	تضيف العملية <code>INSERT INTO</code> عشر سيارات رياضية نموذجية للجدول، حيث توجد أربع سيارات من نوع Porsche وخمسة سيارات من نوع Ferrari. يشير الخرج التالي إلى إضافة جميع الصفوف العشرة:
</p>

<pre class="ipsCode">الخرج
Query OK, 10 rows affected (0.00 sec)
Records: 10  Duplicates: 0  Warnings: 0
</pre>

<p>
	نحن الآن جاهزون لمتابعة هذا المقال والبدء باستخدام الإجراءات المُخزَّنة في لغة SQL.
</p>

<h2 id="storedprocedures">
	مقدمة إلى الإجراءات المخزنة Stored Procedures
</h2>

<p>
	الإجراءات المخزنة في MySQL وفي العديد من أنظمة قواعد البيانات العلاقية الأخرى هي كائنات مُسمَّاة تحتوي على تعليمة واحدة أو أكثر تضعها وتنفّذها قاعدة البيانات عند استدعائها. يمكن للإجراء المخزن حفظ تعليمة مشتركة ضمن برنامج قابل لإعادة الاستخدام مثل استرجاع البيانات من قاعدة البيانات باستخدام المرشّحات Filters المُستخدَمة كثيرًا، حيث يمكنك مثلًا إنشاء إجراء مخزن لاسترجاع عملاء متجر الكتروني قدّموا طلبات خلال عدد معين من الأشهر. يمكن للإجراءات المخزنة أيضًا تمثيل البرامج الشاملة التي تصف منطق الأعمال المعقد للتطبيقات القوية في السيناريوهات الأكثر تعقيدًا.
</p>

<p>
	يمكن أن تتضمن مجموعة التعليمات في إجراء مخزَّن تعليمات SQL شائعة مثل استعلامات <code>SELECT</code> أو <code>INSERT</code> التي تعيد البيانات أو تعالجها، ويمكن للإجراءات المخزنة الاستفادة مما يلي:
</p>

<ul>
	<li>
		المعاملات المُمرَّرة إلى الإجراء المخزن أو المُعادة منه
	</li>
	<li>
		المتغيرات المُصرَّح عنها لمعالجة البيانات المُسترجَعة من شيفرة الإجراء البرمجية مباشرةً
	</li>
	<li>
		التعليمات الشرطية التي تسمح بتنفيذ أجزاء من شيفرة الإجراء المخزن البرمجية وفق شروط معينة مثل تعليمات <code>IF</code> أو <code>CASE</code>
	</li>
	<li>
		الحلقات مثل <code>WHILE</code> و <code>LOOP</code> و <code>REPEAT</code> لتنفيذ أجزاء من الشيفرة البرمجية عدة مرات
	</li>
	<li>
		تعليمات معالجة الأخطاء مثل إعادة رسائل الخطأ إلى مستخدمي قاعدة البيانات الذين يمكنهم الوصول إلى الإجراء.
	</li>
	<li>
		استدعاءات إجراءات مخزنة أخرى في قاعدة البيانات
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: تسمح الصيغة الموسَّعة Extensive Syntax التي يدعمها MySQL بكتابة برامج قوية وحل المشكلات المعقدة باستخدام الإجراءات المخزنة مثل التحكم في تدفق البرنامج باستخدام التعليمات الشرطية واستخدام المتغيرات والحلقات ومعالجة الأخطاء المخصصة وغيرها من الاستخدامات ولكن سيغطي هذا المقال فقط الاستخدام الأساسي للإجراءات المخزنة مع تعليمات SQL المُضمَّنة في جسم الإجراء المخزَّن ومعاملات الدخل والخرج، إذ سيكون تنفيذ التعليمات الشرطية واستخدام المتغيرات والحلقات ومعالجة الأخطاء المُخصَّصة خارج نطاق هذا المقال، لذا يمكن مطالعة <a href="https://dev.mysql.com/doc/refman/8.0/en/stored-programs-defining.html" rel="external nofollow" target="_blank">توثيق MySQL الرسمي</a> لمعرفة المزيد حول الإجراءات المخزنة.
</p>

<p>
	إذا استدعينا الإجراء باسمه، فسينفّذه محرّك قاعدة البيانات كما هو مُعرَّف تعليمةً تلو الأخرى. يجب أيضًا أن يكون لدى مستخدم قاعدة البيانات الأذونات المناسبة لتنفيذ الإجراء المُحدَّد، حيث توفر هذه الأذونات المطلوبة طبقة من الأمان، مما يمنع الوصول المباشر إلى قاعدة البيانات مع منح المستخدمين إمكانية الوصول إلى إجراءات فردية مضمونة الأمان لتنفيذها.
</p>

<p>
	تُنفَّذ الإجراءات المخزَّنة على خادم قاعدة البيانات مباشرةً مع إجراء جميع العمليات الحسابية محليًا وإعادة النتائج إلى المستخدم المستدعي عند الانتهاء فقط. وإذا أردنا تغيير سلوك الإجراء، فيمكن تحديث الإجراء في قاعدة البيانات، وستلتقط التطبيقات التي تستخدمه الإصدار الجديد تلقائيًا، وسيبدأ جميع المستخدمين باستخدام الشيفرة البرمجية للإجراء الجديد مباشرةً دون الحاجة لتعديل تطبيقاتهم.
</p>

<p>
	فيما يلي الهيكل العام لشيفرة SQL المستخدَمة لإنشاء إجراءٍ مخزَّن:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2968_17" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> DELIMITER </span><span class="com">//</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE PROCEDURE procedure_name</span><span class="pun">(</span><span class="pln">parameter_1</span><span class="pun">,</span><span class="pln"> parameter_2</span><span class="pun">,</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.,</span><span class="pln"> parameter_n</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">BEGIN</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     instruction_1</span><span class="pun">;</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     instruction_2</span><span class="pun">;</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     instruction_n</span><span class="pun">;</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">END</span><span class="pln"> </span><span class="com">//</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> DELIMITER </span><span class="pun">;</span></pre>

<p>
	التعليمتان الأولى والأخيرة في مقطع الشيفرة البرمجية السابق هما <code>DELIMITER //‎</code> و <code>DELIMITER ;‎</code>، حيث يستخدم <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a> رمز الفاصلة المنقوطة <code>;</code> لتحديد التعليمات والإشارة إلى بدايتها ونهايتها. إذا نفّذنا تعليمات متعددة في طرفية MySQL مع الفصل بينها بفواصل منقوطة، سيكون التعامل معها كأنها أوامر منفصلة مع تنفيذ كل تعليمة تنفيذًا مستقلًا عن التعليمات الأخرى واحدة تلو الأخرى.
</p>

<p>
	يمكن للإجراء المخزَّن أيضًا أن يتضمّن أوامر متعددة ستُنفَّذ تسلسليًا عند استدعائه، مما يشكّل صعوبة عند محاولة إخبار MySQL بإنشاء إجراء جديد، إذ سيرى محرّك قاعدة البيانات علامة الفاصلة المنقوطة في جسم الإجراء المُخزَّن ويعتقد أنه يجب أن يتوقف عن تنفيذ التعليمة، وبالتالي تكون التعليمة المقصودة في هذه الحالة هي الشيفرة البرمجية لإنشاء الإجراء بالكامل، وليس تعليمةً واحدة ضمن الإجراء نفسه، لذا قد يسيء MySQL تفسير ما نقصده.
</p>

<p>
	يمكن التغلب على هذا القيد من خلال استخدام الأمر <code>DELIMITER</code> لتغيير هذا المحدِّد مؤقتًا من <code>;</code> إلى <code>//</code> طوال مدة استدعاء التعليمة <code>CREATE PROCEDURE</code>، ثم ستُمرَّر جميع الفواصل المنقوطة الموجودة في جسم الإجراء المخزَّن إلى الخادم كما هي، ثم يتغير المحدِّد مرة أخرى إلى <code>;</code> في آخر تعليمة <code>DELIMITER ;‎</code> بعد الانتهاء من الإجراء بالكامل.
</p>

<p>
	يمثّل الاستدعاء <code>CREATE PROCEDURE</code> وبعده اسم الإجراء <code>procedure_name</code> في المثال السابق جوهر الشيفرة البرمجية الخاصة بإنشاء إجراء جديد، ويتبع اسم الإجراء قائمة اختيارية من المعاملات التي سيقبلها الإجراء. الجزء الأخير من الشيفرة البرمجية هو جسم الإجراء المضمَّن ضمن تعليمتي <code>BEGIN</code> و <code>END</code>، ويوجد في الداخل شيفرة الإجراء البرمجية، والتي يمكن أن تحتوي على تعليمة SQL واحدة مثل استعلام <code>SELECT</code> أو شيفرة برمجية أكثر تعقيدًا. ينتهي الأمر <code>END</code> بالرمز <code>//</code>، والذي يُعَد محدِّدًا مؤقتًا عوضًا عن الفاصلة المنقوطة النموذجية.
</p>

<p>
	سننشئ في القسم التالي إجراءً مخزنًا بسيطًا بدون معاملات تتضمن استعلامًا واحدًا.
</p>

<h2 id="-1">
	إنشاء إجراء مخزن بدون معاملات
</h2>

<p>
	سننشئ في هذا القسم أول إجراء مخزّن يغلِّف تعليمة SQL واحدة هي التعليمة <code>SELECT</code> لإعادة قائمة السيارات المملوكة المرتبة حسب نوعها وقيمتها بترتيب تنازلي.
</p>

<p>
	نبدأ بتنفيذ التعليمة <code>SELECT</code> التي ستستخدمها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2968_19" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM cars ORDER BY make</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">value</span><span class="pln"> DESC</span><span class="pun">;</span></pre>

<p>
	ستعيد قاعدة البيانات قائمة السيارات من الجدول <code>cars</code> مع ترتيبها حسب نوعها أولًا ثم حسب قيمتها بترتيب تنازلي ضمن نوع السيارة الواحد كما يلي:
</p>

<pre class="ipsCode">الخرج
+---------+---------------+------+-----------+
| make    | model         | year | value     |
+---------+---------------+------+-----------+
| Ferrari | SF90 Stradale | 2020 | 627000.00 |
| Ferrari | F8 Tributo    | 2019 | 375000.00 |
| Ferrari | 812 Superfast | 2017 | 335300.00 |
| Ferrari | GTC4Lusso     | 2016 | 268000.00 |
| Ferrari | 488 GTB       | 2015 | 254750.00 |
| Porsche | 911 GT3       | 2020 | 169700.00 |
| Porsche | Cayman GT4    | 2018 | 118000.00 |
| Porsche | Panamera      | 2022 | 113200.00 |
| Porsche | 718 Boxster   | 2017 |  48880.00 |
| Porsche | Macan         | 2019 |  27400.00 |
+---------+---------------+------+-----------+
10 rows in set (0.00 sec)
</pre>

<p>
	نلاحظ ظهور سيارة الفيراري الأعلى قيمة في أعلى القائمة، وظهور سيارة البورش الأدنى قيمة في الأسفل.
</p>

<p>
	لنفترض استخدام هذا الاستعلام بصورة متكررة في تطبيقات متعددة أو سيستخدمه مستخدمون متعددون ونريد التأكّد من أن الجميع سيستخدمون الطريقة نفسها لترتيب النتائج، لذا يجب إنشاء إجراء مخزن يحفظ تعليمة هذا الاستعلام ضمن إجراء مُسمَّى قابل لإعادة الاستخدام من خلال تنفيذ جزء الشيفرة البرمجية التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2968_21" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> DELIMITER </span><span class="com">//</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE PROCEDURE get_all_cars</span><span class="pun">()</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">BEGIN</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     SELECT </span><span class="pun">*</span><span class="pln"> FROM cars ORDER BY make</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">value</span><span class="pln"> DESC</span><span class="pun">;</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">END</span><span class="pln"> </span><span class="com">//</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> DELIMITER </span><span class="pun">;</span></pre>

<p>
	نلاحظ أن الأمرين الأول <code>DELIMITER //‎</code> والأخير <code>DELIMITER ;‎</code> يخبران MySQL بالتوقف عن التعامل مع محرف الفاصلة المنقوطة بوصفه محدِّدًا للتعليمات طوال مدة إنشاء الإجراء كما هو موضّح في القسم السابق.
</p>

<p>
	يتبع أمر SQL الذي هو <code>CREATE PROCEDURE</code> اسم الإجراء <code>get_all_cars</code> الذي يمكن تعريفه لوصف ما يفعله الإجراء، ثم يوجد زوج من الأقواس <code>()</code> يمكننا إضافة معاملات ضمنه، ولكن لا يستخدم هذا الإجراء معاملات في مثالنا، لذا ستكون الأقواس فارغة، ثم تُكتَب تعليمة <code>SELECT</code> نفسها المُستخدَمة سابقًا بين الأمرين <code>BEGIN</code> و <code>END</code> اللذين يحددان بداية ونهاية كتلة شيفرة الإجراء البرمجية.
</p>

<p>
	<strong>ملاحظة</strong>: قد يظهر الخطأ <code>ERROR 1044 (42000): Access denied for user 'user'@'localhost' to database 'procedures'‎</code> عند تنفيذ الأمر <code>CREATE PROCEDURE</code> بناءً على أذونات مستخدم MySQL الخاص بنا. يمكن منح الأذونات اللازمة لإنشاء وتنفيذ الإجراءات المخزنة للمستخدم من خلال تسجيل الدخول إلى MySQL كمستخدم جذر وتنفيذ الأوامر التالية وتغيير اسم مستخدم MySQL والمضيف حسب الحاجة:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2968_23" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> GRANT CREATE ROUTINE</span><span class="pun">,</span><span class="pln"> ALTER ROUTINE</span><span class="pun">,</span><span class="pln"> EXECUTE on </span><span class="pun">*.*</span><span class="pln"> TO </span><span class="str">'user'</span><span class="pun">@</span><span class="str">'localhost'</span><span class="pun">;</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> FLUSH PRIVILEGES</span><span class="pun">;</span></pre>

<p>
	نحدّث أذونات المستخدم، ثم نسجّل الخروج كمستخدم جذر، ونسجّل الدخول مرة أخرى كمستخدم عادي، ثم نعيد تشغيل تعليمة <code>CREATE PROCEDURE</code>.
</p>

<p>
	يمكن معرفة المزيد حول تطبيق الأذونات الخاصة بالإجراءات المخزنة لمستخدمي قاعدة البيانات في <a href="https://dev.mysql.com/doc/refman/8.0/en/stored-routines-privileges.html" rel="external nofollow" target="_blank">توثيق MySQL الرسمي</a> الخاص بصلاحيات MySQL والبرامج المُخزَّنة.
</p>

<p>
	ستستجيب قاعدة البيانات برسالة النجاح التالية:
</p>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.02 sec)
</pre>

<p>
	أصبح الإجراء <code>get_all_cars</code> الآن محفوظًا في قاعدة البيانات، وستُنفَّذ التعليمة المحفوظة كما هي عند استدعائه. يمكن تنفيذ الإجراءات المخزَّنة المحفوظة من خلال استخدام أمر SQL الذي هو <code>CALL</code> متبوعًا باسم الإجراء. نجرّب الآن تشغيل الإجراء الذي أنشأناه كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2968_26" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CALL get_all_cars</span><span class="pun">;</span></pre>

<p>
	نحتاج اسم الإجراء <code>get_all_cars</code> فقط لاستخدام هذا الإجراء، إذ لم تَعُد بحاجة إلى كتابة أيّ جزء من تعليمة <code>SELECT</code> التي استخدمناها سابقًا يدويًا، وستعرض قاعدة البيانات النتائج مثل خرج التعليمة <code>SELECT</code> التي نفّذتها سابقًا كما يلي:
</p>

<pre class="ipsCode">الخرج
+---------+---------------+------+-----------+
| make    | model         | year | value     |
+---------+---------------+------+-----------+
| Ferrari | SF90 Stradale | 2020 | 627000.00 |
| Ferrari | F8 Tributo    | 2019 | 375000.00 |
| Ferrari | 812 Superfast | 2017 | 335300.00 |
| Ferrari | GTC4Lusso     | 2016 | 268000.00 |
| Ferrari | 488 GTB       | 2015 | 254750.00 |
| Porsche | 911 GT3       | 2020 | 169700.00 |
| Porsche | Cayman GT4    | 2018 | 118000.00 |
| Porsche | Panamera      | 2022 | 113200.00 |
| Porsche | 718 Boxster   | 2017 |  48880.00 |
| Porsche | Macan         | 2019 |  27400.00 |
+---------+---------------+------+-----------+
10 rows in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)
</pre>

<p>
	نجحنا في إنشاء إجراء مخزَّن بدون معاملات، حيث يعيد هذا الإجراء جميع السيارات من الجدول <code>cars</code> مرتبةً بطريقة معينة، ويمكن استخدام هذا الإجراء في تطبيقات متعددة.
</p>

<p>
	سننشئ في القسم التالي إجراء يقبل المعاملات لتغيير سلوك الإجراء وفقًا لدخل المستخدم.
</p>

<h2 id="-2">
	إنشاء إجراء مخزن مع معامل دخل
</h2>

<p>
	سنضمِّن في هذا القسم معاملات دخل في تعريف الإجراء المخزَّن للسماح للمستخدمين الذين ينفّذون الإجراء بتمرير البيانات إليه، فمثلًا يمكن للمستخدمين توفير مرشّحات للاستعلام.
</p>

<p>
	يسترجع الإجراء المخزن <code>get_all_cars</code> الذي أنشأناه مسبقًا جميع السيارات من الجدول <code>cars</code> المصنعة في جميع سنوات التصنيع، ولننشئ الآن إجراء آخر للعثور على السيارات المُصنَّعة في سنة معينة، حيث سنعرِّف معاملًا في تعريف الإجراء من خلال تشغيل الشيفرة البرمجية التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2968_28" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> DELIMITER </span><span class="com">//</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE PROCEDURE get_cars_by_year</span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     IN year_filter </span><span class="kwd">int</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">BEGIN</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     SELECT </span><span class="pun">*</span><span class="pln"> FROM cars WHERE year </span><span class="pun">=</span><span class="pln"> year_filter ORDER BY make</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">value</span><span class="pln"> DESC</span><span class="pun">;</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">END</span><span class="pln"> </span><span class="com">//</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> DELIMITER </span><span class="pun">;</span></pre>

<p>
	توجد العديد من التغييرات على شيفرة إنشاء الإجراء مقارنة بالشيفرة المستخدمة في القسم السابق، حيث تغيّر الاسم ليكون <code>get_cars_by_year</code> ليمثل عمل الإجراء، وهو استرجاع السيارات بناءً على سنة إصدارها. كما أصبحت الأقواس الفارغة سابقًا محتوية على تعريف معامل واحد هو <code>IN year_filter int</code>، حيث تخبر الكلمة المفتاحية <code>IN</code> قاعدة البيانات بأن المستخدم المستدعِي سيمرّر المعامل إلى الإجراء. يُعَد <code>year_filter</code> اسمًا عشوائيًا للمعامل، حيث سنستخدمه للإشارة إلى المعامل في شيفرة الإجراء البرمجية، و <code>int</code> هو نوع البيانات، حيث نمثّل سنة التصنيع بقيمة عددية. يظهر المعامل <code>year_filter</code> المُعرَّف بعد اسم الإجراء في تعليمة <code>SELECT</code> ضمن التعليمة <code>WHERE year = year_filter</code>، مما يؤدي إلى ترشيح الجدول <code>cars</code> وفقًا لسنة التصنيع، وستستجيب قاعدة البيانات برسالة النجاح التالية:
</p>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.02 sec)
</pre>

<p>
	نفّذ الإجراء بدون تمرير أي معاملات إليه كما فعلنا سابقًا:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6807_17" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CALL get_cars_by_year</span><span class="pun">;</span></pre>

<p>
	وستعيد قاعدة بيانات MySQL رسالة الخطأ التالية:
</p>

<pre class="ipsCode">رسالة خطأ
ERROR 1318 (42000): Incorrect number of arguments for PROCEDURE procedures.get_cars_by_year; expected 1, got 0
</pre>

<p>
	يتوقع الإجراء المخزن هذه المرة توفير معامل له، ولكن لم نقدّم له أيّ معامل، لذا يمكننا استدعاء إجراء مخزَّن مع معاملات من خلال توفير قيم المعاملات بين قوسين بنفس الترتيب الذي يتوقعه الإجراء. سننفّذ الإجراء التالي لاسترجاع السيارات المصنعة في عام 2017:
</p>

<pre class="ipsCode">mysql&gt; CALL get_cars_by_year(2017);
</pre>

<p>
	سيُنفَّذ الإجراء المستدعَى الآن تنفيذًا صحيحًا ويعيد قائمة السيارات من عام 2017، وسينتج الخرج التالي:
</p>

<pre class="ipsCode">الخرج
+---------+---------------+------+-----------+
| make    | model         | year | value     |
+---------+---------------+------+-----------+
| Ferrari | 812 Superfast | 2017 | 335300.00 |
| Porsche | 718 Boxster   | 2017 |  48880.00 |
+---------+---------------+------+-----------+
2 rows in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)
</pre>

<p>
	تعلّمنا في المثال السابق كيفية تمرير معاملات الدخل إلى الإجراءات المخزنة واستخدامها في الاستعلامات ضمن الإجراء لتوفير خيارات الترشيح، وسنستخدم في القسم التالي معاملات الخرج لإنشاء إجراءات تعيد قيمًا مختلفة متعددة في تنفيذ واحد.
</p>

<h2 id="-3">
	إنشاء إجراء مخزن مع معاملات دخل وخرج
</h2>

<p>
	في الإجراءات المخزَّنة التي أنشأناها في المثالين السابقين استدعينا تعليمة <code>SELECT</code> للحصول على مجموعة نتائج، ولكن قد نحتاج في بعض الحالات إلى إجراء مخزّن يعيد قيمًا مختلفة متعددة مع بعضها البعض بدل إعادة مجموعة نتائج واحدة لاستعلام فردي.
</p>

<p>
	لنفترض أننا تريد إنشاء إجراء يوفّر معلومات عن السيارات الصادرة في سنة معينة بما في ذلك كمية السيارات في المجموعة وقيمتها السوقية -الحد الأدنى والحد الأقصى والمتوسط- من خلال استخدام معاملات <code>OUT</code> عند إنشاء إجراء مخزّن جديد. 
</p>

<p>
	تحتوي معاملات <code>OUT</code> مثل معاملات <code>IN</code> على أسماء وأنواع بيانات مرتبطة بها، ولكن يمكن ملء هذه المعاملات بالبيانات باستخدام الإجراء المخزن بدل تمرير البيانات إلى الإجراء المخزن لإعادة القيم إلى المستخدم المستدعِي.
</p>

<p>
	لننشئ الآن الإجراء <code>get_car_stats_by_year</code> التالي الذي سيعيد بيانات موجزة عن السيارات من سنة إنتاج معينة باستخدام معاملات خرج:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6807_7" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> DELIMITER </span><span class="com">//</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE PROCEDURE get_car_stats_by_year</span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     IN year_filter </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     OUT cars_number </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     OUT min_value </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     OUT avg_value </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     OUT max_value </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">BEGIN</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     SELECT COUNT</span><span class="pun">(*),</span><span class="pln"> MIN</span><span class="pun">(</span><span class="kwd">value</span><span class="pun">),</span><span class="pln"> AVG</span><span class="pun">(</span><span class="kwd">value</span><span class="pun">),</span><span class="pln"> MAX</span><span class="pun">(</span><span class="kwd">value</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     INTO cars_number</span><span class="pun">,</span><span class="pln"> min_value</span><span class="pun">,</span><span class="pln"> avg_value</span><span class="pun">,</span><span class="pln"> max_value
mysql</span><span class="pun">&gt;</span><span class="pln">     FROM cars
mysql</span><span class="pun">&gt;</span><span class="pln">     WHERE year </span><span class="pun">=</span><span class="pln"> year_filter ORDER BY make</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">value</span><span class="pln"> DESC</span><span class="pun">;</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">END</span><span class="pln"> </span><span class="com">//</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> DELIMITER </span><span class="pun">;</span></pre>

<p>
	استخدمنا معامل <code>IN</code> الذي هو <code>year_filter</code> لترشيح السيارات حسب سنة الإصدار، وعرّفنا أربعة معاملات <code>OUT</code> ضمن كتلة الأقواس. نمثّل المعامل <code>cars_number</code> بنوع البيانات <code>int</code> وسنستخدمه لإعادة عدد السيارات في المجموعة، وتمثّل المعاملات <code>min_value</code> و <code>avg_value</code> و <code>max_value</code> القيمة السوقية وتُعرَّف باستخدام نوع البيانات <code>decimal(10, 2)‎</code> مثل العمود <code>value</code> في الجدول <code>cars</code>، وتُستخدَم هذه المعاملات لإعادة معلومات حول أرخص وأغلى السيارات من المجموعة، بالإضافة إلى متوسط أسعار جميع السيارات المطابقة.
</p>

<p>
	تستعلم التعليمة <code>SELECT</code> عن أربع قيم من الجدول <code>cars</code> باستخدام دوال SQL الرياضية وهي: <code>COUNT</code> للحصول على العدد الإجمالي للسيارات، و <code>MIN</code> و <code>AVG</code> و <code>MAX</code> للحصول على القيمة الدنيا والمتوسط والقيمة العليا من العمود <code>value</code>.<strong> </strong>يمكن مطالعة مقال<a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%AC%D9%85%D9%8A%D8%B9%D9%8A%D8%A9-%D9%81%D9%8A-sql-r2410/" rel=""> كيفية استخدام التعابير الرياضية والدوال التجميعية في لغة SQL</a> لمعرفة المزيد حول استخدام الدوال الرياضية في لغة SQL.
</p>

<p>
	يمكننا إخبار قاعدة البيانات بأننا نريد تخزين نتائج هذا الاستعلام في معاملات الخرج للإجراء المخزَّن من خلال تقديم كلمة مفتاحية جديدة هي <code>INTO</code>، ونضع بعدها أسماء أربعة معاملات إجراء تقابل البيانات المُسترجَعة، وبالتالي سيحفظ MySQL قيمة <code>COUNT(*)‎</code> في المعامل <code>cars_number</code>، ونتيجة <code>MIN(value)‎</code> في المعامل <code>min_value</code> ...إلخ.
</p>

<p>
	تؤكد قاعدة البيانات إنشاء الإجراء بنجاح كما يلي:
</p>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.02 sec)
</pre>

<p>
	لنشغّل الآن الإجراء الجديد من خلال تنفيذ الأمر التالي:
</p>

<pre class="ipsCode">mysql&gt; CALL get_car_stats_by_year(2017, @number, @min, @avg, @max);
</pre>

<p>
	تبدأ المعاملات الأربعة الجديدة بالإشارة <code>@</code>، وهي أسماء متغيرات محلية في طرفية MySQL يمكنك استخدامها لتخزين البيانات مؤقتًا، وإذا مرّرنا هذه المعاملات إلى الإجراء المخزَّن الذي أنشأناه، فسيدرج الإجراءُ قيمًا في هذه المتغيرات.
</p>

<p>
	وستستجيب قاعدة البيانات بالخرج التالي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.00 sec)
</pre>

<p>
	يختلف هذا الخرج عن السلوك السابق، حيث كانت النتائج تُعرَض على الشاشة مباشرةً، لأن نتائج الإجراء المخزَّن محفوظة في معاملات الخرج دون إعادتها كنتيجة للاستعلام، ولكن يمكننا الوصول إلى النتائج من خلال استخدام التعليمة <code>SELECT</code> مباشرةً في صدفة MySQL كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6807_10" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="lit">@number</span><span class="pun">,</span><span class="pln"> </span><span class="lit">@min</span><span class="pun">,</span><span class="pln"> </span><span class="lit">@avg</span><span class="pun">,</span><span class="pln"> </span><span class="lit">@max</span><span class="pun">;</span></pre>

<p>
	نحدّد قيمًا من المتغيرات المحلية باستخدام الاستعلام السابق، ولا نستدعي الإجراء مرة أخرى، ويحفظ الإجراء المخزَّن نتائجه في تلك المتغيرات، وستبقى البيانات متاحة حتى قطع الاتصال بالصدفة.
</p>

<p>
	<strong>ملاحظة</strong>: يمكن مطالعة على قسم <a href="https://dev.mysql.com/doc/refman/5.7/en/user-variables.html" rel="external nofollow" target="_blank">المتغيرات التي يعرِّفها المستخدم</a> في توثيق MySQL الرسمي لمعرفة المزيد حول استخدام هذه المتغيرات. ستختلف طرق الوصول إلى البيانات المُعادة من الإجراءات المخزنة في <a href="https://academy.hsoub.com/programming/general/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a> وأطر العمل المختلفة عند استخدامها في تطوير التطبيقات، لذا يتوجب الاطلاع على توثيق اللغة وإطار العمل لمعرفة الطريقة المناسبة.
</p>

<p>
	يعرض الخرج قيم المتغيرات التي استعلمنا عنها كما يلي:
</p>

<pre class="ipsCode">الخرج
+---------+----------+-----------+-----------+
| @number | @min     | @avg      | @max      |
+---------+----------+-----------+-----------+
|       2 | 48880.00 | 192090.00 | 335300.00 |
+---------+----------+-----------+-----------+
1 row in set (0.00 sec)
</pre>

<p>
	تتوافق القيم مع عدد السيارات المُصنَّعة في عام 2017، والقيمة السوقية الدنيا والمتوسطة والعليا للسيارات في هذه السنة من الإنتاج.
</p>

<p>
	تعلّمنا في المثال السابق كيفية استخدام معاملات الخرج لإعادة قيم مختلفة متعددة من الإجراء المخزن لاستخدامها لاحقًا، وسنتعلّم في القسم التالي كيفية إزالة الإجراءات التي أنشأناها.
</p>
<iframe allowfullscreen="" class="ipsEmbed_finishedLoading" data-controller="core.front.core.autosizeiframe" data-embedauthorid="3889" data-embedcontent="" data-embedid="embed4589320706" src="https://academy.hsoub.com/files/16-%D9%85%D9%84%D8%A7%D8%AD%D8%B8%D8%A7%D8%AA-%D9%84%D9%84%D8%B9%D8%A7%D9%85%D9%84%D9%8A%D9%86-%D8%A8%D9%84%D8%BA%D8%A9-sql/?do=embed" style="overflow: hidden; height: 468px; max-width: 500px; margin: auto;"></iframe>

<h2 id="-4">
	إزالة الإجراءات المخزنة
</h2>

<p>
	سنزيل في هذا القسم الإجراءات المخزنة الموجودة في قاعدة البيانات، فقد لا تكون هناك حاجة إلى الإجراء الذي أنشأناه في بعض الأحيان، أو قد نرغب في تغيير طريقة عمل الإجراء، حيث لا يسمح MySQL بتغيير تعريف الإجراء بعد إنشائه، فالطريقة الوحيدة لذلك هي إزالة الإجراء أولًا وإعادة إنشائه مرة أخرى مع التغييرات المطلوبة.
</p>

<p>
	لنحذف الآن الإجراء الأخير <code>get_car_stats_by_year</code> باستخدام التعليمة <code>DROP PROCEDURE</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6807_13" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> DROP PROCEDURE get_car_stats_by_year</span><span class="pun">;</span></pre>

<p>
	وستؤكد قاعدة البيانات حذف الإجراء برسالة النجاح التالية:
</p>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.02 sec)
</pre>

<p>
	يمكن التحقق من حذف الإجراء من خلال محاولة استدعائه باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6807_15" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CALL get_car_stats_by_year</span><span class="pun">(</span><span class="lit">2017</span><span class="pun">,</span><span class="pln"> </span><span class="lit">@number</span><span class="pun">,</span><span class="pln"> </span><span class="lit">@min</span><span class="pun">,</span><span class="pln"> </span><span class="lit">@avg</span><span class="pun">,</span><span class="pln"> </span><span class="lit">@max</span><span class="pun">);</span></pre>

<p>
	سنرى رسالة خطأ تفيد بأن الإجراء غير موجود في قاعدة البيانات كما يلي:
</p>

<pre class="ipsCode">رسالة خطأ
ERROR 1305 (42000): PROCEDURE procedures.get_car_stats_by_year does not exist
</pre>

<p>
	تعلمنا في هذا القسم كيفية حذف الإجراءات المخزنة الموجودة في قاعدة البيانات.
</p>

<h2 id="-5">
	الخلاصة
</h2>

<p>
	تعلمنا في هذا المقال ما هي الإجراءات المخزنة وأنواعها المختلفة وكيفية استخدامها في MySQL لحفظ البيانات القابلة لإعادة الاستخدام في إجراءات مسمَّاة وتنفيذها لاحقًا، يمكن استخدام الإجراءات المخزنة لإنشاء برامج قابلة لإعادة الاستخدام، وتوحيد طرق الوصول إلى البيانات عبر تطبيقات متعددة، بالإضافة إلى تنفيذ سلوكيات معقدة تتجاوز الإمكانيات التي توفرها استعلامات SQL الفردية. غطى هذا المقال فقط أساسيات استخدام الإجراءات المخزنة، لذا لمزيد من المعلومات ننصح بالاطلاع على <a href="https://dev.mysql.com/doc/refman/8.0/en/create-procedure.html" rel="external nofollow" target="_blank">توثيق MySQL للإجراءات المخزنة</a> لمعرفة مزيد من التفاصيل.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-stored-procedures-in-mysql" rel="external nofollow" target="_blank">How To Use Stored Procedures in MySQL</a> لصاحبَيه Mateusz Papiernik و Rachel Lee.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق:<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%81%D8%A7%D8%AA%D9%8A%D8%AD-%D8%A7%D9%84%D8%B1%D8%A6%D9%8A%D8%B3%D9%8A%D8%A9-primary-keys-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-sql-r2528/" rel=""> استخدام المفاتيح الرئيسية Primary Keys في لغة SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D9%81%D8%B1%D8%B9%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%A5%D8%AC%D8%B1%D8%A7%D8%A1%D8%A7%D8%AA-%D9%81%D9%8A-sql-r858/" rel="">الاستعلامات الفرعية والإجراءات في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%AC%D9%84%D8%A8-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85%D8%A7%D8%AA-%D8%B9%D8%A8%D8%B1-select-%D9%81%D9%8A-sql-r845/" rel="">جلب الاستعلامات عبر SELECT في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%AD%D8%B0%D9%81-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%88%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r852/" rel="">حذف الجداول وقواعد البيانات في SQL</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2536</guid><pubDate>Mon, 17 Mar 2025 12:07:02 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x645;&#x641;&#x627;&#x62A;&#x64A;&#x62D; &#x627;&#x644;&#x631;&#x626;&#x64A;&#x633;&#x64A;&#x629; Primary Keys &#x641;&#x64A; &#x644;&#x63A;&#x629; SQL</title><link>https://academy.hsoub.com/programming/sql/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%81%D8%A7%D8%AA%D9%8A%D8%AD-%D8%A7%D9%84%D8%B1%D8%A6%D9%8A%D8%B3%D9%8A%D8%A9-primary-keys-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-sql-r2528/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_03/SQL.png.30e2399652fa689be0535ccf7aa365b4.png" /></p>
<p>
	تتميز قواعد البيانات العلاقية Relational Databases بكونها تهيكل البيانات ضمن بنية منظّمة، فهي تستخدم جداول ذات أعمدة ثابتة وتتبع أنواع بيانات مُعرَّفة بدقة وتضمن بأن جميع الصفوف لها الشكل نفسه. ومن المهم أن نكون قادرين في هذه البينة على العثور على الصفوف في الجداول والإشارة إليها دون التباس عند تخزين هذه البيانات ضمن صفوف الجداول. ويمكننا تحقيق ذلك في لغة الاستعلام البنيوية SQL باستخدام المفاتيح الرئيسية Primary Keys، والتي تعمل كمعرّفات مميزة للصفوف في جداول قاعدة البيانات العلاقية.
</p>

<p>
	سنتعرّف في هذا المقال على مفهوم المفاتيح الرئيسية، وكيفية استخدام أنواعها المختلفة لتحديد الصفوف الفريدة في جداول قاعدة البيانات، وسننشئ مفاتيح رئيسية من أعمدة فردية وأعمدة متعددة، كما سننشئ مفاتيح تسلسلية مع زيادة تلقائية باستخدام بعض البيانات التجريبية النموذجية.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	يجب أن يكون لدينا حاسوب يشغّل نظام إدارة قواعد البيانات العلاقية Relational Database Management System -أو RDBMS اختصارًا- مستند إلى لغة SQL. وقد اختبرنا التعليمات والأمثلة الواردة في هذا المقال باستخدام البيئة التالية:
</p>

<ul>
	<li>
		خادم عامل على توزيعة أوبنتو مع مستخدم بصلاحيات مسؤول مختلف عن المستخدم الجذر وجدار حماية مضبوط باستخدام أداة UFW كما هو موضح في <a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04" rel="external nofollow">دليل الإعداد الأولي للخادم مع الإصدار 20.04 من أوبنتو</a>
	</li>
	<li>
		نظام MySQL مُثبَّت ومؤمَّن على الخادم كما هو موضح في مقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبنتو</a>، وقد نفذنا خطوات هذا المقال باستخدام مستخدم MySQL مختلف عن المستخدم الجذر
	</li>
	<li>
		معرفة أساسية بتنفيذ استعلامات <code>SELECT</code> كما هو موضّح في مقال <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%B9%D9%86-%D8%A7%D9%84%D8%B3%D8%AC%D9%84%D8%A7%D8%AA-%D9%85%D9%86-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r2249/" rel="">كيفية الاستعلام عن السجلات من الجداول في SQL</a>
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: تجدر الإشارة لأنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية RDBMS لها تقديماتها الفريدة من لغة SQL، إذ ستعمل الأوامر في هذا المقال بنجاح مع معظم هذه الأنظمة، تُعَد المفاتيح الرئيسية جزءًا من معيار SQL، ولكن هناك بعض الميزات خاصة بقاعدة البيانات، لذا قد نجد بعض الاختلافات في الصيغة أو الناتج عند اختبارها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<p>
	سنحتاج أيضًا إلى قاعدة بيانات فارغة يمكن من خلالها إنشاء جداول توضّح استخدام المفاتيح الرئيسية كما سنشرح بالتفصيل في الفقرات التالية.
</p>

<h2 id="mysql">
	الاتصال بخادم MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	سنتصل في هذا القسم بخادم MySQL وننشئ قاعدة بيانات تجريبية. إذا كان نظام قاعدة بيانات SQL الخاص بنا يعمل على خادم بعيد، نتصل بالخادم باستخدام بروتوكول <a href="https://academy.hsoub.com/devops/security/ssh/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr></a> من جهازنا المحلي كما يلي:
</p>

<pre class="ipsCode">$ ssh user@your_server_ip
</pre>

<p>
	ثم نفتح واجهة سطر أوامر خادم MySQL مع وضع اسم حساب مستخدم MySQL الخاص بك مكان <code>user</code>:
</p>

<pre class="ipsCode">$ mysql -u user -p
</pre>

<p>
	ننشئ قاعدة بيانات بالاسم <code>primary_keys</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_11" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE DATABASE primary_keys</span><span class="pun">;</span></pre>

<p>
	إذا أُنشئِت قاعدة البيانات بنجاح، فسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	يمكن اختيار قاعدة البيانات <code>primary_keys</code> من خلال تنفيذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_13" style=""><span class="pln">$ USE primary_keys</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	اخترنا قاعدة البيانات، ويمكن الآن إنشاء جداول تجريبية ضمنها، وبذلك أصبحنا جاهزًا لمتابعة هذا المقال والبدء في العمل مع المفاتيح الرئيسية.
</p>

<h2 id="primarykeys">
	مقدمة إلى المفاتيح الرئيسية Primary Keys
</h2>

<p>
	تُخزَّن البيانات في قاعدة البيانات العلاقية في جداول ذات بنية موحَّدة ومحدَّدة من الصفوف الفردية، ويوضّح تعريف الجدول الأعمدة الموجودة فيه وأنواع البيانات التي يمكن حفظها في الأعمدة الفردية، وهذا كافي لتخزين المعلومات في قاعدة البيانات والعثور عليها لاحقًا باستخدام معايير ترشيح مختلفة باستخدام تعليمة <code>WHERE</code>، ولكن لا تضمن هذه البنية إمكانية العثور على أي صف بعينه دون التباس مع صفوف أخرى.
</p>

<p>
	لنفترض أن لدينا قاعدة بيانات لجميع السيارات المسجَّلة المسموح لها بالقيادة على الطرق العامة، حيث ستحتوي قاعدة البيانات على معلومات مثل نوع السيارة وطرازها وسنة تصنيعها ولون طلائها، ولكن إذا بحثنا عن سيارة شيفروليه كامارو Chevrolet Camaro حمراء اللون ومصنوعة في عام 2007، فيمكن العثور على أكثر من سيارة، إذ سيبيع المصنعون سيارات متماثلة لعملاء متعددين، لذا تحتوي السيارات المُسجَّلة على أرقام لوحات ترخيص تحدّد كل سيارة فريدة. إذا بحثنا عن سيارة تحمل لوحة الترخيص <code>OFP857</code>، فيمكن التأكد من أن هذا المعيار سيجد سيارة واحدة فقط، لأن أرقام اللوحات تحدد السيارات المسجلة بطريقة فريدة قانونيًا، ويسمى هذا الجزء من البيانات في قاعدة البيانات العلاقية بالمفتاح الرئيسي Primary Key.
</p>

<p>
	المفاتيح الرئيسية هي معرّفات فريدة موجودة في عمود واحد أو مجموعة من الأعمدة يمكنها تحديد كل صف في جدول قاعدة البيانات دون التباس. ومن أبرز خصائص المفاتيح الرئيسية نذكر:
</p>

<ul>
	<li>
		يجب أن يستخدم المفتاح الرئيسي قيمًا فريدة، وإذا تكوّن المفتاح الرئيسي من أكثر من عمود واحد، فيجب أن تكون مجموعة القيم في هذه الأعمدة فريدة في الجدول بأكمله، إذ لا يمكن أن يظهر المفتاح الرئيسي أكثر من مرة لأنه مخصّص لتحديد كل صف بطريقة فريدة
	</li>
	<li>
		يجب ألا يحتوي المفتاح الرئيسي على قيم <code>NULL</code>
	</li>
	<li>
		يمكن لكل جدول في قاعدة البيانات استخدام مفتاح رئيسي واحد فقط
	</li>
</ul>

<p>
	يفرض محرّك قاعدة البيانات هذه القواعد، لذا يمكنك الوثوق بصحة هذه الخاصيات عند تعريف المفتاح الرئيسي لجدول ما، ويجب أن تضع في بالنا محتوى البيانات وما الذي يمكن اختياره منها لتمثيل المفتاح الرئيسي بشكل مناسب. المفاتيح الطبيعية Natural keys هي معرّفات موجودة مسبقًا في مجموعة البيانات، والمفاتيح البديلة Surrogate Keys هي معرّفات اصطناعية، ويمكن مطالعة مقال <a href="https://academy.hsoub.com/programming/sql/%D9%81%D9%87%D9%85-%D9%82%D9%8A%D9%88%D8%AF-sql-r2217/" rel="">فهم قيود SQL</a> لمزيد من التفاصيل.
</p>

<p>
	تحتوي بعض هياكل البيانات على مفاتيح رئيسية تظهر في مجموعة البيانات طبيعيًا مثل أرقام لوحات الترخيص في قاعدة بيانات السيارات أو أرقام الضمان الاجتماعي في دليل المواطنين، ولا تكون هذه المعرّفات مؤلَّفة من قيمة واحدة بل مؤلفة من زوج أو مجموعة من عدة قيم في بعض الأحيان، فمثلًا لا يمكن تحديد منزل فريد باستخدام اسم الشارع أو رقم الشارع فقط في دليل مدينة محلية للمنازل، إذ يمكن أن يكون هناك عدة منازل في شارع واحد، ويمكن أن يظهر الرقم نفسه في شوارع متعددة، ولكن يمكن افتراض أن كلًا من اسم الشارع ورقمه هو معرّف منزل فريد. تسمّى هذه المعرّفات بالمفاتيح الطبيعية.
</p>

<p>
	لكن لا يمكن في بعض الأحيان تمييز البيانات تمييزًا فريدًا باستخدام قيم عمود واحد أو مجموعة فرعية صغيرة من الأعمدة، وبالتالي يجب إنشاء مفاتيح رئيسية اصطناعية باستخدام تسلسل من الأرقام مثلًا أو معرّفات مُولَّدة عشوائيًا مثل معرّفات UUID، وتسمّى هذه المفاتيح بالمفاتيح البديلة.
</p>

<p>
	سننشئ في الأقسام التالية مفاتيح طبيعية بناءً على عمود واحد أو أعمدة متعددة، كما سنولّد مفاتيح بديلة للجداول التي لا يكون المفتاح الطبيعي خيارًا فيها.
</p>

<h2 id="-1">
	إنشاء مفتاح رئيسي من عمود واحد
</h2>

<p>
	تحتوي مجموعة البيانات على عمود واحد طبيعيًا في العديد من الحالات، ويمكن استخدام هذا العمود لتحديد الصفوف في الجدول تحديدًا فريدًا، وبالتالي يمكن إنشاء مفتاح طبيعي لوصف هذه البيانات. لنفترض أن لدينا الجدول التالي باتباع المثال السابق لقاعدة بيانات السيارات المُسجَّلة:
</p>

<pre class="ipsCode">جدول بسيط
+---------------+-----------+------------+-------+------+
| license_plate | brand     | model      | color | year |
+---------------+-----------+------------+-------+------+
| ABC123        | Ford      | Mustang    | Red   | 2018 |
| CES214        | Ford      | Mustang    | Red   | 2018 |
| DEF456        | Chevrolet | Camaro     | Blue  | 2016 |
| GHI789        | Dodge     | Challenger | Black | 2014 |
+---------------+-----------+------------+-------+------+
</pre>

<p>
	يمثّل الصف الأول والثاني سيارة فورد موستانج Ford Mustang حمراء مصنوعة في عام 2018، ولن تتمكّن من تحديد السيارة بطريقة فريدة باستخدام نوع السيارة وطرازها فقط، لذا تختلف لوحة الترخيص لكل سيارة في كلتا الحالتين، ممّا يوفّر معرّفًا فريدًا جيدًا لكل صف في الجدول. يُعَد رقم لوحة الترخيص جزءًا من البيانات، لذا يؤدي استخدامه بوصفه مفتاحًا رئيسيًا إلى إنشاء مفتاح طبيعي. إذا أنشأنا الجدول دون استخدام مفتاح رئيسي في العمود <code>license_plate</code>، فستخاطر بظهور لوحة مكررة أو فارغة في مجموعة البيانات لاحقًا.
</p>

<p>
	لننشئ الآن جدولًا يشبه الجدول السابق مع استخدام العمود <code>license_plate</code> بوصفه مفتاحًا رئيسيًا مع الأعمدة التالية:
</p>

<ul>
	<li>
		<code>license_plate</code>: رقم لوحة الترخيص، ونمثله باستخدام نوع البيانات <code>varchar</code>
	</li>
	<li>
		<code>brand</code>: نوع السيارة، ونمثّله باستخدام نوع البيانات <code>varchar</code> بحد أقصى 50 محرفًا
	</li>
	<li>
		<code>model</code>: طراز السيارة، ونمثّله باستخدام نوع البيانات <code>varchar</code> بحد أقصى 50 محرفًا
	</li>
	<li>
		<code>color</code>: لون السيارة، ونمثّله باستخدام نوع البيانات <code>varchar</code> بحد أقصى 20 محرفًا
	</li>
	<li>
		<code>year</code>: سنة تصنيع السيارة، ونمثّله باستخدام نوع البيانات <code>int</code> لتخزين البيانات العددي.
	</li>
</ul>

<p>
	ولننشئ الآن الجدول <code>cars</code> من خلال تنفيذ تعليمة SQL التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_15" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE cars </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     license_plate varchar</span><span class="pun">(</span><span class="lit">8</span><span class="pun">)</span><span class="pln"> PRIMARY KEY</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     brand varchar</span><span class="pun">(</span><span class="lit">50</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     model varchar</span><span class="pun">(</span><span class="lit">50</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     color varchar</span><span class="pun">(</span><span class="lit">20</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     year </span><span class="kwd">int</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	تكون تعليمة <code>PRIMARY KEY</code> بعد تعريف نوع بيانات <code>license_plate</code>، حيث يمكنك استخدام الصيغة المبسَّطة لإنشاء المفتاح وكتابة <code>PRIMARY KEY</code> في تعريف العمود عند التعامل مع المفاتيح الرئيسية المستندة إلى أعمدة مفردة.
</p>

<p>
	إذا كان الخرج كما يلي، فهذا يعني إنشاء الجدول بنجاح:
</p>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.00 sec)
</pre>

<p>
	نحمّل بعد ذلك الجدول ببعض الصفوف التجريبية المعروضة في المثال السابق من خلال تشغيل عملية <code>INSERT INTO</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_17" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO cars VALUES
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'ABC123'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Ford'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Mustang'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Red'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2018</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'CES214'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Ford'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Mustang'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Red'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2018</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'DEF456'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Chevrolet'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Camaro'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Blue'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2016</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'GHI789'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Dodge'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Challenger'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Black'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2014</span><span class="pun">);</span></pre>

<p>
	وستستجيب قاعدة البيانات برسالة النجاح التالية:
</p>

<pre class="ipsCode">الخرج
Query OK, 4 rows affected (0.010 sec)
Records: 4  Duplicates: 0  Warnings: 0
</pre>

<p>
	يمكن الآن التحقق من أن الجدول الذي أنشأناه يحتوي على البيانات والتنسيق المتوقع باستخدام تعليمة <code>SELECT</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_19" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM cars</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج جدولًا مشابهًا للجدول الموجود في بداية هذا القسم:
</p>

<pre class="ipsCode">الخرج
+---------------+-----------+------------+-------+------+
| license_plate | brand     | model      | color | year |
+---------------+-----------+------------+-------+------+
| ABC123        | Ford      | Mustang    | Red   | 2018 |
| CES214        | Ford      | Mustang    | Red   | 2018 |
| DEF456        | Chevrolet | Camaro     | Blue  | 2016 |
| GHI789        | Dodge     | Challenger | Black | 2014 |
+---------------+-----------+------------+-------+------+
</pre>

<p>
	يمكن بعد ذلك التحقق من ضمان قواعد المفتاح الرئيسي باستخدام محرّك قاعدة البيانات، لذا نجرّب إدخال سيارة لها رقم لوحة مكرّر من خلال تنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_21" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO cars VALUES </span><span class="pun">(</span><span class="str">'DEF456'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Jeep'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Wrangler'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Yellow'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2019</span><span class="pun">);</span></pre>

<p>
	وسيستجيب MySQL برسالة الخطأ التالية التي تفيد بأن لوحة الترخيص <code>DEF456</code> تمثّل إدخالًا مكررًا للمفتاح الرئيسي:
</p>

<pre class="ipsCode">الخرج
ERROR 1062 (23000): Duplicate entry 'DEF456' for key 'cars.PRIMARY'
</pre>

<p>
	<strong>ملاحظة</strong>: تُطبَّق المفاتيح الرئيسية باستخدام الفهارس الفريدة Unique Indexes وتشترك في العديد من الخاصيات مع الفهارس التي قد ننشئها يدويًا لأعمدة أخرى في الجدول، وتعمل فهارس المفاتيح الرئيسية أيضًا على تحسين أداء الاستعلام في الجدول للعمود الذي عرّفنا الفهرس له. اطّلع على مقال <a href="https://academy.hsoub.com/programming/sql/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%81%D9%87%D8%A7%D8%B1%D8%B3-indexes-%D9%81%D9%8A-sql-r2523/" rel="">كيفية استخدام الفهارس في SQL</a> لمزيد من المعلومات.
</p>

<p>
	يمكن الآن التأكد من عدم السماح باستخدام لوحات ترخيص مكرّرة، ولنتحقق الآن من إمكانية إدخال سيارة لها لوحة ترخيص فارغة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_24" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO cars VALUES </span><span class="pun">(</span><span class="pln">NULL</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Jeep'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Wrangler'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Yellow'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2019</span><span class="pun">);</span></pre>

<p>
	وستستجيب قاعدة البيانات برسالة الخطأ التالية:
</p>

<pre class="ipsCode">الخرج
ERROR 1048 (23000): Column 'license_plate' cannot be null
</pre>

<p>
	يمكن التأكّد من أن المفتاح الرئيسي <code>license_plate</code> يحدّد كلّ صف في الجدول تحديدًا فريدًا بفضل القاعدتين السابقتين اللتين تفرضهما قاعدة البيانات، وبالتالي إذا استعلمنا في الجدول عن أي لوحة ترخيص، فسنتوقع إعادة صف واحد في كل مرة.
</p>

<p>
	نشرح في القسم التالي كيفية استخدام المفاتيح الرئيسية مع أعمدة متعددة.
</p>

<h2 id="-2">
	إنشاء مفتاح رئيسي من أعمدة متعددة
</h2>

<p>
	إن لم يكن عمود واحد كافيًا لتحديد صف فريد في الجدول، فيمكن إنشاء مفاتيح رئيسية تستخدم أكثر من عمودٍ واحد. لنفترض مثلًا وجود سجلٍ للمنازل بحيث لا يكفي اسم الشارع أو رقمه فقط لتحديد أيّ منزل فردي كما يلي:
</p>

<pre class="ipsCode">جدول بسيط
+-------------------+---------------+-------------------+------+
| street_name       | street_number | house_owner       | year |
+-------------------+---------------+-------------------+------+
| 5th Avenue        | 100           | Bob Johnson       | 2018 |
| Broadway          | 1500          | Jane Smith        | 2016 |
| Central Park West | 100           | John Doe          | 2014 |
| Central Park West | 200           | Tom Thompson      | 2015 |
| Lexington Avenue  | 5001          | Samantha Davis    | 2010 |
| Park Avenue       | 7000          | Michael Rodriguez | 2012 |
+-------------------+---------------+-------------------+------+
</pre>

<p>
	يظهر اسم الشارع <code>Central Park West</code> أكثر من مرة في الجدول، ويظهر رقم الشارع <code>100</code> أكثر من مرة أيضًا، ولكن لا تظهَر أي أزواج مكررة من أسماء الشوارع وأرقامها، وبالتالي يمكن استخدام زوج من هاتين القيمتين لتحديد كل صف في الجدول تحديدًا فريدًا في الحالة التي لا يمكن فيها لأي عمود بمفرده أن يكون مفتاحًا رئيسيًا.
</p>

<p>
	لننشئ الآن جدولًا يشبه الجدول السابق ويحتوي على الأعمدة التالية:
</p>

<ul>
	<li>
		<code>street_name</code>: اسم الشارع الذي يقع فيه المنزل، ونمثّله باستخدام نوع البيانات <code>varchar</code> بحد أقصى 50 محرفًا.
	</li>
	<li>
		<code>street_number</code><span>:</span> رقم شارع المنزل، ونمثّله باستخدام نوع البيانات <code>varchar</code>، ويمكن لهذا العمود تخزين ما يصل إلى 5 محارف، ولا يستخدم نوع البيانات العددي <code>int</code> لأن بعض أرقام الشوارع قد تحتوي على محارف مثل <code>200B</code>.
	</li>
	<li>
		<code>house_owner</code>: اسم مالك المنزل، ونمثّله باستخدام نوع البيانات <code>varchar</code> بحد أقصى 50 محرفًا.
	</li>
	<li>
		<code>year</code>: العام الذي بُني فيه المنزل، ونمثّله باستخدام نوع البيانات <code>int</code> لتخزين القيم العددية.
	</li>
</ul>

<p>
	سيستخدم المفتاح الرئيسي هذه المرة العمودين <code>street_name</code> و <code>street_number</code> بدلًا من استخدام عمود واحد من خلال تنفيذ تعليمة SQL التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_26" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE houses </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     street_name varchar</span><span class="pun">(</span><span class="lit">50</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     street_number varchar</span><span class="pun">(</span><span class="lit">5</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     house_owner varchar</span><span class="pun">(</span><span class="lit">50</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     year </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     PRIMARY KEY</span><span class="pun">(</span><span class="pln">street_name</span><span class="pun">,</span><span class="pln"> street_number</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	تظهر تعليمة <code>PRIMARY KEY</code> بعد تعريفات الأعمدة على عكس المثال السابق، وتليها أقواس تحتوي على اسمي عمودين هما: <code>street_name</code> و <code>street_number</code>. تنشئ هذه الصيغة المفتاح الرئيسي في الجدول <code>houses</code> الذي يمتد إلى عمودين.
</p>

<p>
	إذا ظهر الخرج التالي، فهذا يعني إنشاء الجدول بنجاح:
</p>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.00 sec)
</pre>

<p>
	نحمّل بعد ذلك الجدول بالصفوف التجريبية المعروضة في المثال السابق من خلال تشغيل عملية <code>INSERT INTO</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_28" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO houses VALUES
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'Central Park West'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'100'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'John Doe'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2014</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'Broadway'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1500'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Jane Smith'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2016</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'5th Avenue'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'100'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Bob Johnson'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2018</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'Lexington Avenue'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'5001'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Samantha Davis'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2010</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'Park Avenue'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'7000'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Michael Rodriguez'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2012</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'Central Park West'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'200'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Tom Thompson'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2015</span><span class="pun">);</span></pre>

<p>
	وستستجيب قاعدة البيانات برسالة النجاح التالية:
</p>

<pre class="ipsCode">الخرج
Query OK, 6 rows affected (0.000 sec)
Records: 6  Duplicates: 0  Warnings: 0
</pre>

<p>
	يمكن الآن التحقق من أن الجدول الذي أنشأناه يحتوي على البيانات والتنسيق المتوقع باستخدام تعليمة <code>SELECT</code> التالية:
</p>

<pre class="ipsCode">mysql&gt; SELECT * FROM houses;
</pre>

<p>
	وسيُظهِر الخرج جدولًا مشابهًا للجدول الموجود في بداية هذا القسم:
</p>

<pre class="ipsCode">الخرج
+-------------------+---------------+-------------------+------+
| street_name       | street_number | house_owner       | year |
+-------------------+---------------+-------------------+------+
| 5th Avenue        | 100           | Bob Johnson       | 2018 |
| Broadway          | 1500          | Jane Smith        | 2016 |
| Central Park West | 100           | John Doe          | 2014 |
| Central Park West | 200           | Tom Thompson      | 2015 |
| Lexington Avenue  | 5001          | Samantha Davis    | 2010 |
| Park Avenue       | 7000          | Michael Rodriguez | 2012 |
+-------------------+---------------+-------------------+------+
6 rows in set (0.000 sec)
</pre>

<p>
	لنتحقق الآن من سماح قاعدة البيانات بإدراج صفوف تحتوي على أسماء وأرقام شوارع مكرَّرة، مع تقييد ظهور عناوين كاملة مكررة في الجدول. لنبدأ أولًا بإضافة منزل آخر في شارع Park Avenue كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_30" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO houses VALUES </span><span class="pun">(</span><span class="str">'Park Avenue'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'8000'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Emily Brown'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2011</span><span class="pun">);</span></pre>

<p>
	سيستجيب MySQL برسالة النجاح التالية لأن العنوان ‎<code>8000 Park Avenue</code>‎ لم يظهر في الجدول سابقًا:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.010 sec)
</pre>

<p>
	وستظهر نتيجة مماثلة عند إضافة منزل في العنوان ‎<code>8000 Main Street</code>‎ مع تكرار رقم الشارع كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_33" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO houses VALUES </span><span class="pun">(</span><span class="str">'Main Street'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'8000'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'David Jones'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2009</span><span class="pun">);</span></pre>

<p>
	مما سيؤدي إلى إدراج صف جديد بنجاح بسبب عدم تكرار العنوان الكامل:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.010 sec)
</pre>

<p>
	نحاول الآن إضافة منزل آخر في العنوان ‎<code>100 5th Avenue</code>‎ باستخدام تعليمة <code>INSERT</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_35" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO houses VALUES </span><span class="pun">(</span><span class="str">'5th Avenue'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'100'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Josh Gordon'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2008</span><span class="pun">);</span></pre>

<p>
	وستستجيب قاعدة البيانات برسالة الخطأ التالية لإعلامك بوجود إدخال مكرر للمفتاح الرئيسي لزوج القيم <code>5th Avenue</code> و <code>100</code>:
</p>

<pre class="ipsCode">الخرج
ERROR 1062 (23000): Duplicate entry '5th Avenue-100' for key 'houses.PRIMARY'
</pre>

<p>
	تطبّق قاعدة البيانات قواعد المفتاح الرئيسي بطريقة صحيحة، مع تعريف المفتاح لزوج من الأعمدة، وبالتالي يمكن التأكّد من عدم تكرار العنوان الكامل المكون من اسم الشارع ورقمه في الجدول.
</p>

<p>
	أنشأنا في هذا القسم مفتاحًا طبيعيًا مع زوج من الأعمدة لتحديد كل صف في الجدول <code>house</code> بوصفه صفًا فريدًا، ولكن لا يمكن دائمًا استخلاص المفاتيح الرئيسية من مجموعة البيانات، لذا سنستخدم في القسم التالي المفاتيح الرئيسية الاصطناعية التي لا تأتي من البيانات مباشرةً.
</p>

<h2 id="sequentialprimarykey">
	إنشاء مفتاح رئيسي تسلسلي Sequential Primary Key
</h2>

<p>
	أنشأنا مفاتيح رئيسية فريدة باستخدام أعمدة موجودة في مجموعات البيانات التجريبية النموذجية، ولكن البيانات ستتكرّر في بعض الحالات، مما يمنع الأعمدة من أن تكون معرّفات فريدة جيدة، لذا يمكن إنشاء مفاتيح رئيسية تسلسلية باستخدام معرّفات مُولَّدة. تسمَّى المفاتيح الرئيسية التي أنشأناها من المعرّفات الاصطناعية بالمفاتيح البديلة Surrogate Keys عندما تتطلب البيانات المتاحة ابتكار معرّفات جديدة لتحديد الصفوف بطريقة فريدة.
</p>

<p>
	لنفترض أن لدينا قائمة بأعضاء نادي الكتاب، وهو تجمع غير رسمي يمكن لأي شخص الانضمام إليه دون بطاقة هوية حكومية، وبالتالي هناك احتمال أن ينضم إلى النادي في وقتٍ ما أشخاص يحملون أسماء متطابقة:
</p>

<pre class="ipsCode">جدول بسيط
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| John       | Doe       |
| Jane       | Smith     |
| Bob        | Johnson   |
| Samantha   | Davis     |
| Michael    | Rodriguez |
| Tom        | Thompson  |
| Sara       | Johnson   |
| David      | Jones     |
| Jane       | Smith     |
| Bob        | Johnson   |
+------------+-----------+
</pre>

<p>
	يتكرر الاسمان <code>Bob Johnson</code> و <code>Jane Smith</code> في الجدول، لذا نحتاج إلى استخدام معرّف إضافي للتأكد من هوية كل منهما، ولا يمكن تحديد الصفوف تحديدًا فريدًا في هذا الجدول بأيّ طريقة، ولكن إذا احتفظنا بقائمة أعضاء نادي الكتاب على الورق، فيمكن الاحتفاظ بمعرّفات مساعدة للتمييز بين الأشخاص الذين يحملون الأسماء نفسها في المجموعة.
</p>

<p>
	يمكن إجراء شيء مماثل في قاعدة بيانات علاقية باستخدام عمود إضافي يحتوي على معرّفات مُولَّدة بدون معنى لغرض وحيد هو الفصل بين جميع الصفوف في الجدول بطريقة فريدة، ولنسمي هذا العمود بالاسم <code>member_id</code>، ولكن سيكون من الصعب التوصّل إلى مثل هذا المعرّف كلما أردنا إضافة عضو آخر في نادي الكتاب إلى قاعدة البيانات. يمكن حل هذه المشكلة باستخدام الميزة التي يوفرها MySQL، والتي هي ميزة خاصة بالأعمدة الرقمية المتزايدة تلقائيًا، حيث توفّر قاعدة البيانات قيمة العمود بتسلسل متزايد من الأعداد الصحيحة تلقائيًا.
</p>

<p>
	لننشئ الآن جدولًا يشبه الجدول السابق، ولكن سنضيف عمودًا إضافيًا متزايدًا تلقائيًا <code>member_id</code> ليكون محتويًا على العدد المُسنَد لكل عضو في النادي تلقائيًا، وسيمثّل هذا العدد المُسنَد تلقائيًا مفتاحًا رئيسيًا للجدول الذي سيحتوي على الأعمدة التالية:
</p>

<ul>
	<li>
		<code>member_id</code>: معرّف رقمي متزايد تلقائيًا، ونمثّله باستخدام نوع البيانات <code>int</code>.
	</li>
	<li>
		<code>first_name</code>: الاسم الأول لأعضاء النادي، والذي نمثله باستخدام نوع البيانات <code>varchar</code> بحد أقصى 50 محرفًا.
	</li>
	<li>
		<code>last_name</code>: يالاسم الأخير لأعضاء النادي، والذي نمثله باستخدام نوع البيانات <code>varchar</code> بحد أقصى 50 محرفًا.
	</li>
</ul>

<p>
	لننشئ هذا الجدول من خلال تنفيذ تعليمة SQL التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_37" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE club_members </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     member_id </span><span class="kwd">int</span><span class="pln"> AUTO_INCREMENT PRIMARY KEY</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     first_name varchar</span><span class="pun">(</span><span class="lit">50</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     last_name varchar</span><span class="pun">(</span><span class="lit">50</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	تظهَر تعليمة <code>PRIMARY KEY</code> بعد تعريف نوع العمود مثل المفتاح الرئيسي لعمود واحد، ولكن تظهَر سمة Attribute إضافية قبلها هي <code>AUTO_INCREMENT</code> التي تخبر MySQL بتوليد قيم تلقائيًا لهذا العمود باستخدام تسلسلٍ متزايد من الأرقام إن لم تكن متوفّرة صراحةً.
</p>

<p>
	<strong>ملاحظة</strong>: تُعَد الخاصية <code>AUTO_INCREMENT</code> لتعريف عمود خاصةً بقاعدة بيانات MySQL، ولكن توفّر قواعد البيانات الأخرى طرقًا مماثلة لتوليد مفاتيح تسلسلية مع اختلاف صيغتها بين المحرّكات، لذا نشجّعك على الرجوع إلى التوثيق الرسمي لنظام إدارة قواعد البيانات العلاقية الخاص بك لمزيد من التفاصيل.
</p>

<p>
	إذا ظهر الخرج التالي، فهذا يعني إنشاء الجدول بنجاح:
</p>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.00 sec)
</pre>

<p>
	نحمّل بعد ذلك الجدول بالصفوف التجريبية النموذجية المعروضة في المثال السابق من خلال تشغيل عملية <code>INSERT INTO</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_39" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO club_members </span><span class="pun">(</span><span class="pln">first_name</span><span class="pun">,</span><span class="pln"> last_name</span><span class="pun">)</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'John'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Doe'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'Jane'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Smith'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'Bob'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Johnson'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'Samantha'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Davis'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'Michael'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Rodriguez'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'Tom'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Thompson'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'Sara'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Johnson'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'David'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Jones'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'Jane'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Smith'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="str">'Bob'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Johnson'</span><span class="pun">);</span></pre>

<p>
	تتضمن تعليمة <code>INSERT</code> الآن قائمة بأسماء الأعمدة <code>first_name</code> و <code>first_name</code>، مما يضمن معرفة قاعدة البيانات بأن العمود <code>member_id</code> غير مُدرَج في مجموعة البيانات، لذا يجب أخذ القيمة الافتراضية له بدلًا من ذلك.
</p>

<p>
	وستستجيب قاعدة البيانات برسالة النجاح التالية:
</p>

<pre class="ipsCode">الخرج
Query OK, 10 rows affected (0.002 sec)
Records: 10  Duplicates: 0  Warnings: 0
</pre>

<p>
	نستخدم تعليمة <code>SELECT</code> التالية للتحقق من البيانات الموجودة في الجدول الذي أنشأناه:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_41" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM club_members</span><span class="pun">;</span></pre>

<p>
	وسيعرض الخرج التالي جدولًا مشابهًا للجدول الموجود في بداية هذا القسم:
</p>

<pre class="ipsCode">الخرج
+-----------+------------+-----------+
| member_id | first_name | last_name |
+-----------+------------+-----------+
|         1 | John       | Doe       |
|         2 | Jane       | Smith     |
|         3 | Bob        | Johnson   |
|         4 | Samantha   | Davis     |
|         5 | Michael    | Rodriguez |
|         6 | Tom        | Thompson  |
|         7 | Sara       | Johnson   |
|         8 | David      | Jones     |
|         9 | Jane       | Smith     |
|        10 | Bob        | Johnson   |
+-----------+------------+-----------+
10 rows in set (0.000 sec)
</pre>

<p>
	ولكن سيظهر العمود <code>member_id</code> في النتيجة، والذي يحتوي على تسلسل من الأرقام من 1 إلى 10، وبالتالي أصبح من الممكن التمييز بين الصفوف <code>Jane Smith</code> و <code>Bob Johnson</code> المكرّرة، إذ سيرتبط كل اسم بمعرّف فريد <code>member_id</code>.
</p>

<p>
	لنتحقّق الآن مما إذا كانت قاعدة البيانات ستسمح بإضافة بعضو آخر اسمه <code>Tom Thompson</code> إلى قائمة أعضاء النادي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_43" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO club_members </span><span class="pun">(</span><span class="pln">first_name</span><span class="pun">,</span><span class="pln"> last_name</span><span class="pun">)</span><span class="pln"> VALUES </span><span class="pun">(</span><span class="str">'Tom'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Thompson'</span><span class="pun">);</span></pre>

<p>
	وسيستجيب MySQL برسالة النجاح التالية:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.009 sec)
</pre>

<p>
	ولنتحقق الآن من المعرّف الرقمي الذي أسندَته قاعدة البيانات للإدخال الجديد من خلال تنفيذ استعلام <code>SELECT</code> التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6686_45" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM club_members</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي الذي سيحتوي على صفٍ جديد:
</p>

<pre class="ipsCode">الخرج
+-----------+------------+-----------+
| member_id | first_name | last_name |
+-----------+------------+-----------+
|         1 | John       | Doe       |
|         2 | Jane       | Smith     |
|         3 | Bob        | Johnson   |
|         4 | Samantha   | Davis     |
|         5 | Michael    | Rodriguez |
|         6 | Tom        | Thompson  |
|         7 | Sara       | Johnson   |
|         8 | David      | Jones     |
|         9 | Jane       | Smith     |
|        10 | Bob        | Johnson   |
|        11 | Tom        | Thompson  |
+-----------+------------+-----------+
11 rows in set (0.000 sec)
</pre>

<p>
	أُسنِد الرقم 11 إلى الصف الجديد تلقائيًا في عمود <code>member_id</code> باستخدام ميزة <code>AUTO_INCREMENT</code> في قاعدة البيانات.
</p>

<p>
	إذا لم يكن للبيانات التي نعمل عليها مرشَّحين طبيعيين ليكونوا مفاتيح رئيسية، ولا نريد توفير معرّفات اصطناعية في كل مرة نضيف فيها بيانات جديدة إلى قاعدة البيانات، فيمكن الاعتماد على المعرّفات المولَّدة تسلسليًا لتكون مفاتيح رئيسية.
</p>

<h2 id="-3">
	الخلاصة
</h2>

<p>
	تعلّمنا في هذا المقال ما هي المفاتيح الرئيسية وكيفية إنشاء أنواعها الشائعة في MySQL لتحديد الصفوف الفريدة في جداول قاعدة البيانات، حيث بنينا مفاتيح رئيسية طبيعية، وأنشأنا مفاتيح رئيسية تمتد على أعمدة متعددة، واستخدمنا مفاتيح تسلسلية متزايدة تلقائيًا عند عدم وجود مفاتيح طبيعية.
</p>

<p>
	يمكن استخدام المفاتيح الرئيسية لتشكيل بنية قاعدة البيانات، مما يضمن إمكانية التعرّف على صفوف البيانات بطريقة فريدة. وضّح هذا المقال أساسيات استخدام المفاتيح الرئيسية فقط، لذا يمكن مطالعة <a href="https://dev.mysql.com/doc/refman/8.0/en/create-table.html" rel="external nofollow">توثيق MySQL للقيود</a> لمزيد من المعلومات، ويمكن أيضًا الاطلاع على مقال <a href="https://academy.hsoub.com/programming/sql/%D9%81%D9%87%D9%85-%D9%82%D9%8A%D9%88%D8%AF-sql-r2217/" rel="">فهم قيود SQL</a> ومقال <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%82%D9%8A%D9%88%D8%AF-%D9%81%D9%8A-sql-r2225/" rel="">كيفية استخدام القيود في SQL</a>.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-primary-keys-in-sql" rel="external nofollow">How To Use Primary Keys in SQL</a> لصاحبَيه Mateusz Papiernik و Rachel Lee.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D9%81%D9%87%D8%A7%D8%B1%D8%B3-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF%D8%A9-%D8%A7%D9%84%D8%A3%D8%B9%D9%85%D8%AF%D8%A9-%D9%81%D9%8A-sql-r2524/" rel="">الفهارس متعددة الأعمدة في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%AF%D8%AD-triggers-%D9%81%D9%8A-sql-r2520/" rel="">كيفية استخدام القوادح Triggers في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r2224/" rel="">كيفية إنشاء وإدارة الجداول في SQL</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/SQL/keys" rel="external">مفاتيح الجداول</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2528</guid><pubDate>Mon, 10 Mar 2025 12:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x641;&#x647;&#x627;&#x631;&#x633; &#x645;&#x62A;&#x639;&#x62F;&#x62F;&#x629; &#x627;&#x644;&#x623;&#x639;&#x645;&#x62F;&#x629; &#x641;&#x64A; SQL</title><link>https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D9%81%D9%87%D8%A7%D8%B1%D8%B3-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF%D8%A9-%D8%A7%D9%84%D8%A3%D8%B9%D9%85%D8%AF%D8%A9-%D9%81%D9%8A-sql-r2524/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_03/SQL.png.da90ebcd55c94d33f11557020123065b.png" /></p>
<p>
	شرحنا في <a href="https://academy.hsoub.com/programming/sql/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%81%D9%87%D8%A7%D8%B1%D8%B3-indexes-%D9%81%D9%8A-sql-r2523/" rel="">المقال السابق</a> مفهوم الفهارس في قاعدة البيانات، ووضحنا أنواعًا مختلفة من الفهارس على قاعدة البيانات، وكانت جميع هذه الفهارس معرًفة باستخدام اسم عمود واحد single column، حيث يتعلق هذا الفهرس بقيم هذا العمود المختار، ولكن تدعم معظم أنظمة قواعد البيانات الفهارس التي تمتد لأكثر من عمود واحد multiple columns، وهذا ما سنوضّحه في هذا المقال، بالإضافة توضيح كيفية سرد وإزالة الفهارس الموجودة مسبقًا.
</p>

<h2 id="">
	استخدام الفهارس مع أعمدة متعددة
</h2>

<p>
	توفر الفهارس متعددة الأعمدة طريقةً لتخزين قيم أعمدة متعددة في فهرس واحد، مما يسمح <a href="https://academy.hsoub.com/devops/servers/databases/%D9%85%D8%AD%D8%B1%D9%83%D8%A7%D8%AA-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/" rel="">لمحرّك قاعدة البيانات</a> بتنفيذ الاستعلامات بسرعة وكفاءة أكبر باستخدام مجموعة الأعمدة مع بعضها البعض. فالاستعلامات المستخدَمة بصورة متكررة والتي يجب تحسينها للحصول على أداء أفضل تستخدم شروطًا متعددة في تعليمة الترشيح <code>WHERE</code> في أغلب الأحيان، ومن الأمثلة على هذا النوع من الاستعلامات استعلام يطلب من قاعدة البيانات أن تعثر على شخص معين من خلال اسمه الأول والأخير كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8823_9" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM employees WHERE last_name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Smith'</span><span class="pln"> AND first_name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'John'</span><span class="pun">;</span></pre>

<p>
	قد تكون الفكرة الأولى لتحسين هذا الاستعلام باستخدام الفهارس هي إنشاء فهرسين، أحدهما في العمود <code>last_name</code> والآخر في العمود <code>first_name</code>، ولكنه ليس الخيار الأفضل لهذه الحالة، حيث فإذا أنشأنا فهرسين منفصلين بهذه الطريقة، فسيعرف MySQL كيفية العثور على جميع الموظفين الذين يحملون اسم <code>Smith</code> مثلًا، وسيعرف أيضًا كيفية العثور على جميع الموظفين الذين يحملون اسم <code>John</code>، ولكنه لن يعرف كيفية العثور على الموظفين الذين يحملون الاسم <code>John Smith</code>.
</p>

<p>
	لنوضّح مشكلة وجود فهرسين فرديين من خلال تخيل وجود دليلي هاتف منفصلين، أحدهما مرتب حسب الاسم الأخير والآخر حسب الاسم الأول، ويشبه هذان الدليلان الفهارس التي أنشأناها في المقال السابق في العمودين <code>last_name</code> و <code>first_name</code> على التوالي. يمكنك التعامل مع مشكلة العثور على الاسم <code>John Smith</code> كمستخدمٍ لدليل الهاتف باستخدام ثلاث طرق ممكنة هي:
</p>

<p>
	الطريقة الأولى هي استخدم دليل الهاتف المرتب حسب الاسم الأخير للعثور على جميع الأشخاص الذين يحملون الاسم <code>Smith</code>، وتجاهل دليل الهاتف الثاني، ثم يمكن المرور يدويًا على جميع الأشخاص الذين يحملون اسم <code>Smith</code> واحدًا تلو الآخر حتى نجد الاسم <code>John Smith</code>.
</p>

<p>
	الطريقة الثانية هي تطبيق الطريقة المعاكسة من خلال استخدام دليل الهاتف المرتب حسب الاسم الأول للعثور على جميع الأشخاص الذين يحملون اسم <code>John</code>، وتجاهل دليل الهاتف الثاني، ثم المرور يدويًا على جميع الأشخاص الذين يحملون اسم <code>John</code> واحدًا تلو الآخر حتى نجد الاسم <code>John Smith</code>.
</p>

<p>
	الطريقة الأخيرة هي محاولة استخدام دليلي الهاتف معًا من خلال البحث عن جميع الأشخاص الذين يحملون اسم <code>John</code> وعن جميع الأشخاص الذين يحملون اسم <code>Smith</code> بطريقة منفصلة، وكتابة النتائج المؤقتة، ثم نحاول يدويًا إيجاد تقاطع هاتين المجموعتين الفرعيتين من البيانات بحثًا عن الأشخاص الموجودين في القائمتين الفرديتين.
</p>

<p>
	لا تُعَد أي طريقة من الطرق السابقة مثالية، ويوفر MySQL أيضًا خيارات مماثلة عند التعامل مع العديد من الفهارس المنفصلة والاستعلامات التي تطلب أكثر من شرط ترشيح واحد.
</p>

<p>
	توجد طريقة أخرى أيضًا تتمثّل باستخدام الفهارس التي تأخذ عدة أعمدة بدلًا من عمود واحد، حيث يمكنك تخيل ذلك كدليل هاتف موضوع ضمن دليل هاتف آخر، إذ سنبحث أولًا عن الاسم الأخير <code>Smith</code>، مما يوجّهنا إلى الدليل الثاني لجميع الأشخاص الذين يحملون اسم <code>Smith</code> بحيث تكون الأسماء مرتبة أبجديًا حسب الاسم الأول، ويمكننا استخدام هذا الدليل للعثور على الاسم <code>John</code> بسرعة.
</p>

<h2>
	إنشاء فهرس متعدد الأعمدة
</h2>

<p>
	يمكن إنشاء فهرس متعدد الأعمدة في MySQL للأسماء الأخيرة والأسماء الأولى في الجدول <code>employees</code> من خلال تنفيذ التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8823_11" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE INDEX names ON employees</span><span class="pun">(</span><span class="pln">last_name</span><span class="pun">,</span><span class="pln"> first_name</span><span class="pun">);</span></pre>

<p>
	تختلف التعليمة <code>CREATE INDEX</code> في هذه الحالة بعض الشيء، حيث ستحتوي على عمودين هما: <code>last_name</code> ثم <code>first_name</code> بين قوسين بعد اسم الجدول <code>employees</code>، مما يؤدي إلى إنشاء فهرس متعدد الأعمدة مع هذين العمودين، ويُعَد ترتيب الأعمدة في تعريف الفهرس مهمًا.
</p>

<p>
	تعرض قاعدة البيانات الرسالة التالية التي تؤكّد إنشاء الفهرس بنجاح:
</p>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.024 sec)
Records: 0  Duplicates: 0  Warnings: 0
</pre>

<p>
	نستخدم الآن استعلام <code>SELECT</code> للعثور على الصفوف التي يتطابق فيها الاسم الأول مع <code>John</code> والاسم الأخير مع <code>Smith</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8823_13" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM employees WHERE last_name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Smith'</span><span class="pln"> AND first_name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'John'</span><span class="pun">;</span></pre>

<p>
	وتكون النتيجة صفًا واحدًا يحتوي على موظف اسمه <code>John Smith</code>:
</p>

<pre class="ipsCode">الخرج
+-------------+------------+-----------+---------------+--------+
| employee_id | first_name | last_name | device_serial | salary |
+-------------+------------+-----------+---------------+--------+
|           1 | John       | Smith     | ABC123        |  60000 |
+-------------+------------+-----------+---------------+--------+
1 row in set (0.000 sec)
</pre>

<p>
	نستخدم الآن استعلام مع أمر <code>EXPLAIN</code> التالي للتحقق من استخدام الفهرس:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8823_15" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> EXPLAIN SELECT </span><span class="pun">*</span><span class="pln"> FROM employees WHERE last_name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Smith'</span><span class="pln"> AND first_name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'John'</span><span class="pun">;</span></pre>

<p>
	وستكون طريقة التنفيذ مشابهة لما يلي:
</p>

<pre class="ipsCode">الخرج
+----+-------------+-----------+------------+------+---------------+-------+---------+-------------+------+----------+-------+
| id | select_type | table     | partitions | type | possible_keys | key   | key_len | ref         | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+-------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | employees | NULL       | ref  | names         | names | 406     | const,const |    1 |   100.00 | NULL  |
+----+-------------+-----------+------------+------+---------------+-------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
</pre>

<p>
	في هذه الحالة استخدمت قاعدة البيانات الفهرس <code>names</code>، ومسحت صفًا واحدًا، لذا لم تمر على الجدول أكثر مما تحتاج إليه. يحتوي العمود <code>Extra</code> على العبارة <code>Using index condition</code> التي تعني أن MySQL يمكنه إكمال الترشيح باستخدام الفهرس فقط، حيث يوفّر الترشيح -وفقًا للأسماء الأولى والأخيرة باستخدام الفهرس متعدد الأعمدة الذي يمتد بين هذين العمودين لقاعدة البيانات- طريقةً مباشرة وسريعة للعثور على النتائج المطلوبة.
</p>

<p>
	لنشاهد الآن ما سيحدث إذا حاولنا العثور على جميع الموظفين الذين يحملون اسم <code>Smith</code> دون الترشيح وفقًا للاسم الأول مع تعريف الفهرس في العمودين، ولنشغّل الاستعلام المعدَّل التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8823_17" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM employees WHERE last_name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Smith'</span><span class="pun">;</span></pre>

<p>
	وستظهر النتائج التالية:
</p>

<pre class="ipsCode">الخرج
+-------------+------------+-----------+---------------+--------+
| employee_id | first_name | last_name | device_serial | salary |
+-------------+------------+-----------+---------------+--------+
|          20 | Abigail    | Smith     | FGH890        | 155000 |
|          17 | Daniel     | Smith     | WXY901        | 140000 |
|           1 | John       | Smith     | ABC123        |  60000 |
|           5 | Michael    | Smith     | MNO345        |  80000 |
+-------------+------------+-----------+---------------+--------+
4 rows in set (0.000 sec)
</pre>

<p>
	نلاحظ وجود أربع موظفين يحملون الاسم الأخير <code>Smith</code>.
</p>

<p>
	ننتقل الآن إلى طريقة تنفيذ الاستعلام باستخدام التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8823_19" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> EXPLAIN SELECT </span><span class="pun">*</span><span class="pln"> FROM employees WHERE last_name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Smith'</span><span class="pun">;</span></pre>

<p>
	وستكون طريقة التنفيذ مشابهة لما يلي:
</p>

<pre class="ipsCode">الخرج
+----+-------------+-----------+------------+------+---------------+-------+---------+-------+------+----------+-------+
| id | select_type | table     | partitions | type | possible_keys | key   | key_len | ref   | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+-------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | employees | NULL       | ref  | names         | names | 203     | const |    4 |   100.00 | NULL  |
+----+-------------+-----------+------------+------+---------------+-------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)
</pre>

<p>
	نلاحظ إعادة 4 صفوف هذه المرة، حيث يوجد أكثر من موظف يحمل هذا الاسم الأخير، ولكن يوضّح جدول طريقة التنفيذ أن قاعدة البيانات استخدمت الفهرس متعدد الأعمدة <code>names</code> لإجراء هذا الاستعلام، ومسحت 4 صفوف فقط، وهو العدد الدقيق المُعاد.
</p>

<p>
	مرّرنا في الاستعلامات السابقة العمود المُستخدَم لترشيح النتائج <code>last_name</code> أولًا في تعليمة <code>CREATE INDEX</code>، وسنرشّح الآن الجدول <code>employees</code> وفق العمود <code>first_name</code>، وهو العمود الثاني في قائمة الأعمدة لهذا الفهرس متعدد الأعمدة، لذا ننفّذ الآن الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8823_21" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM employees WHERE first_name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'John'</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
+-------------+------------+-----------+---------------+--------+
| employee_id | first_name | last_name | device_serial | salary |
+-------------+------------+-----------+---------------+--------+
|           1 | John       | Smith     | ABC123        |  60000 |
+-------------+------------+-----------+---------------+--------+
1 row in set (0.000 sec)
</pre>

<p>
	لننتقل الآن لعرض طريقة تنفيذ الاستعلام كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8823_23" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> EXPLAIN SELECT </span><span class="pun">*</span><span class="pln"> FROM employees WHERE first_name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'John'</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | employees | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   20 |    10.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
</pre>

<p>
	تحتوي النتائج المُعادة على موظف واحد دون استخدام أي فهرس هذه المرة، ومسحَت قاعدة البيانات الجدول بالكامل كما يوضح التعليق <code>Using where</code> في العمود <code>Extra</code>، بالإضافة إلى 20 صفًا ممسوحًا.
</p>

<p>
	لم تستخدم قاعدة البيانات الفهرس في هذه الحالة بسبب ترتيب الأعمدة المُمرَّرة إلى التعليمة <code>CREATE INDEX</code> عند إنشاء الفهرس لأول مرة: <code>last_name, first_name</code>، إذ لا يمكن لقاعدة البيانات استخدام الفهرس إلا إذا استخدم الاستعلام العمود الأول أو العمودين الأول والثاني، ولا يمكنها دعم الاستعلامات مع الفهرس عند عدم استخدام العمود الأول من تعريف الفهرس.
</p>

<p>
	إذا أنشأنا فهرسًا لأعمدة متعددة، فيمكن لقاعدة البيانات استخدام هذا الفهرس لتسريع الاستعلامات التي تتضمن جميع الأعمدة المفهرسَة أو ذات البادئة المتزايدة اليسارية لجميع الأعمدة المفهرسَة، فمثلًا يمكن استخدام فهرس متعدد الأعمدة يتضمن الأعمدة <code>a</code> و<code> b</code> و<code> c</code> لتسريع الاستعلامات التي تتضمن جميع الأعمدة الثلاثة، والاستعلامات التي تتضمن العمودين الأولين فقط، أو حتى الاستعلامات التي تتضمن العمود الأول فقط، ولكن لن يساعد الفهرس في الاستعلامات التي تتضمن العمود الأخير فقط <code>c</code> أو العمودين الأخيرين <code>b</code> و <code>c</code>.
</p>

<p>
	يمكن استخدام فهرس واحد متعدد الأعمدة لتسريع الاستعلامات المختلفة للجدول نفسه من خلال اختيار الأعمدة المُضمَّنة في الفهرس بعناية وترتيبها، فمثلًا إذا افترضنا أن نبحث عن الموظفين باستخدام الاسم الأول والأخير أو الاسم الأخير فقط، فسيضمن الترتيب المُقدَّم للأعمدة في الفهرس <code>names</code> أن الفهرس سيسرّع جميع الاستعلامات ذات الصلة.
</p>

<p>
	استخدمنا في هذا القسم فهرسًا متعدد الأعمدة وتعلّمنا ترتيب الأعمدة عند تحديد مثل هذا الفهرس، وسنتعلّم في الفقرات التالية كيفية إدارة الفهارس الموجودة مسبقًا.
</p>

<h2 id="-1">
	سرد وإزالة الفهارس الموجودة مسبقًا
</h2>

<p>
	أنشأنا في الأقسام السابقة فهارس جديدة، بما أن الفهارس لها أسماء وتُعرَّف لجداول معينة، فيمكننا أيضًا سردها ومعالجتها عند الحاجة، حيث يمكن سرد جميع الفهارس التي أنشأناها سابقًا للجدول <code>employees</code> من خلال تنفيذ التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8823_27" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SHOW INDEXES FROM employees</span><span class="pun">;</span></pre>

<p>
	وسيكون الخرج مشابهًا لما يلي:
</p>

<pre class="ipsCode">الخرج
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table     | Non_unique | Key_name      | Seq_in_index | Column_name   | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| employees |          0 | device_serial |            1 | device_serial | A         |          20 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
| employees |          1 | salary        |            1 | salary        | A         |          20 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
| employees |          1 | names         |            1 | last_name     | A         |          16 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
| employees |          1 | names         |            2 | first_name    | A         |          20 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
4 rows in set (0.01 sec)
</pre>

<p>
	قد يختلف الخرج بعض الشيء اعتمادًا على إصدار MySQL الخاص بنا، ولكنه سيتضمن جميع الفهارس مع أسمائها والأعمدة المستخدمة لتعريف الفهرس والمعلومات التي تجعله فريدًا وتفاصيل أخرى لتعريف الفهرس.
</p>

<p>
	يمكن حذف الفهارس الموجودة مسبقًا من خلال استخدام تعليمة SQL التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8823_29" style=""><span class="pln">DROP INDEX</span></pre>

<p>
	فإن لم نعد نرغب في فرض جعل العمود <code>device_serial</code> فريدًا، فلن تكون هناك حاجة إلى الفهرس <code>device_serial</code> بعد الآن، وسننفّذ الأمر التالي لحذفه:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8823_31" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> DROP INDEX device_serial ON employees</span><span class="pun">;</span></pre>

<p>
	<code>device_serial</code> هو اسم الفهرس و <code>employees</code> هو الجدول الذي عرّفنا الفهرس له، وستؤكد قاعدة البيانات حذف الفهرس كما يلي:
</p>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.018 sec)
Records: 0  Duplicates: 0  Warnings: 0
</pre>

<p>
	قد تتغير أنماط الاستعلامات النموذجية بمرور الوقت، وقد تظهر أنواع استعلامات جديدة في بعض الأحيان، لذا قد نحتاج إلى إعادة تقييم الفهارس التي نستخدمها أو إنشاء فهارس جديدة أو حذف الفهارس غير المستخدمة لتجنب تناقص أداء قاعدة البيانات من خلال تحديثها باستمرار.
</p>

<p>
	يمكننا إدارة الفهارس في قاعدة بيانات موجودة مسبقًا باستخدام أوامر <code>CREATE INDEX</code> و <code>DROP INDEX</code> من خلال اتباع أفضل الممارسات لإنشاء الفهارس عندما تصبح ضرورية ومفيدة.
</p>

<h2 id="-2">
	الخلاصة
</h2>

<p>
	تعلّمنا في هذا المقال كيف يمكن تعريف فهارس متعددة الأعمدة وكيف يمكن للفهارس أن تؤثر على الاستعلامات عند استخدام أكثر من عمود واحد في شرط الترشيح وكيفية سرد وإزالة الفهارس الموجودة مسبقًا، وقد ركزنا على أمثلة بسيطة توضح أساسيات استخدام الفهارس فقط، ولكن يمكننا دعم الاستعلامات الأكثر تعقيدًا من خلال الفهارس عند فهم كيفية اختيار MySQL للفهارس المُستخدَمة ومتى يستخدمها، لذا يمكن الرجوع ل<a href="https://dev.mysql.com/doc/refman/8.0/en/optimization-indexes.html" rel="external nofollow">توثيق MySQL للفهارس</a> لمزيد من المعلومات.
</p>

<p>
	ترجمة -وبتصرف- للجزء الثاني من مقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-indexes-in-mysql" rel="external nofollow">How To Use Indexes in MySQL</a> لصاحبيه Mateusz Papiernik و Rachel Lee.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/sql/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%81%D9%87%D8%A7%D8%B1%D8%B3-indexes-%D9%81%D9%8A-sql-r2523/" rel="">مقدمة إلى الفهارس Indexes في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D9%81%D9%87%D8%A7%D8%B1%D8%B3-indexes-%D9%81%D9%8A-sql-r589/" rel="">الفهارس Indexes في SQL</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/SQL/create_index" rel="external">إنشاء فهرس CREATE INDEX</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/SQL/drop_index" rel="external">حذف الفهرس DROP INDEX</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/SQL/alter_index" rel="external">تعديل الفهرس ALTER INDEX</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2524</guid><pubDate>Mon, 03 Mar 2025 12:00:00 +0000</pubDate></item><item><title>&#x645;&#x642;&#x62F;&#x645;&#x629; &#x625;&#x644;&#x649; &#x627;&#x644;&#x641;&#x647;&#x627;&#x631;&#x633; Indexes &#x641;&#x64A; SQL</title><link>https://academy.hsoub.com/programming/sql/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%81%D9%87%D8%A7%D8%B1%D8%B3-indexes-%D9%81%D9%8A-sql-r2523/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_03/SQL.png.31f3e6f1120512b4ab6a21b52bb842d1.png" /></p>
<p>
	يمكن استخدام قواعد البيانات العلاقية Relational Databases للعمل مع بيانات من جميع الأحجام بما في ذلك قواعد البيانات الكبيرة التي تحتوي على ملايين الصفوف، وتوفّر لنا لغة الاستعلام البنيوية Structured Query Language -أو SQL اختصارًا- طريقة موجزة ومباشرة للعثور على صفوف معينة في جداول قاعدة البيانات وفق معايير محددة، ولكن مع تزايد الحجم سيصبح تحديد موقع صفوف معينة في قواعد البيانات أكثر صعوبة ويشبه البحث عن إبرة في كومة قش!
</p>

<p>
	تصعّب قدرة قواعد البيانات على قبول مجموعة واسعة من شروط الاستعلام على <a href="https://academy.hsoub.com/devops/servers/databases/%D9%85%D8%AD%D8%B1%D9%83%D8%A7%D8%AA-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/" rel="">محرّك قاعدة البيانات</a> توقّع الاستعلامات الأكثر شيوعًا، إذ يجب أن يكون المحرّك مستعدًا لتحديد موقع الصفوف بكفاءة في جداول قاعدة البيانات بغض النظر عن حجمها، ولكن بطبيعة الحالة سيسوء أداء البحث مع زيادة حجم البيانات وسيصعب العثور على النتائج التي تتطابق مع الاستعلام بسرعة.
</p>

<p>
	في هذه الحالة يمكن لمسؤول قواعد البيانات استخدام مفهوم الفهارس Indexes لمساعدة محرّك قاعدة البيانات على تسريع البحث وتحسين أدائه، حيث سنتعلم في هذا المقال مفهوم الفهارس وكيفية إنشائها للاستفادة منها في الاستعلام من قاعدة البيانات.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	يجب أن يكون لدينا حاسوب يشغّل نظام إدارة قواعد البيانات العلاقية Relational Database Management System -أو RDBMS اختصارًا- مستند إلى لغة SQL. وقد اختبرنا التعليمات والأمثلة الواردة في هذا المقال باستخدام البيئة التالية:
</p>

<ul>
	<li>
		خادم عامل على توزيعة أوبنتو Ubuntu مع مستخدم ذي صلاحيات مسؤول مختلف عن المستخدم الجذر وجدار حماية مضبوط باستخدام أداة UFW كما هو موضح في <a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04" rel="external nofollow">دليل الإعداد الأولي للخادم مع الإصدار 20.04 من أوبنتو</a>
	</li>
	<li>
		نظام MySQL مُثبَّت ومؤمَّن على الخادم كما هو موضح في مقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبنتو</a>، وقد نفذنا خطوات هذا المقال باستخدام مستخدم MySQL مختلف عن المستخدم الجذر وفق الطريقة الموضحة في الخطوة التالية من المقال
	</li>
	<li>
		معرفة أساسية بتنفيذ استعلامات <code>SELECT</code> لاسترجاع البيانات من قاعدة البيانات كما هو موضّح في مقال <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%B9%D9%86-%D8%A7%D9%84%D8%B3%D8%AC%D9%84%D8%A7%D8%AA-%D9%85%D9%86-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r2249/" rel="">الاستعلام عن السجلات من الجداول في SQL</a>
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: تجدر الإشارة إلى أنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية RDBMS لها تقديماتها الفريدة من لغة SQL، إذ ستعمل الأوامر في هذا المقال بنجاح مع معظم هذه الأنظمة، ولكن الفهارس ليست جزءًا من صيغة SQL المعيارية، لذا قد تجد بعض الاختلافات في الصيغة أو الناتج عند اختبارها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<p>
	سنحتاج أيضًا إلى قاعدة بيانات تحتوي على بعض الجداول المُحمَّلة ببيانات تجريبية نموذجية لنتمكن من التدرب على استخدام الفهارس، وسنشرح في القسم التالي تفاصيل حول الاتصال بخادم MySQL وإنشاء قاعدة بيانات تجريبية، والتي سنستخدمها في أمثلة هذا المقال.
</p>

<h2 id="mysql">
	الاتصال بخادم MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	سنتصل بخادم MySQL وسننشئ قاعدة بيانات تجريبية لاتباع الأمثلة الواردة في هذا المقال. إذا كان نظام قاعدة بيانات SQL الخاص بنا يعمل على خادم بعيد، نتصل بالخادم باستخدام بروتوكول <a href="https://academy.hsoub.com/devops/security/ssh/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr></a> من جهازنا المحلي كما يلي:
</p>

<pre class="ipsCode">$ ssh user@your_server_ip
</pre>

<p>
	ثم نفتح واجهة سطر أوامر خادم MySQL مع وضع اسم حساب مستخدم MySQL الخاص بنا مكان <code>user</code>:
</p>

<pre class="ipsCode">$ mysql -u user -p
</pre>

<p>
	ننشئ قاعدة بيانات باسم <code>indexes</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_9" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE DATABASE indexes</span><span class="pun">;</span></pre>

<p>
	إذا أُنشئِت قاعدة البيانات بنجاح، فسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	يمكن اختيار قاعدة البيانات <code>indexes</code> من خلال تنفيذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode">$ USE indexes;
</pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	اخترنا قاعدة البيانات، وسننشئ جدولًا تجريبيًا ضمنها، حيث سنستخدم في هذا المقال قاعدة بيانات افتراضية للموظفين لتخزين تفاصيل الموظفين الحاليين وأجهزة عملهم. سيحتوي الجدول <code>employees</code> على بيانات بسيطة حول الموظفين في قاعدة البيانات، والتي سنمثّلها باستخدام الأعمدة التالية:
</p>

<ul>
	<li>
		<code>employee_id</code><span>:</span> معرّف الموظف، نوع بياناته <code>int</code>، وسيكون هذا العمود المفتاح الرئيسي <a href="https://wiki.hsoub.com/SQL/keys" rel="external">Primary Key</a> للجدول
	</li>
	<li>
		<code>first_name</code>: الاسم الأول لكل موظف، نوع بياناته <code>varchar</code> بحد أقصى 50 محرفًا
	</li>
	<li>
		<code>last_name</code>: لاسم الأخير لكل موظف، ونمثّله باستخدام نوع بياناته <code>varchar</code> بحد أقصى 50 محرفًا
	</li>
	<li>
		<code>device_serial</code>: الرقم التسلسلي لحاسوب الموظف، ونمثّله باستخدام نوع البيانات <code>varchar</code> بحد أقصى 15 محرفًا
	</li>
	<li>
		<code>salary</code>: راتب كل موظف، ونمثّله باستخدام نوع البيانات <code>int</code> الذي يخزّن البيانات العددية
	</li>
</ul>

<p>
	ننشئ هذا الجدول التجريبي باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_13" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE employees </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     employee_id </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     first_name varchar</span><span class="pun">(</span><span class="lit">50</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     last_name varchar</span><span class="pun">(</span><span class="lit">50</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     device_serial varchar</span><span class="pun">(</span><span class="lit">15</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     salary </span><span class="kwd">int</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	إذا كان الخرج كما يلي، فهذا يعني إنشاء الجدول بنجاح:
</p>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.00 sec)
</pre>

<p>
	نحمّل بعد ذلك الجدول <code>employees</code> ببعض البيانات التجريبية من خلال تشغيل عملية <code><a href="https://wiki.hsoub.com/SQL/insert" rel="external">INSERT INTO</a></code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_15" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO employees VALUES
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'John'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Smith'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ABC123'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">60000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Jane'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Doe'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'DEF456'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">65000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Bob'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Johnson'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'GHI789'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">70000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Sally'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Fields'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'JKL012'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">75000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Michael'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Smith'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'MNO345'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">80000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Emily'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Jones'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'PQR678'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">85000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="str">'David'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Williams'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'STU901'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">90000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Sarah'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Johnson'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'VWX234'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">95000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="str">'James'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Brown'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'YZA567'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Emma'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Miller'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'BCD890'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">105000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">11</span><span class="pun">,</span><span class="pln"> </span><span class="str">'William'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Davis'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'EFG123'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">110000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Olivia'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Garcia'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'HIJ456'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">115000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">13</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Christopher'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Rodriguez'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'KLM789'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">120000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">14</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Isabella'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Wilson'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'NOP012'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">125000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">15</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Matthew'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Martinez'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'QRS345'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">130000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Sophia'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Anderson'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'TUV678'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">135000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">17</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Daniel'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Smith'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'WXY901'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">140000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">18</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Mia'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Thomas'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ZAB234'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">145000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">19</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Joseph'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Hernandez'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'CDE567'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">150000</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="pun">(</span><span class="lit">20</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Abigail'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Smith'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'FGH890'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">155000</span><span class="pun">);</span></pre>

<p>
	ستستجيب قاعدة البيانات برسالة النجاح التالية:
</p>

<pre class="ipsCode">الخرج
Query OK, 20 rows affected (0.010 sec)
Records: 20  Duplicates: 0  Warnings: 0
</pre>

<p>
	<strong>ملاحظة</strong>: مجموعة البيانات هنا ليست كبيرة بما يكفي لتوضيح تأثير الفهارس على الأداء مباشرة، فالهدف منها هنا توضيح لكيفية استخدام الفهارس في MySQL لتقييد عدد الصفوف التي نجتازها لإجراء الاستعلامات والحصول على النتائج المطلوبة.
</p>

<p>
	نحن الآن جاهزون لمتابعة هذا المقال والبدء باستخدام الفهارس في MySQL.
</p>

<h2 id="indexes">
	ما هي الفهارس Indexes
</h2>

<p>
	يجب أن تمر قاعدة البيانات على جميع الصفوف الموجودة في الجدول واحدًا تلو الآخر عند تنفيذ استعلام على قاعدة بيانات MySQL، فمثلًا قد نرغب في البحث عن الاسم الأخير للموظفين المتطابق مع الاسم <code>Smith</code> أو جميع الموظفين الذين يتقاضون راتبًا أعلى من 100000 دولار، حيث سيُفحَص كل صف في الجدول واحدًا تلو الآخر للتحقق مما إذا كان يتطابق مع الشرط.
</p>

<p>
	إذا كان الصف متطابقًا مع الشرط، فسيُضاف إلى قائمة الصفوف المُعادة، وإن لم يكن كذلك، فسيمسح MySQL الصفوف اللاحقة حتى يستعرض الجدول بالكامل. هذه الطريقة للعثور على الصفوف المطابقة فعّالة، ولكنها قد تصبح بطيئة وتستهلك كثيرًا من الموارد عند زيادة حجم الجدول، لذا قد لا تكون مناسبة للجداول الكبيرة أو الاستعلامات التي تتطلب وصولًا متكررًا أو سريعًا إلى البيانات.
</p>

<p>
	يمكن حل مشكلات الأداء المتعلقة بالجداول والاستعلامات الكبيرة باستخدام الفهارس، وهي هياكل بيانات فريدة تخزّن مجموعة فرعية مرتبة من البيانات فقط بحيث تكون منفصلة عن صفوف الجدول، وتسمح لمحرّك قاعدة البيانات بالعمل بسرعة وكفاءة أكبر عند البحث عن القيم أو الترتيب وفق حقل معين أو مجموعة من الحقول.
</p>

<p>
	لنستخدم الآن الجدول <code>employees</code>، فأحد الاستعلامات النموذجية التي يمكن تنفيذها هو العثور على الموظفين باستخدام اسمهم الأخير. إن لم نستخدم الفهارس، فسيسترجع MySQL كل موظف من الجدول ويتحقق من تطابق الاسم الأخير مع الاستعلام، وإذا استخدمنا فهرسًا ما، فسيحتفظ بقائمة منفصلة من الأسماء الأخيرة، والتي تحتوي فقط على مؤشّرات إلى صفوف الموظفين المحدَّدين في الجدول الرئيسي، ثم سيستخدم هذا الفهرس لاسترجاع النتائج دون مسح الجدول بأكمله.
</p>

<p>
	يمكن تشبيه الفهارس بدليل الهاتف، فإذا أردنا تحديد موقع شخص اسمه <code>John Smith</code> في هذا الدليل، ننتقل أولًا إلى الصفحة الصحيحة التي تسرد الأشخاص الذين تبدأ أسماؤهم بالحرف <code>S</code>، ثم نبحث في الصفحات عن الأشخاص الذين تبدأ أسماؤهم بالحرفين <code>Sm</code>، وبذلك يمكن استبعاد العديد من الإدخالات بسرعة، مع العلم أنها لا تتطابق مع الشخص الذي نبحث عنه. تعمل هذه العملية بنجاح لأن البيانات في دليل الهاتف مرتبة أبجديًا، وهو أمر نادر الحدوث مع البيانات المخزَّنة مباشرةً في قاعدة البيانات. يمثّل الفهرس في محرّك قاعدة البيانات غرضًا مشابهًا لدليل الهاتف، حيث يحتفظ بالمراجع المرتبة أبجديًا إلى البيانات، وبالتالي يساعد قاعدة البيانات في العثور على الصفوف المطلوبة بسرعة.
</p>

<p>
	لاستخدام الفهارس فوائد متعددة، وأكثرها شيوعًا هو تسريع<a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D9%86%D9%89-where-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2258/" rel=""> استعلامات WHERE الشرطية</a>، وفرز البيانات باستخدام تعليمات <code>ORDER BY</code> بسرعة أكبر، وفرض أن تكون القيم فريدة، لكن من ناحية أخرى قد يؤدي استخدام الفهارس إلى تراجع أداء قاعدة البيانات في بعض الظروف، فهي مصممة الفهارس لتسريع استرجاع البيانات وتُنفَّذ باستخدام هياكل بيانات إضافية مخزَّنة مع بيانات الجدول، ويجب تحديث هذه الهياكل عند كل تغيير في قاعدة البيانات، مما قد يؤدي إلى إبطاء أداء استعلامات <code>INSERT</code> و <code>UPDATE</code> و <code>DELETE</code>. لكن إذا كان لدينا مجموعات بيانات كبيرة تتغير كثيرًا، فستتفوق الفوائد الناتجة عن السرعة المُحسَّنة لاستعلامات <code>SELECT</code> أحيانًا على الأداء الأبطأ الملحوظ للاستعلامات التي تكتب البيانات في قاعدة البيانات.
</p>

<p>
	يُفضَّل إنشاء الفهارس عند وجود حاجة واضحة إليها فقط مثل الوقت الذي يبدأ فيه أداء التطبيق في الانخفاض. نحتاج لأن نضع في الاعتبار الاستعلامات التي تُنفَّذ بشكل متكرر وتستغرق وقتًا أطول عند اختيار الفهارس التي سننشئها، ونبني الفهارس بناءً على شروط الاستعلام التي ستستفيد منها أكثر من غيرها.
</p>

<p>
	<strong>ملاحظة</strong>: نركز في هذا المقال على شرح فهارس قاعدة البيانات في MySQL وتوضيح تطبيقاتها الشائعة وأنواعها، حيث يدعم محرّك قاعدة البيانات عدة سيناريوهات أكثر تعقيدًا لاستخدام الفهارس لزيادة أداء قاعدة البيانات، لكن هذا خارج نطاق هذا المقال. ويمكن مطالعة <a href="https://dev.mysql.com/doc/refman/8.0/en/optimization-indexes.html" rel="external nofollow">توثيق MySQL الرسمي حول الفهارس</a> للحصول على معلومات وافية عن مميزات فهارس قاعدة البيانات.
</p>

<p>
	ستنشئ في الأقسام التالية فهارس من أنواع مختلفة لمجموعة من السيناريوهات، وسنتعلم كيفية التحقق من استخدام الفهارس في الاستعلام، وكيفية إزالة الفهارس عند الحاجة.
</p>

<h2 id="singlecolumn">
	استخدام فهارس العمود الواحد Single-Column
</h2>

<p>
	فهرس العمود الواحد هو أحد أكثر أنواع الفهارس شيوعًا ووضوحًا، حيث يمكن استخدامه لتحسين أداء الاستعلام، ويساعد هذا النوع من الفهارس قاعدة البيانات على تسريع الاستعلامات التي ترشّح مجموعة البيانات بناءً على قيم عمود واحد. يمكن للفهارس التي أنشأناها على عمود واحد تسريع العديد من الاستعلامات الشرطية التي تستخدم المطابقات التامة باستخدام المعامل <code>=</code> والمقارنات باستخدام ‎<code>&gt;</code>‎ أو ‎<code>&lt;</code>‎.
</p>

<p>
	لا توجد فهارس في قاعدة البيانات التجريبية التي أنشأناها في خطوة سابقة. سنختبر أولًا كيفية تعامل قاعدة البيانات مع استعلامات <code>SELECT</code> للجدول <code>employees</code> عند استخدام التعليمة <code>WHERE</code> لطلب مجموعة فرعية من البيانات من الجدول فقط قبل إنشاء الفهرس.
</p>

<p>
	لنفترض أننا نريد العثور على الموظفين الذين راتبهم يساوي 100000 دولار أمريكي تمامًا من خلال تنفيذ الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_19" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM employees WHERE salary </span><span class="pun">=</span><span class="pln"> </span><span class="lit">100000</span><span class="pun">;</span></pre>

<p>
	تطلب التعليمة <code>WHERE</code> مطابقة تامة للموظفين الذين يتطابق راتبهم مع القيمة المطلوبة، وستستجيب قاعدة البيانات كما يلي:
</p>

<pre class="ipsCode">الخرج
+-------------+------------+-----------+---------------+--------+
| employee_id | first_name | last_name | device_serial | salary |
+-------------+------------+-----------+---------------+--------+
|           9 | James      | Brown     | YZA567        | 100000 |
+-------------+------------+-----------+---------------+--------+
1 row in set (0.000 sec)
</pre>

<p>
	<strong>ملاحظة</strong>: استجابت قاعدة البيانات استجابةً آنية تقريبًا للاستعلام كما يظهر الخرج السابق، فلن يؤثر استخدام الفهارس بوضوح على أداء الاستعلام مع وجود عدد قليل من الصفوف في قاعدة البيانات، ولكن سنلاحظ تغييرات كبيرة في زمن تنفيذ الاستعلام في حال تنفيذه على مجموعات البيانات الكبيرة.
</p>

<p>
	لا يمكن معرفة كيفية تعامل محرّك قاعدة البيانات مع مسألة العثور على الصفوف المطابقة في الجدول بالاعتماد على خرج الاستعلام فقط، ولكن يوفّر MySQL طريقة لمعرفة الطريقة التي ينفّذ بها المحرّك الاستعلام باستخدام التعليمة <code>EXPLAIN</code>، حيث يمكننا مثلًا الوصول إلى طريقة تنفيذ الاستعلام <code>SELECT</code> من خلال تنفيذ الأمر التالي:
</p>

<pre class="ipsCode">mysql&gt; EXPLAIN SELECT * FROM employees WHERE salary = 100000;
</pre>

<p>
	يخبر الأمر <code>EXPLAIN</code> نظام MySQL بتشغيل استعلام <code>SELECT</code>، ويعرض معلومات حول كيفية إجراء الاستعلام داخليًا إلى جانب إعادة النتائج، وستكون نتيجة التنفيذ مشابهة لما يلي:
</p>

<pre class="ipsCode">الخرج
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | employees | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   20 |    10.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
</pre>

<p>
	توضّح الأعمدة في جدول الخرج السابق العديد من جوانب تنفيذ الاستعلام، وقد يحتوي الخرج على أعمدة إضافية بناءً على إصدار MySQL، وفيما يلي  أهم هذه المعلومات:
</p>

<ul>
	<li>
		يسرد <code>possible_keys </code>الفهارس التي اعتمدها MySQL للاستخدام، حيث لا يوجد فهارس في حالتنا <code>NULL</code>
	</li>
	<li>
		يمثل <code>key </code>الفهرس الذي قرّر MySQL استخدامه عند تنفيذ الاستعلام، حيث لم نستخدم أي فهرس في مثالنا <code>NULL</code>.
	</li>
	<li>
		يحدد <code>rows</code> عدد الصفوف التي يجب على MySQL تحليلها قبل إعادة النتائج، وتبلغ قيمته 20 في مثالنا وهو يمثّل عدد جميع الصفوف الممكنة في الجدول، مما يعني أنه يجب على MySQL مسح جميع الصفوف في الجدول <code>employees</code> للعثور على الصف الوحيد المُعاد
	</li>
	<li>
		يعرض <code>Extra</code> معلومات إضافية تصف خطة الاستعلام، حيث تعني <code>Using where</code> في مثالنا أن قاعدة البيانات رشّحت النتائج مباشرة من الجدول باستخدام التعليمة <code>WHERE</code>
	</li>
</ul>

<p>
	تجدر الإشارة لأنه يجب على قاعدة البيانات مسح 20 صفًا لاسترجاع صف واحد في حالة عدم وجود فهرس، وإذا احتوى الجدول على ملايين الصفوف، فيجب على MySQL المرور عليها واحدًا تلو الآخر، مما يؤدي إلى ضعف أداء الاستعلام.
</p>

<p>
	<strong>ملاحظة</strong>: تعرض إصدارات MySQL الأحدث العبارة ‎<code>1 row in set, 1 warning</code>‎ في الخرج عند استخدام التعليمة <code>EXPLAIN</code>، بينما تعرض إصدارات MySQL الأقدم وقواعد البيانات المتوافقة مع MySQL العبارة <code>‎1 row in set</code>، ولا يُعَد التحذير علامة على وجود مشكلة، حيث يستخدم MySQL آلية التحذيرات الخاصة به لتوفير مزيد من المعلومات الموسَّعة حول خطة الاستعلام. يُعَد هذا الاستخدام لهذه المعلومات الإضافية خارج نطاق هذا المقال، حيث يمكنك معرفة المزيد حول هذا السلوك في صفحة <a href="https://dev.mysql.com/doc/refman/8.0/en/explain-extended.html" rel="external nofollow">تنسيق خرج التعليمة <code>EXPLAIN</code> المُوسَّع</a> في توثيق MySQL.
</p>

<p>
	استخدم استعلام <code>SELECT</code> الذي نفّذته سابقًا شرط المساواة <code>WHERE salary = 100000</code>، ولكن لنتحقق مما إذا كانت قاعدة البيانات ستتصرف بطريقة مماثلة مع شرط المقارنة، ونجرب استرجاع الموظفين الذين راتبهم أقل من 70000:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_21" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM employees WHERE salary </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">70000</span><span class="pun">;</span></pre>

<p>
	أعادت قاعدة البيانات هذه المرة صفين <code>John Smith</code> و <code>Jane Doe</code> كما يلي:
</p>

<pre class="ipsCode">الخرج
+-------------+------------+-----------+---------------+--------+
| employee_id | first_name | last_name | device_serial | salary |
+-------------+------------+-----------+---------------+--------+
|           1 | John       | Smith     | ABC123        |  60000 |
|           2 | Jane       | Doe       | DEF456        |  65000 |
+-------------+------------+-----------+---------------+--------+
8 rows in set (0.000 sec)
</pre>

<p>
	ولكن إذا استخدمنا التعليمة <code>EXPLAIN</code> لفهم تنفيذ الاستعلام كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_23" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> EXPLAIN SELECT </span><span class="pun">*</span><span class="pln"> FROM employees WHERE salary </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">70000</span><span class="pun">;</span></pre>

<p>
	فسنلاحظ أن الجدول مطابق تقريبًا للاستعلام السابق:
</p>

<pre class="ipsCode">الخرج
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | employees | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   20 |    33.33 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
</pre>

<p>
	مسح MySQL جميع الصفوف 20 في الجدول للعثور على الصفوف التي طلبتها باستخدام تعليمة <code>WHERE</code> في الاستعلام كما هو الحال مع الاستعلام السابق. يُعَد عدد الصفوف المُعادة في الخرج السابق صغيرًا مقارنة بعدد جميع الصفوف في الجدول، ولكن يجب على محرّك قاعدة البيانات إنجاز الكثير من العمل للعثور عليها.
</p>

<p>
	يمكن حل هذه المشكلة من خلال إنشاء فهرس للعمود <code>salary</code>، والذي سيخبر MySQL بالحفاظ على هيكل بيانات إضافي ومُحسَّن، وخاصةً لبيانات العمود <code>salary</code> من الجدول <code>employees</code>، لذا ننفّذ الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_25" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE INDEX salary ON employees</span><span class="pun">(</span><span class="pln">salary</span><span class="pun">);</span></pre>

<p>
	تتطلب صيغة التعليمة <code>CREATE INDEX</code> ما يلي:
</p>

<ul>
	<li>
		اسم الفهرس وهو <code>salary</code> في مثالنا، ويجب أن يكون اسم الفهرس فريدًا في الجدول الواحد ويمكن تكراره بجداول مختلفة في قاعدة البيانات
	</li>
	<li>
		اسم الجدول الذي أنشأنا الفهرس له، وهو <code>employees</code> في مثالنا
	</li>
	<li>
		قائمة الأعمدة التي أنشأنا الفهرس لها، حيث استخدمنا في مثالنا عمودًا واحدًا بالاسم <code>salary</code> لبناء الفهرس
	</li>
</ul>

<p>
	قد يظهر الخطأ التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7767_27" style=""><span class="pln"> ERROR 1142 (42000): INDEX command denied to user 'user'@'host' for table 'employees'‎ </span></pre>

<p>
	عند تنفيذ الأمر <code>CREATE INDEX</code> بناءً على أذونات مستخدم MySQL، حيث يمكن منح أذونات <code>INDEX</code> للمستخدم من خلال تسجيل الدخول إلى MySQL كمستخدم جذر وتنفيذ الأوامر التالية مع تعديل اسم مستخدم MySQL والمضيف حسب الحاجة:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_29" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> GRANT INDEX on </span><span class="pun">*.*</span><span class="pln"> TO </span><span class="str">'user'</span><span class="pun">@</span><span class="str">'localhost'</span><span class="pun">;</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> FLUSH PRIVILEGES</span><span class="pun">;</span></pre>

<p>
	نسجّل الخروج كمستخدم جذر ونسجّل الدخول مرة أخرى كمستخدم عادي بعد تحديث أذونات المستخدم، ثم نعيد تشغيل التعليمة <code>CREATE INDEX</code>، ستؤكّد الآن قاعدة البيانات إنشاء الفهرس بنجاح كما يلي:
</p>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.024 sec)
Records: 0  Duplicates: 0  Warnings: 0
</pre>

<p>
	نجرّب تكرار الاستعلامات السابقة للتحقق مما إذا كان هناك أي تغيير عند استخدام الفهرس، لذا نبدأ باسترجاع الموظف الذي يتقاضى راتبًا قدره 100000 بالضبط كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_31" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM employees WHERE salary </span><span class="pun">=</span><span class="pln"> </span><span class="lit">100000</span><span class="pun">;</span></pre>

<p>
	وستبقى النتيجة نفسها مع إعادة الموظف <code>James Brown</code> فقط كما يلي:
</p>

<pre class="ipsCode">الخرج
+-------------+------------+-----------+---------------+--------+
| employee_id | first_name | last_name | device_serial | salary |
+-------------+------------+-----------+---------------+--------+
|           9 | James      | Brown     | YZA567        | 100000 |
+-------------+------------+-----------+---------------+--------+
1 row in set (0.000 sec)
</pre>

<p>
	وإذا طلبنا من MySQL شرحَ كيفية تعامله مع الاستعلام، فسيعرض بعض الاختلافات عمّا سبق، لذا ننفّذ تعليمة <code>EXPLAIN</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_33" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> EXPLAIN SELECT </span><span class="pun">*</span><span class="pln"> FROM employees WHERE salary </span><span class="pun">=</span><span class="pln"> </span><span class="lit">100000</span><span class="pun">;</span></pre>

<p>
	وسيكون الخرج هذه المرة كما يلي:
</p>

<pre class="ipsCode">الخرج
+----+-------------+-----------+------------+------+---------------+--------+---------+-------+------+----------+-------+
| id | select_type | table     | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+--------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | employees | NULL       | ref  | salary        | salary | 5       | const |    1 |   100.00 | NULL  |
+----+-------------+-----------+------------+------+---------------+--------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
</pre>

<p>
	يصرّح MySQL أنه قرّر استخدام المفتاح الذي اسمه <code>salary</code> من المفتاح الوحيد الموضح في العمود <code>possible_keys</code>، وهذا المفتاح هو الفهرس الذي أنشأناه. يعرض العمود <code>rows</code> الآن القيمة 1 بدلًا من 20، حيث تجنّبت قاعدة البيانات مسح جميع الصفوف في قاعدة البيانات ويمكنها إعادة الصف المطلوب مباشرة لأنها استخدمت الفهرس. لا يذكر العمود <code>Extra</code> الآن العبارة <code>Using WHERE</code>، لأن التكرار على الجدول الرئيسي والتحقق من أن كل صف يحقق شرط الاستعلام لم يكن ضروريًا لإجراء الاستعلام.
</p>

<p>
	وكما ذكرنا سابقًا لن نلاحظ تأثير استخدام الفهرس جدًا مع مجموعة بيانات تجريبية صغيرة، ولكن تطلّب الأمر من قاعدة البيانات عملًا أقل بكثير لاسترجاع النتيجة وسيكون تأثير هذا التغيير كبيرًا على مجموعة بيانات أكبر.
</p>

<p>
	نجرّب إعادة تشغيل الاستعلام الثاني واسترجاع الموظفين الذين راتبهم أقل من 70000 للتحقق من استخدام الفهرس، لذا نفّذ الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_35" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM employees WHERE salary </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">70000</span><span class="pun">;</span></pre>

<p>
	نلاحظ إعادة بيانات <code>John Smith</code> و <code>Jane Doe</code> أيضًا كما يلي:
</p>

<pre class="ipsCode">الخرج
+-------------+------------+-----------+---------------+--------+
| employee_id | first_name | last_name | device_serial | salary |
+-------------+------------+-----------+---------------+--------+
|           1 | John       | Smith     | ABC123        |  60000 |
|           2 | Jane       | Doe       | DEF456        |  65000 |
+-------------+------------+-----------+---------------+--------+
8 rows in set (0.000 sec)
</pre>

<p>
	ولكن إذا استخدمنا تعليمة <code>EXPLAIN</code> كما يلي:
</p>

<pre class="ipsCode">mysql&gt; EXPLAIN SELECT * FROM employees WHERE salary &lt; 70000;
</pre>

<p>
	فسيكون الجدول مختلفًا عن التنفيذ السابق للاستعلام نفسه كما يلي:
</p>

<pre class="ipsCode">الخرج
+----+-------------+-----------+------------+-------+---------------+--------+---------+------+------+----------+-----------------------+
| id | select_type | table     | partitions | type  | possible_keys | key    | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+-----------+------------+-------+---------------+--------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | employees | NULL       | range | salary        | salary | 5       | NULL |    2 |   100.00 | Using index condition |
+----+-------------+-----------+------------+-------+---------------+--------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
</pre>

<p>
	يخبرنا العمود <code>key</code> أن MySQL استخدم الفهرس لإجراء الاستعلام، ويخبرنا العمود <code>rows</code> بتحليل صفين فقط لإعادة النتيجة. يحتوي العمود <code>Extra</code> الآن على العبارة <code>Using index condition</code>، مما يعني أن MySQL أجرى ترشيحًا باستخدام الفهرس في هذه الحالة ثم استخدم الجدول الأساسي فقط لاسترجاع الصفوف المطابقة فعليًا.
</p>

<p>
	<strong>ملاحظة</strong>: قد يقرر MySQL عدم استخدام الفهرس في بعض الأحيان حتى في حالة وجود الفهرس وإمكانية استخدامه، فمثلًا إذا نفذنا الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_37" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> EXPLAIN SELECT </span><span class="pun">*</span><span class="pln"> FROM employees WHERE salary </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">140000</span><span class="pun">;</span></pre>

<p>
	فستكون خطة التنفيذ كما يلي:
</p>

<pre class="ipsCode">الخرج
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | employees | NULL       | ALL  | salary        | NULL | NULL    | NULL |   20 |    80.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
</pre>

<p>
	يعني وجود عمود <code>key</code> الفارغ الذي له القيمة <code>NULL</code> أن MySQL قرر عدم استخدام الفهرس، والذي يمكن تأكيده من خلال الصفوف العشرين الممسوحة بالرغم من إدراج الفهرس <code>salary</code> في العمود <code>possible_keys</code>. يحلل مخطِّط استعلام قاعدة البيانات كل استعلام مقابل للفهارس المحتملة لتحديد أسرع مسار للتنفيذ، فإذا كانت تكلفة الوصول إلى الفهرس أكبر من فائدة استخدامه مثل إعادة الاستعلام جزءًا كبيرًا من بيانات الجدول الأصلية، فيمكن لقاعدة البيانات أن تقرر أنه من الأسرع إجراء مسح كامل للجدول فعليًا.
</p>

<p>
	توضّح التعليقات في العمود <code>Extra</code> مثل <code>Using index condition</code> أو <code>Using where</code> كيفية تنفيذ محرّك قاعدة البيانات للاستعلام بمزيد من التفصيل، فقد تختار قاعدة البيانات طريقة أخرى لتنفيذ الاستعلام وقد يكون لدينا خرج مع عدم وجود التعليق <code>Using index condition</code> أو أي تعليق آخر اعتمادًا على السياق. لا يعني ذلك عدم استخدام الفهرس استخدامًا صحيحًا، ولكنه يعني أن قاعدة البيانات قرّرت أن الطريقة الأخرى للوصول إلى الصفوف ستكون أفضل في الأداء.
</p>

<p>
	أنشأنا واستخدمنا في هذا القسم فهارس مؤلفة من عمود واحد لتحسين أداء استعلامات <code>SELECT</code> التي تعتمد على الترشيح لعمود واحد، وسنتعرّف في القسم التالي على كيفية استخدام الفهارس لضمان أن تكون القيم فريدة في عمود معين.
</p>

<h2 id="-1">
	استخدام الفهارس الفريدة لمنع تكرار البيانات
</h2>

<p>
	أحد الاستخدامات الشائعة للفهارس هو استرجاع البيانات بسرعة من خلال مساعدة محرّك قاعدة البيانات على إجراء عمل أقل لتحقيق النتيجة نفسها كما وضّحنا سابقًا، وهناك استخدام آخر وهو ضمان عدم تكرار البيانات في جزء الجدول الذي عرّفنا الفهرس له، وهذا ما يفعله الفهرس الفريد Unique Index.
</p>

<p>
	إن تجنب القيم المكررة ضروري لضمان سلامة البيانات سواءً من وجهة نظر منطقية أو تقنية، فمثلًا لا ينبغي أن يكون هناك شخصان يستخدمان نفس رقم الضمان الاجتماعي، ولا ينبغي لنظام عبر الإنترنت أن يسمح لمستخدمين متعددين أن يسجّلوا باستخدام اسم المستخدم أو عنوان البريد الإلكتروني نفسه.
</p>

<p>
	في حالة جدولنا <code>employees</code> لا ينبغي أن يحتوي حقل الرقم التسلسلي على قيمٍ مكررة وإذا كان الأمر كذلك، فهذا قد يتسبب في منح أكثر من موظف الحاسوب نفسه، ففي هذا الجدول يمكن بسهولة إدخال موظفين جدد مع أرقام تسلسلية مكررة. لنحاول إدخال موظف آخر مع رقم تسلسلي لجهاز قيد الاستخدام كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_39" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO employees VALUES </span><span class="pun">(</span><span class="lit">21</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Sammy'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Smith'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ABC123'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">65000</span><span class="pun">);</span></pre>

<p>
	ستدرج قاعدة البيانات هذا الصف وتعلمنا بنجاح العملية كما يلي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.009 sec)
</pre>

<p>
	فإذا استعلمنا عن الموظفين باستخدام الحاسوب ذي الرقم التسلسلي <code>ABCD123</code> كما يلي:
</p>

<pre class="ipsCode">mysql&gt; SELECT * FROM employees WHERE device_serial = 'ABC123';
</pre>

<p>
	فسنحصل على شخصين مختلفين كما توضح النتيجة التالية:
</p>

<pre class="ipsCode">الخرج
+-------------+------------+-----------+---------------+--------+
| employee_id | first_name | last_name | device_serial | salary |
+-------------+------------+-----------+---------------+--------+
|           1 | John       | Smith     | ABC123        |  60000 |
|          21 | Sammy      | Smith     | ABC123        |  65000 |
+-------------+------------+-----------+---------------+--------+
2 rows in set (0.000 sec)
</pre>

<p>
	هذا ليس سلوكًا متوقعًا لإبقاء قاعدة بيانات <code>employees</code> صالحة. لذا سنتراجع عن هذا التغيير من خلال حذف الصف الأخير الذي أنشأناه كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_41" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> DELETE FROM employees WHERE employee_id </span><span class="pun">=</span><span class="pln"> </span><span class="lit">21</span><span class="pun">;</span></pre>

<p>
	يمكنك التأكد من ذلك من خلال إعادة تشغيل استعلام <code>SELECT</code> السابق كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_43" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM employees WHERE device_serial </span><span class="pun">=</span><span class="pln"> </span><span class="str">'ABC123'</span><span class="pun">;</span></pre>

<p>
	وبالتالي أصبح الموظف <code>John Smith</code> المستخدم الوحيد للجهاز الذي رقمه التسلسلي <code>ABC123</code> مرة أخرى:
</p>

<pre class="ipsCode">الخرج
+-------------+------------+-----------+---------------+--------+
| employee_id | first_name | last_name | device_serial | salary |
+-------------+------------+-----------+---------------+--------+
|           1 | John       | Smith     | ABC123        |  60000 |
+-------------+------------+-----------+---------------+--------+
1 row in set (0.000 sec)
</pre>

<p>
	لننشئ الآن فهرسًا فريدًا للعمود <code>device_serial</code> لحماية قاعدة البيانات من مثل هذه الأخطاء من خلال تنفيذ التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_45" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE UNIQUE INDEX device_serial ON employees</span><span class="pun">(</span><span class="pln">device_serial</span><span class="pun">);</span></pre>

<p>
	توجِّه إضافة الكلمة المفتاحية <code>UNIQUE</code> عند إنشاء الفهرس قاعدةَ البيانات للتأكد من عدم تكرار القيم في العمود <code>device_serial</code>، حيث تؤدي الفهارس الفريدة إلى التحقق من جميع الصفوف الجديدة المضافة إلى الجدول مقابل الفهرس لتحديد ما إذا كانت قيمة العمود تتوافق مع القيد أم لا.
</p>

<p>
	وستؤكد قاعدة البيانات إنشاء الفهرس كما يلي:
</p>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.021 sec)
Records: 0  Duplicates: 0  Warnings: 0
</pre>

<p>
	نتحقق الآن من إمكانية إضافة إدخال مكرر إلى الجدول من خلال تشغيل استعلام <code>INSERT</code> من جديد:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_48" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO employees VALUES </span><span class="pun">(</span><span class="lit">21</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Sammy'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Smith'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ABC123'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">65000</span><span class="pun">);</span></pre>

<p>
	ستظهر رسالة الخطأ التالية هذه المرة:
</p>

<pre class="ipsCode">الخرج
ERROR 1062 (23000): Duplicate entry 'ABC123' for key 'device_serial'
</pre>

<p>
	يمكن التحقق من عدم إضافة الصف الجديد إلى الجدول باستخدام استعلام <code>SELECT</code> مرة أخرى:
</p>

<pre class="ipsCode">mysql&gt; SELECT * FROM employees WHERE device_serial = 'ABC123';
</pre>

<p>
	وسيُعاد صف واحد فقط هذه المرة:
</p>

<pre class="ipsCode">الخرج
+-------------+------------+-----------+---------------+--------+
| employee_id | first_name | last_name | device_serial | salary |
+-------------+------------+-----------+---------------+--------+
|           1 | John       | Smith     | ABC123        |  60000 |
+-------------+------------+-----------+---------------+--------+
1 row in set (0.000 sec)
</pre>

<p>
	تعمل الفهارس الفريدة على الحماية من الإدخالات المكررة، وهي أيضًا فهارس وظيفية بالكامل لتسريع الاستعلامات. ويستخدم محرّك قاعدة البيانات الفهارس الفريدة باستخدام الطريقة نفسها في الخطوة السابقة، حيث يمكننا التحقق من ذلك من خلال تنفيذ التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7767_50" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> EXPLAIN SELECT </span><span class="pun">*</span><span class="pln"> FROM employees WHERE device_serial </span><span class="pun">=</span><span class="pln"> </span><span class="str">'ABC123'</span><span class="pun">;</span></pre>

<p>
	وستكون نتيجة التنفيذ مشابهة لما يلي:
</p>

<pre class="ipsCode">الخرج
+----+-------------+-----------+------------+-------+---------------+---------------+---------+-------+------+----------+-------+
| id | select_type | table     | partitions | type  | possible_keys | key           | key_len | ref   | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | employees | NULL       | const | device_serial | device_serial | 63      | const |    1 |   100.00 | NULL  |
+----+-------------+-----------+------------+-------+---------------+---------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
</pre>

<p>
	يظهَر الفهرس <code>device_serial</code> في العمودين <code>possible_keys</code> و <code>key</code>، مما يؤكّد استخدام الفهرس عند تنفيذ الاستعلام.
</p>

<p>
	بهذا تعلمنا استخدام الفهارس الفريدة Unique Index للحماية من البيانات المكررة في قاعدة البيانات، وسنستخدم في القسم التالي الفهارس التي تمتد إلى أكثر من عمود واحد.
</p>

<h2 id="-4">
	الخلاصة
</h2>

<p>
	تعلّمنا في هذا المقال ما هي الفهارس واستعرضنا أمثلة متعددة على فهارس العمود الواحد المستخدمة لتسريع استرجاع البيانات من خلال استعلامات <code>SELECT</code> الشرطية، أو للحفاظ على جعل بيانات العمود فريدة، وسنشرح في المقال التالي المزيد حول الفهارس ونوضح كيفية تعريف فهارس متعددة الأعمدة Indexes on Multiple Columns وحالات استخدامها، كما ننصح بالاطلاع على <a href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20%D8%AA%D8%B9%D9%84%D9%85%20sql/" rel="">سلسلة تعلم SQL</a> للمزيد حول التعامل مع لغة SQL.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-indexes-in-mysql" rel="external nofollow">How To Use Indexes in MySQL</a> لصاحبَيه Mateusz Papiernik و Rachel Lee.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D9%81%D9%87%D8%A7%D8%B1%D8%B3-indexes-%D9%81%D9%8A-sql-r589/" rel="">الفهارس Indexes في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%86%D8%B8%D8%B1%D8%A9-%D8%B3%D8%B1%D9%8A%D8%B9%D8%A9-%D8%B9%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D9%87%D9%8A%D9%83%D9%84%D9%8A%D8%A9-sql-r1368/" rel="">نظرة سريعة على لغة الاستعلامات الهيكلية SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%81%D9%87%D9%85-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D9%82%D9%8A%D8%A9-r2216/" rel="">فهم قواعد البيانات العلاقية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%A3%D8%AF%D8%A7%D8%A1-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-sql-%D9%84%D9%84%D9%85%D8%B7%D9%88%D8%B1%D9%8A%D9%86-r383/" rel="">تحسين أداء قواعد بيانات SQL للمطورين</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2523</guid><pubDate>Fri, 28 Feb 2025 15:00:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x642;&#x648;&#x627;&#x62F;&#x62D; Triggers &#x641;&#x64A; SQL</title><link>https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%AF%D8%AD-triggers-%D9%81%D9%8A-sql-r2520/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_02/MySQL.png.2c537c3ae9bd4a8e9deab96190f05f1e.png" /></p>
<p>
	عند العمل مع قواعد البيانات العلاقية Relational Databases ولغة الاستعلام البنيوية SQL فإننا نجري معظم العمليات على البيانات الناتجة عن استعلامات منفَّذة صراحةً مثل استعلامات <code>SELECT</code> أو <code>INSERT</code> أو <code>UPDATE</code>. لكن يمكننا توجيه قواعد بيانات SQL لتنفيذ إجراءات مُعرَّفة مسبقًا تلقائيًا في كل مرة يقع فيها حدث معين باستخدام القوادح أو محفّزات التنفيذ Triggers. يمكننا مثلًا استخدام هذه القوادح للاحتفاظ بسجل يتضمن جميع تعليمات الحذف <code>DELETE</code> بحيث نحفظ بعد كل عملية حدث تقع تفاصيل هذه العملية ومن قام بها ومتى، كما يمكن استخدامها لتحديث البيانات التراكمية مثل المجموع أو المتوسط حيث يمكننا تحديث هذه البيانات الإحصائية كلما جرت عملية إضافة أو تحديث على البيانات الموجودة.
</p>

<p>
	سنستخدم في هذا المقال قوادح SQL مختلفة لتنفيذ الإجراءات تلقائيًا عندما ندرج الصفوف أو نحدثها أو نحذفها.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	يجب توفر حاسوب يشغّل <a href="https://academy.hsoub.com/devops/servers/databases/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D9%82%D9%8A%D8%A9-sqlite-%D9%85%D8%B9-mysql-%D9%85%D8%B9-postgresql-r72/" rel="">نظام إدارة قواعد بيانات علاقية RDBMS</a> مستند إلى لغة SQL. وقد اختبرنا التعليمات والأمثلة الواردة في هذا المقال باستخدام البيئة التالية:
</p>

<ul>
	<li>
		خادم عامل على توزيعة أوبنتو Ubuntu مع مستخدم ذي صلاحيات مسؤول مختلف عن المستخدم الجذر، وجدار حماية مضبوط باستخدام أداة UFW كما هو موضح في <a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04" rel="external nofollow">دليل الإعداد الأولي للخادم مع الإصدار 20.04 من أوبنتو</a>، ومقال <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A9-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-%D9%85%D9%86-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A8%D8%A3%D8%A8%D8%B3%D8%B7-%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-r575/" rel="">كيفية تثبيت توزيعة أوبنتو من لينكس بأبسط طريقة</a>
	</li>
	<li>
		نظام MySQL مُثبَّت ومؤمَّن على الخادم كما هو موضح في مقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبنتو</a>، وقد نفذنا خطوات هذا المقال باستخدام مستخدم MySQL مختلف عن المستخدم الجذر وفق الطريقة الموضحة في الخطوة التالية من المقال
	</li>
	<li>
		معرفة أساسية بتنفيذ استعلامات <code>SELECT</code> و <code>INSERT</code> و <code>UPDATE</code> و <code>DELETE</code> لمعالجة البيانات في قاعدة البيانات كما هو موضح في مقال <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%B9%D9%86-%D8%A7%D9%84%D8%B3%D8%AC%D9%84%D8%A7%D8%AA-%D9%85%D9%86-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r2249/" rel="">كيفية الاستعلام عن السجلات من الجداول في SQL</a> و<a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D8%AF%D8%B1%D8%A7%D8%AC-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r2226/" rel="">كيفية إدراج البيانات في SQL</a> و<a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AD%D8%AF%D9%8A%D8%AB-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2242/" rel="">تحديث البيانات في لغة الاستعلام البنيوية SQL</a> و<a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AD%D8%B0%D9%81-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2246/" rel="">حذف البيانات في لغة الاستعلام البنيوية SQL</a>
	</li>
	<li>
		معرفة أساسية باستخدام الاستعلامات المتداخلة كما هو موضَّح في مقال كيفية <a href="https://academy.hsoub.com/programming/sql/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AA%D8%AF%D8%A7%D8%AE%D9%84%D8%A9-nested-queries-%D9%81%D9%8A-sql-r2505/" rel="">استخدام الاستعلامات المتداخلة في لغة SQL</a>
	</li>
	<li>
		المعرفة الأساسية باستخدام الدوال الرياضية التجميعية كما هو موضَّح في مقال <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%AC%D9%85%D9%8A%D8%B9%D9%8A%D8%A9-%D9%81%D9%8A-sql-r2410/" rel="">كيفية استخدام التعابير الرياضية والدوال التجميعية في SQL</a>
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: تجدر الإشارة إلى أنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية RDBMS لها تقديماتها الفريدة من لغة SQL. فلا يفرض معيار SQL صيغةً للقوادح Triggers أو طريقة صارمة لتحقيقها بالرغم من أنها تُعَد جزءًا من هذا المعيار، لذا يختلف تقديمها من قاعدة البيانات إلى أخرى، وتستخدم الأوامر الموضَّحة في هذا المقال صيغة قاعدة بيانات <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a> وقد لا تعمل على محرّكات قواعد البيانات الأخرى.
</p>

<p>
	سنحتاج أيضًا إلى قاعدة بيانات تحتوي على بعض الجداول المُحمَّلة ببيانات تجريبية نموذجية لنتمكّن من التدرب على استخدام المحفّزات، وفي القسم التالي نوضح تفاصيل الاتصال بخادم MySQL وإنشاء قاعدة بيانات تجريبية لاستخدامها في أمثلة هذا المقال.
</p>

<h2 id="mysql">
	الاتصال بخادم MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	سنتصل في هذا القسم بخادم MySQL وننشئ قاعدة بيانات تجريبية لتطبيق الأمثلة الواردة في هذا المقال، حيث سنستخدم قاعدة بيانات تحفظ مجموعة هدايا تذكارية افتراضية، ونخزّن كل تفاصيل الهدايا التذكارية المملوكة حاليًا، وقيمتها الإجمالية المتاحة ونحتاج للتأكد من أن إجراء حذف الهدايا التذكارية سيُحفَظ في سجل دائم يوضح كافة تفاصيل عملية الحذف.
</p>

<p>
	إذا كان نظام قاعدة بيانات SQL الخاص بنا يعمل على خادم بعيد، نحتاج للاتصال بالخادم باستخدام بروتوكول <a href="https://academy.hsoub.com/devops/security/ssh/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr></a> من جهازنا المحلي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_9" style=""><span class="pln">$ ssh user@your_server_ip</span></pre>

<p>
	ثم نفتح واجهة <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر أوامر</a> خادم MySQL مع وضع اسم حساب مستخدم MySQL الخاص بنا مكان <code>user</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_11" style=""><span class="pln">$ mysql </span><span class="pun">-</span><span class="pln">u user </span><span class="pun">-</span><span class="pln">p</span></pre>

<p>
	ننشئ قاعدة بيانات بالاسم <code>collectibles</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_3253_17" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE DATABASE collectibles</span><span class="pun">;</span></pre>

<p>
	إذا أُنشئِت قاعدة البيانات بنجاح، فسيظهر الخرج التالي:
</p>

<pre class="ipsCode" id="ips_uid_4741_27">Query OK, 1 row affected (0.01 sec)</pre>

<p>
	يمكن اختيار قاعدة البيانات <code>collectibles</code> من خلال تنفيذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_3253_20" style=""><span class="pln">$ USE collectibles</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode" id="ips_uid_4741_31">Database changed
</pre>

<p>
	اخترنا قاعدة البيانات، وسننشئ عدة جداول تجريبية ضمنها، حيث سيحتوي الجدول <code>collectibles</code> على بيانات مبسَّطة عن الهدايا التذكارية الموجودة في قاعدة البيانات، ويتضمن الجدول الأعمدة التالية:
</p>

<ul>
	<li>
		<code>name</code>: يخزّن اسم كل هدية تذكارية، ويستخدم نوع البيانات <code>varchar</code> بحد أقصى 50 محرفًا
	</li>
	<li>
		<code>value</code>: يخزّن قيمة الهدية التذكارية، ويستخدم نوع البيانات <code>decimal</code> بحد أقصى 5 قيم قبل الفاصلة العشرية وقيمتين بعدها
	</li>
</ul>

<p>
	أنشئ هذا الجدول التجريبي باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_21" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE collectibles </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     name varchar</span><span class="pun">(</span><span class="lit">50</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="kwd">value</span><span class="pln"> </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	إذا كان الخرج كما يلي، فهذا يعني إنشاء الجدول بنجاح:
</p>

<pre class="ipsCode" id="ips_uid_4741_33">Query OK, 0 rows affected (0.00 sec)
</pre>

<p>
	سنسمّي الجدول الثاني باسم <code>collectibles_stats</code> وسنستخدمه لتتبّع القيمة المتراكمة لجميع الهدايا التذكارية في المجموعة، وسيحتوي هذا الجدول على صف واحد من البيانات مع الأعمدة التالية:
</p>

<ul>
	<li>
		<code>count</code>: يحتوي عدد الهدايا التذكارية المملوكة، ونمثّله باستخدام نوع البيانات <code>int</code>
	</li>
	<li>
		<code>value</code>: يخزّن القيمة المتراكمة لجميع الهدايا باستخدام نوع البيانات <code>decimal</code> بحد أقصى 5 قيم قبل الفاصلة العشرية وقيمتين بعدها
	</li>
</ul>

<p>
	أنشئ هذا الجدول التجريبي باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_25" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE collectibles_stats </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     count </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="kwd">value</span><span class="pln"> </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	إذا كان الخرج كما يلي، فهذا يعني إنشاء الجدول بنجاح:
</p>

<pre class="ipsCode" id="ips_uid_4741_35">Query OK, 0 rows affected (0.00 sec)
</pre>

<p>
	سنسمي الجدول الثالث والأخير بالاسم <code>collectibles_archive</code>، والذي سيتتبّع جميع الهدايا التذكارية المحذوفة من المجموعة لضمان عدم اختفائها أبدًا، وسيحتوي على بيانات مشابهة للجدول <code>collectibles</code> مع تاريخ الإزالة، وسيستخدم الأعمدة التالية:
</p>

<ul>
	<li>
		<code>name</code>: يحتوي اسم كل هدية تذكارية محذوفة، ونمثّله باستخدام نوع البيانات <code>varchar</code> بحد أقصى 50 محرفًا
	</li>
	<li>
		<code>value</code>: يخزّن قيمة الهدايا التذكارية لحظة الحذف باستخدام نوع البيانات <code>decimal</code> بحد أقصى 5 قيم قبل الفاصلة العشرية وقيمتين بعدها
	</li>
	<li>
		<code>removed_on</code>: يخزّن تاريخ ووقت الحذف لكل هدية تذكارية مؤرشفة باستخدام نوع البيانات <code>timestamp</code> باستخدام القيمة الافتراضية <code>NOW()‎</code> التي تعني التاريخ الحالي لإدراج صف جديد في هذا الجدول
	</li>
</ul>

<p>
	أنشئ هذا الجدول التجريبي باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_37" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE collectibles_archive </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     name varchar</span><span class="pun">(</span><span class="lit">50</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     </span><span class="kwd">value</span><span class="pln"> </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     removed_on timestamp DEFAULT CURRENT_TIMESTAMP
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	إذا كان الخرج كما يلي، فهذا يعني إنشاء الجدول بنجاح:
</p>

<pre class="ipsCode" id="ips_uid_4741_39">Query OK, 0 rows affected (0.00 sec)
</pre>

<p>
	نحمّل بعد ذلك الجدول <code>collectibles_stats</code> بالبيانات الأولية لمجموعة الهدايا التذكارية من خلال تنفيذ عملية <code>INSERT INTO</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_42" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO collectibles_stats SELECT COUNT</span><span class="pun">(</span><span class="pln">name</span><span class="pun">),</span><span class="pln"> SUM</span><span class="pun">(</span><span class="kwd">value</span><span class="pun">)</span><span class="pln"> FROM collectibles</span><span class="pun">;</span></pre>

<p>
	تضيف عملية <code>INSERT INTO</code> السابقة صفًا واحدًا إلى الجدول <code>collectibles_stats</code> مع القيم المحسوبة باستخدام <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%AC%D9%85%D9%8A%D8%B9%D9%8A%D8%A9-%D9%81%D9%8A-sql-r2410/" rel="">الدوال التجميعية Aggregate Functions</a> لحساب عدد الصفوف في الجدول <code>collectibles</code> ولجمع قيم جميع الهدايا التذكارية باستخدام العمود <code>value</code> والدالة <code>SUM</code>. يشير الخرج التالي إلى إضافة الصف بنجاح:
</p>

<pre class="ipsCode" id="ips_uid_4741_44">Query OK, 1 row affected (0.002 sec)
Records: 1  Duplicates: 0  Warnings: 0
</pre>

<p>
	يمكننا التحقق من ذلك من خلال تنفيذ تعليمة <code>SELECT</code> التالية مع الجدول <code>collectibles_stats</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_46" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM collectibles_stats</span><span class="pun">;</span></pre>

<p>
	لا توجد هدايا تذكارية في قاعدة البيانات حتى الآن، لذا يكون العدد الأولي للعناصر هو 0 وتكون القيمة المتراكمة هي <code>NULL</code> كما يلي:
</p>

<pre class="ipsCode" id="ips_uid_4741_48">+-------+-------+
| count | value |
+-------+-------+
|     0 |  NULL |
+-------+-------+
1 row in set (0.000 sec)
</pre>

<p>
	نحن الآن جاهزون لمتابعة هذا المقال والبدء باستخدام القوادح Triggers في MySQL.
</p>

<h2 id="triggers">
	فهم Triggers
</h2>

<p>
	القوادح Triggers هي تعليمات مُعرَّفة من أجل جدول معين تنفّذها قاعدة البيانات تلقائيًا في كل مرة يقع فيها حدث محدد في هذا الجدول، ويمكننا استخدامها لضمان تنفيذ بعض الإجراءات بتناسق في كل مرة نُنفَّذ فيها تعليمة معينة مع الجدول بدلًا من أن يحتاج مستخدمو قاعدة البيانات لتنفيذها يدويًا.
</p>

<p>
	يُعرَّف كل Trigger مرتبط بجدول باسم يحدده المستخدم، وشرطين لتوجيه محرك قاعدة البيانات وإعلامه بالوقت المناسب لتنفيذ القادح، ويمكن تجميع هذين الشرطين ضمن فئتين منفصلتين هما:
</p>

<ul>
	<li>
		<strong>حدث قاعدة البيانات</strong>: يمكن تنفيذ القوادح عند تشغيل تعليمات <code>INSERT</code> أو <code>UPDATE</code> أو <code>DELETE</code> مع الجدول
	</li>
	<li>
		<strong>وقت الحدث</strong>: يمكن تنفيذ القوادح أيضًا قبل <code>BEFORE</code> أو بعد <code>AFTER</code> التعليمة المحدَّدة
	</li>
</ul>

<p>
	يؤدي الجمع بين مجموعتي الشروط السابقتين لستة احتمالات منفصلة للقوادح التي تُنفَّذ تلقائيًا في كل مرة يتحقق فيها الشرط المشترك. القوادح التي تحدث قبل تنفيذ التعليمة التي تحقق الشرط هي <code>BEFORE INSERT</code> و <code>BEFORE UPDATE</code> و <code>BEFORE DELETE</code>، ويمكن استخدامها لمعالجة البيانات والتحقق من صحتها قبل إدراجها أو تحديثها في الجدول أو لحفظ تفاصيل الصف المحذوف لأغراض التدقيق أو الأرشفة. المحفّزات التي تحدث بعد تنفيذ التعليمة التي تحقق الشرط هي <code>AFTER INSERT</code> و <code>AFTER UPDATE</code> و <code>AFTER DELETE</code>، ويمكن استخدامها لتحديث القيم الملخَّصة في جدول منفصل بناءً على الحالة النهائية لقاعدة البيانات بعد التعليمة.
</p>

<p>
	يمكن تنفيذ إجراءات مثل التحقق من صحة بيانات الدخل، ومعالجتها، أو أرشفة الصف المحذوف، إذ تسمح قاعدة البيانات بالوصول إلى قيم البيانات من داخل القوادح، ويمكن استخدام البيانات المدرَجة حديثًا فقط بالنسبة لقوادح <code>INSERT</code>، ويمكن الوصول إلى كلٍّ من البيانات الأصلية والمُحدَّثة بالنسبة لمحفّزات <code>UPDATE</code>، وتكون بيانات الصف الأصلية فقط متاحة للاستخدام بالنسبة لمحفّزات <code>DELETE</code> نظرًا لعدم وجود بيانات جديدة للإشارة إليها.
</p>

<p>
	يمكن الوصول للبيانات المُستخدَمة في جسم القادح ضمن السجل <code>OLD</code> بالنسبة للبيانات الموجودة حاليًا في قاعدة البيانات والسجل <code>NEW</code> بالنسبة للبيانات التي سيحفظها الاستعلام، ويمكن الإشارة إلى الأعمدة الفردية باستخدام الصيغة <code>OLD.column_name</code> و <code>OLD.column_name</code>.
</p>

<p>
	يوضّح المثال التالي الصيغة العامة لتعليمة SQL المُستخدَمة لإنشاء قادح جديد:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_50" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TRIGGER trigger_name trigger_condition
mysql</span><span class="pun">&gt;</span><span class="pln"> ON table_name
mysql</span><span class="pun">&gt;</span><span class="pln"> FOR EACH ROW
mysql</span><span class="pun">&gt;</span><span class="pln"> trigger_actions</span><span class="pun">;</span></pre>

<p>
	لنشرح التعليمة السابقة بالتفصيل:
</p>

<ul>
	<li>
		<strong><code>CREATE TRIGGER</code></strong>: اسم تعليمة SQL المُستخدَمة لإنشاء قادح جديد في قاعدة البيانات
	</li>
	<li>
		<strong><code>trigger_name</code></strong>: هو الاسم الذي يحدّده المستخدم للقادح، ويصف دوره مثل استخدام أسماء الجداول وأسماء الأعمدة لوصف معناها
	</li>
	<li>
		<strong><code>ON table_name</code></strong>: نخبر قاعدة البيانات بأن القادح يجب أن يراقب الأحداث التي تحدث في الجدول <code>table_name</code>
	</li>
	<li>
		<strong><code>trigger_condition</code></strong>: أحد الاختيارات الستة المُحتملة التي تحدد متى يجب تشغيل القادح مثل <code>BEFORE INSERT</code>.
	</li>
	<li>
		<strong><code>FOR EACH ROW</code></strong>: تخبر قاعدة البيانات بأنه يجب تشغيل القادح لكل صف يتأثر بالحدث. تدعم بعض قواعد البيانات أنماطًا إضافية للتنفيذ مختلف عن النمط <code>FOR EACH ROW</code>، ولكن تشغيل التعليمات من جسم القادح لكل صف متأثر بالتعليمة التي تسبّبت في تنفيذ القادح هو الخيار الوحيد في حال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>
	</li>
	<li>
		<strong><code>trigger_actions</code></strong>: جسم القادح الذي يحدّد ما يحدث عند تنفيذه، وهو تعليمة SQL واحدة، ويمكن تضمين تعليمات متعددة في جسم القادح لإجراء عمليات معقدة باستخدام الكلمات المفتاحية <code>BEGIN</code> و <code>END</code> لتضمين التعليمات ضمن كتلة، ولكن ذلك خارج نطاق هذا المقال
	</li>
</ul>

<p>
	اطّلع على <a href="https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html" rel="external nofollow">التوثيق الرسمي للمحفّزات</a> لمعرفة المزيد حول الصيغة المُستخدمَة لتعريف القوادح. سننشئ في القسم التالي أمثلة على قوادح تعالج البيانات قبل إجراء عمليتي <code>INSERT</code> و <code>UPDATE</code>.
</p>

<h2 id="beforeinsertbeforeupdate">
	معالجة البيانات باستخدام محفزات BEFORE INSERT و BEFORE UPDATE
</h2>

<p>
	سنستخدم في هذا القسم قوادح لمعالجة البيانات قبل تنفيذ تعليمات <code>INSERT</code> و <code>UPDATE</code>، حيث سنستخدم المحفّزات في للتأكّد من أن جميع الهدايا التذكارية في قاعدة البيانات تستخدم أسماءً بحروف كبيرة لتحقيق التناسق. في حال لم نستخدم قوادح سيتوجب علينا تذكّر استخدام أسماء الهدايا التذكارية بحروف كبيرة لكل تعليمة <code>INSERT</code> و <code>UPDATE</code>، وإذا نسينا، فستحتفظ قاعدة البيانات بالبيانات كما هي وهذا قد يؤدي إلى حدوث أخطاء محتملة في مجموعة البيانات.
</p>

<p>
	لنبدأ بإدخال مثال لعنصر من الهدايا التذكارية بالاسم <code>spaceship model</code> وبقيمة 12.50 دولار، وسنكتب اسم العنصر بحروف صغيرة لتوضيح المشكلة. لننفّذ التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_52" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO collectibles VALUES </span><span class="pun">(</span><span class="str">'spaceship model'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12.50</span><span class="pun">);</span></pre>

<p>
	تؤكد الرسالة التالية إضافة العنصر:
</p>

<pre class="ipsCode" id="ips_uid_4741_54">Query OK, 1 row affected (0.009 sec)
</pre>

<p>
	يمكننا التحقق من إدراج الصف من خلال تنفيذ استعلام <code>SELECT</code> التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_56" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM collectibles</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode" id="ips_uid_4741_58">+-----------------+-------+
| name            | value |
+-----------------+-------+
| spaceship model | 12.50 |
+-----------------+-------+
1 row in set (0.000 sec)
</pre>

<p>
	حُفِظ هذا العنصر كما هو مع كتابة اسمه بحروف صغيرة فقط. لنتأكد من كتابة جميع الهدايا التذكارية اللاحقة بحروف كبيرة دائمًا من خلال إنشاء قادح باسم <code>BEFORE INSERT</code> لمعالجة البيانات المُمرَّرة إلى قاعدة البيانات قبل حدوثها. نشغّل الآن التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_3742_9" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TRIGGER uppercase_before_insert BEFORE INSERT
mysql</span><span class="pun">&gt;</span><span class="pln"> ON collectibles
mysql</span><span class="pun">&gt;</span><span class="pln"> FOR EACH ROW
mysql</span><span class="pun">&gt;</span><span class="pln"> SET NEW</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> UPPER</span><span class="pun">(</span><span class="pln">NEW</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span></pre>

<p>
	ينشئ الأمر السابق قادح باسم <code>uppercase_before_insert</code>، والذي سيُنفَّذ قبل<code> </code>كافة تعليمات <code>INSERT</code> في الجدول <code>collectibles</code>. تُنفَّذ التعليمة الموجودة في القادح<code>SET NEW.name = UPPER(NEW.name)‎</code> لكل صف مدرج، ويسند أمر SQL الذي هو <code>SET</code> القيمة الموجودة على الجانب الأيمن إلى الجانب الأيسر، حيث يمثل <code>NEW.name</code> قيمة العمود <code>name</code> الذي ستحفظه تعليمة الإدراج. نحوّل حالة الحروف للقيمة التي ستُحفَظ في قاعدة البيانات من خلال تطبيق الدالة <code>UPPER</code> على اسم الهدية وإسناده مرة أخرى لقيمة العمود.
</p>

<p>
	<strong>ملاحظة</strong>: قد تظهر رسالة خطأ مشابهة للخطأ التالي عند تشغيل الأمر <code>CREATE TRIGGER</code>.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3742_15" style=""><span class="pln">ERROR 1419 (HY000): You do not have the SUPER privilege, and binary logging is enabled
(you might want to use the less safe log_bin_trust_function_creators variable)</span></pre>

<p>
	التسجيل الثنائي Binary Logging هو آلية تُسجل كل التعديلات التي تتم على قاعدة البيانات مثل إضافة أو تعديل أو حذف بيانات في سجل ثنائي، ويحتوي هذا السجل على أحداث Events تصف التعديلات التي حدثت وتحفظها بتنسيق ثنائي يمكن معالجته، وهذا التسجيل يكون مفعَّلًا افتراضيًا في محرّك قاعدة بيانات MySQL وذلك بدءًا من الإصدار MySQL 8، حيث يتعقّب التسجيل الثنائي جميع تعليمات SQL التي تعدّل محتويات قاعدة البيانات في صيغة أحداث محفوظة تَصِف هذه التعديلات، وتُستخدَم هذه السجلات في النسخ المتماثل Replication لقاعدة البيانات للحفاظ على مزامنة النسخ المتماثلة لقاعدة البيانات وأثناء استعادة البيانات في الوقت المناسب.
</p>

<p>
	لكن لا يسمح MySQL بإنشاء القوادح Triggers والإجراءات المخزَّنة Stored Procedures في حال تفعيل تسجيل التعديلات أو الأحداث بصيغة ثنائية كإجراء احترازي لضمان سلامة البيانات وتكاملها في بيئات النسخ المتماثل Replication، ولكن فهم كيفية تأثير القوادح والإجراءات المُخزَّنة على النسخ المتماثل خارج نطاق هذا المقال.
</p>

<p>
	يمكننا تجاوز القيود التي يفرضها MySQL عند تفعيل التسجيل الثنائي Binary Logging، وذلك لأغراض التعلم أو الاختبار في بيئة محلية على جهازنا الشخصي، ولكن لن يستمر هذا الإعداد الذي عدّلناه بالعمل وسيعود للقيمة الأصلية عند إعادة تشغيل خادم MySQL. يمكن تجاوز الإعداد الافتراضي من خلال تعديل الإعدادات الخاصة بـ MySQL كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_62" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SET GLOBAL log_bin_trust_function_creators </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span></pre>

<p>
	يتحكم الإعداد <code>log_bin_trust_function_creators</code> بإمكانية الوثوق بالمستخدمين الذين ينشئون القوادح والدوال المخزنة بحيث لا ينشئون قوادح تتسبب في كتابة أحداث غير آمنة في السجل الثنائي. تكون قيمة الإعداد الافتراضية هي 0، مما يسمح للمستخدمين الذين يتمتعون بصلاحيات مميزة فقط بإنشاء قوادح في البيئة التي فعّلنا فيها تسجيل التعديلات أو الأحداث بصيغة ثنائية، وإذا عدّلنا القيمة إلى 1، فسنثق بأيّ مستخدم ينشئ تعليمات <code>CREATE TRIGGER</code> لفهم النتائج.
</p>

<p>
	جرى الآن تحديث الإعداد، لنعمل إذًا على تسجيل الخروج كمستخدم جذر، وتسجيل الدخول مرة أخرى كمستخدم عادي، ونعيد تشغيل تعليمة <code>CREATE TRIGGER</code>. ويمكن الاطلاع على توثيق MySQL الرسمي: <a href="https://dev.mysql.com/doc/refman/8.0/en/binary-log.html" rel="external nofollow">السجل الثنائي</a> و <a href="https://dev.mysql.com/doc/refman/8.0/en/stored-programs-logging.html" rel="external nofollow">تسجيل التعديلات أو الأحداث بصيغة ثنائية للبرنامج المخزن</a>، كما يمكن مطالعة مقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D8%A7%D9%84%D9%86%D8%B3%D8%AE-%D8%A7%D9%84%D9%85%D8%AA%D9%85%D8%A7%D8%AB%D9%84-master-slave-%D9%84%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D9%86%D9%81%D8%B3-%D8%A7%D9%84%D8%AC%D9%87%D8%A7%D8%B2-r386/" rel="">كيفية إعداد النسخ المتماثل في MySQL</a> لمعرفة المزيد حول تسجيل التعديلات أو الأحداث بصيغة ثنائية والنسخ المتماثل في MySQL وارتباطه بالقوادح.
</p>

<p>
	<strong>ملاحظة</strong>: قد نتلقى خطأً عند تنفيذ أمر <code>CREATE TRIGGER</code> اعتمادًا على أذونات مستخدم MySQL الخاصة بنا
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3742_17" style=""><span class="pln"> ERROR 1142 (42000): TRIGGER command denied to user 'user'@'host' for table 'collectibles'‎ </span></pre>

<p>
	لحل هذا الخطأ يمكن منح أذونات <code>TRIGGER</code> للمستخدم الخاص بنا من خلال تسجيل الدخول إلى MySQL كمستخدم جذر، وتنفيذ الأوامر التالية مع وضع اسم مستخدم MySQL والمضيف حسب الحاجة:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_64" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> GRANT TRIGGER on </span><span class="pun">*.*</span><span class="pln"> TO </span><span class="str">'user'</span><span class="pun">@</span><span class="str">'localhost'</span><span class="pun">;</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> FLUSH PRIVILEGES</span><span class="pun">;</span></pre>

<p>
	نحدّث أذونات المستخدم، ثم نسجّل الخروج كمستخدم جذر، ونسجّل الدخول مرة أخرى كمستخدم عادي، ونعيد تشغيل التعليمة <code>CREATE TRIGGER</code>، وسيطبع MySQL الرسالة التالية للتأكد من إنشاء القادح بنجاح:
</p>

<pre class="ipsCode" id="ips_uid_4741_66">Query OK, 1 row affected (0.009 sec)
</pre>

<p>
	نحاول الآن إدراج مجموعة هدايا تذكارية جديدة باستخدام وسيط بحروف صغيرة مع استعلام <code>INSERT</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_68" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO collectibles VALUES </span><span class="pun">(</span><span class="str">'aircraft model'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10.00</span><span class="pun">);</span></pre>

<p>
	ثم نتحقّق من الصفوف الناتجة في الجدول <code>collectibles</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_70" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM collectibles</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode" id="ips_uid_4741_72">+-----------------+-------+
| name            | value |
+-----------------+-------+
| spaceship model | 12.50 |
| AIRCRAFT MODEL  | 10.00 |
+-----------------+-------+
2 rows in set (0.000 sec)
</pre>

<p>
	يشير الإدخال الجديد هذه المرة إلى أن <code>AIRCRAFT MODEL</code> -مع جميع حروفه الكبيرة- يختلف عن الإدخال الذي حاولنا إدراجه، حيث شُغِّل القادح في الخلفية وحوّل حالة الحروف قبل حفظ الصف في قاعدة البيانات.
</p>

<p>
	يحمي القادح جميع الصفوف الجديدة لضمان حفظ الأسماء بحروف كبيرة، ولكن لا يزال من الممكن حفظ البيانات غير المقيَّدة التي تستخدم تعليمات <code>UPDATE</code>، حيث يمكن حماية تعليمات <code>UPDATE</code> باستخدام التأثير نفسه، إذًا لننشئ قادحًا آخر كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_74" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TRIGGER uppercase_before_update BEFORE UPDATE
mysql</span><span class="pun">&gt;</span><span class="pln"> ON collectibles
mysql</span><span class="pun">&gt;</span><span class="pln"> FOR EACH ROW
mysql</span><span class="pun">&gt;</span><span class="pln"> SET NEW</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> UPPER</span><span class="pun">(</span><span class="pln">NEW</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span></pre>

<p>
	يكمن الفرق بين القادحين في المعايير، فالقادح هنا <code>BEFORE UPDATE</code>، مما يعني تنفيذه في كل مرة تُنفَّذ فيها تعليمة <code>UPDATE</code> مع الجدول، ويؤثر ذلك على الصفوف الموجودة في كل تحديث، بالإضافة إلى الصفوف الجديدة التي يؤثر عليها القادح السابق. سيعطي MySQL تأكيدًا بإنشاء القادح بنجاح كما يلي:
</p>

<pre class="ipsCode" id="ips_uid_4741_76">Query OK, 0 row affected (0.009 sec)
</pre>

<p>
	يمكن التحقق من سلوك القادح الجديد من خلال تحديث قيمة سعر <code>spaceship model</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_79" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> UPDATE collectibles SET </span><span class="kwd">value</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">15.00</span><span class="pln"> WHERE name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'spaceship model'</span><span class="pun">;</span></pre>

<p>
	ترشّح تعليمة <code>WHERE</code> الصف المراد تحديثه حسب الاسم، وتغيّر تعليمة <code>SET</code> القيمة إلى 15.00، وسيظهر الخرج التالي، مما يؤكد أن التعليمة قد غيّرت صفًا واحدًا:
</p>

<pre class="ipsCode" id="ips_uid_4741_81">Query OK, 1 row affected (0.002 sec)
Rows matched: 1  Changed: 1  Warnings: 0
</pre>

<p>
	لنتحقّق الآن من الصفوف الناتجة في الجدول <code>collectibles</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_83" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM collectibles</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode" id="ips_uid_4741_85">+-----------------+-------+
| name            | value |
+-----------------+-------+
| SPACESHIP MODEL | 15.00 |
| AIRCRAFT MODEL  | 10.00 |
+-----------------+-------+
2 rows in set (0.000 sec)
</pre>

<p>
	أصبح الاسم الآن <code>SPACESHIP MODEL</code> بالإضافة إلى تحديث السعر إلى 15.00 باستخدام التعليمة المُنفَّذة. إذا شغّلنا تعليمة <code>UPDATE</code>، فسيُنفَّذ القادح، مما يؤثر على القيم الموجودة في الصف المُحدَّث، مع تحويل عمود الاسم إلى حروف كبيرة قبل الحفظ.
</p>

<p>
	أنشأنا في هذا القسم قادحين Triggersيعملان قبل استعلامات <code>INSERT</code> وقبل استعلامات <code>UPDATE</code> لجعل البيانات ملائمةً قبل حفظها في قاعدة البيانات، وسنستخدم في القسم التالي محفّزات <code>BEFORE DELETE</code> لنسخ الصفوف المحذوفة في جدول منفصل للأرشفة.
</p>

<h2 id="beforedelete">
	استخدام قوادح BEFORE DELETE
</h2>

<p>
	قد نرغب في أرشفة البيانات بدلاً من حذفها نهائيًا من قاعدة البيانات، خاصةً إذا كنا بحاجة للاحتفاظ بسجل لهذه البيانات في المستقبل، فإذ أنشأنا في بداية هذا المقال جدول آخر باسم <code>collectibles_archive</code> لتعقّب جميع الهدايا التذكارية المحذوفة من المجموعة وأرشفتها. سنستخدم قادح يُنفَّذ قبل تنفيذ تعليمات الحذف <code>DELETE</code>.
</p>

<p>
	نتحقّق أولًا مما إذا كان جدول الأرشيف فارغًا بالكامل من خلال تنفيذ التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_87" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM collectibles_archive</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي، مما يؤكد أن الجدول <code>collectibles_archive</code> فارغ:
</p>

<pre class="ipsCode" id="ips_uid_4741_89">Empty set (0.000 sec)
</pre>

<p>
	إذا نفّذنا استعلام <code>DELETE</code> مع الجدول <code>collectibles</code>، فيمكن حذف أيّ صفٍ من الجدول دون أيّ أثر. يمكن معالجة ذلك من خلال إنشاء قادح يُنفَّذ قبل جميع استعلامات <code>DELETE</code> مع الجدول <code>collectibles</code>، والغرض من هذا القادح هو حفظ نسخة من الكائن المحذوف في جدول الأرشيف قبل حدوث الحذف.
</p>

<p>
	لنشغّل الآن الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_91" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TRIGGER archive_before_delete BEFORE DELETE
mysql</span><span class="pun">&gt;</span><span class="pln"> ON collectibles
mysql</span><span class="pun">&gt;</span><span class="pln"> FOR EACH ROW
mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO collectibles_archive </span><span class="pun">(</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">value</span><span class="pun">)</span><span class="pln"> VALUES </span><span class="pun">(</span><span class="pln">OLD</span><span class="pun">.</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> OLD</span><span class="pun">.</span><span class="kwd">value</span><span class="pun">);</span></pre>

<p>
	يُسمَّى هذا القادح باسم <code>archive_before_delete</code> ويحدث قبل أيّ استعلامات <code>DELETE</code> مع الجدول <code>collectibles</code>. ستُنفَّذ تعليمة <code>INSERT</code> لكل صفٍ سيُحذف، بينما تدرج تعليمة <code>INSERT</code> صفًا جديدًا في الجدول <code>collectibles_archive</code> مع قيم البيانات المأخوذة من السجل <code>OLD</code>، وهو السجل المقرّر حذفه، حيث يصبح <code>OLD.name</code> هو العمود <code>name</code> ويصبح <code>OLD.value</code> العمود <code>value</code>.
</p>

<p>
	وتؤكد قاعدة البيانات إنشاء هذا القادح كما يلي:
</p>

<pre class="ipsCode" id="ips_uid_4741_93">Query OK, 0 row affected (0.009 sec)
</pre>

<p>
	نحاول حذف إحدى الهدايا التذكارية من الجدول <code>collectibles</code> الرئيسي مع استخدام القادح كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_95" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> DELETE FROM collectibles WHERE name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'SPACESHIP MODEL'</span><span class="pun">;</span></pre>

<p>
	ويؤكّد الخرج التالي نجاح تشغيل الاستعلام السابق:
</p>

<pre class="ipsCode" id="ips_uid_4741_97">Query OK, 1 row affected (0.004 sec)
</pre>

<p>
	لنسرد الآن جميع الهدايا التذكارية كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_99" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM collectibles</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode" id="ips_uid_4741_101">+----------------+-------+
| name           | value |
+----------------+-------+
| AIRCRAFT MODEL | 10.00 |
+----------------+-------+
1 row in set (0.000 sec)
</pre>

<p>
	حذفنا <code>SPACESHIP MODEL</code> ولم يَعُد موجودًا في الجدول مع بقاء <code>AIRCRAFT MODEL</code>، ولكن يجب تسجيل هذا الحذف في الجدول <code>collectibles_archive</code> باستخدام القادح الذي أنشأناه مسبقًا، إذًا لنتحقق من ذلك، ولننفّذ استعلامًا آخر كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_103" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM collectibles_archive</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode" id="ips_uid_4741_105">+-----------------+-------+---------------------+
| name            | value | removed_on          |
+-----------------+-------+---------------------+
| SPACESHIP MODEL | 15.00 | 2022-11-20 11:32:01 |
+-----------------+-------+---------------------+
1 row in set (0.000 sec)
</pre>

<p>
	لاحظ القادح عملية الحذف في هذا الجدول تلقائيًا، مع تعبئة أعمدة <code>name</code> و <code>value</code> بالبيانات من الصف المحذوف، ولم يضبط القادح العمود الثالث <code>removed_on</code> صراحةً، لذلك سيأخذ القيمة الافتراضية المُحدَّدة أثناء إنشاء الجدول، أي تاريخ إنشاء أيّ صف جديد، مما يؤدي دائمًا إلى إضافة تعليق توضيحي لكل إدخال مُضاف بمساعدة القادح مع تاريخ الحذف.
</p>

<p>
	يمكننا الآن مع وجود هذا القادح التأكد من أن جميع استعلامات <code>DELETE</code> ستؤدي إلى إدخال سجلٍ في الجدول <code>collectibles_archive</code> مع ترك معلومات حول الهدايا التذكارية المملوكة مسبقًا.
</p>

<p>
	سنستخدم في القسم التالي القوادح التي تُنفَّذ بعد تعليمات تحديث الجدول الذي يحتوي على القيم المُجمَّعة بناءً على جميع الهدايا التذكارية.
</p>

<h2 id="afterinsertafterupdateafterdelete">
	استخدام محفزات AFTER INSERT و AFTER UPDATE و AFTER DELETE
</h2>

<p>
	استخدمنا في القسمين السابقين القوادح المُنفَّذة قبل التعليمات الرئيسية لتنفيذ العمليات المستندة إلى البيانات الأصلية قبل تحديث قاعدة البيانات، وسنحدّث في هذا القسم جدول البيانات التلخيصية بالعدد والقيمة المتراكمة لجميع الهدايا التذكارية المحدَّثة دائمًا باستخدام القوادح المُنفَّذة بعد التعليمات المطلوبة، وبذلك سنكون متأكدين من أن بيانات الجدول تأخذ في الحسبان الحالة الحالية لقاعدة البيانات.
</p>

<p>
	لنبدأ الآن بفحص الجدول <code>collectibles_stats</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_107" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM collectibles_stats</span><span class="pun">;</span></pre>

<p>
	لم نضف معلومات إلى هذا الجدول بعد، لذا يكون عدد عناصر الهدايا التذكارية المملوكة هو 0، والقيمة التراكمية هي <code>NULL</code>:
</p>

<pre class="ipsCode" id="ips_uid_4741_109">+-------+-------+
| count | value |
+-------+-------+
|     0 |  NULL |
+-------+-------+
1 row in set (0.000 sec)
</pre>

<p>
	لا توجد قوادح لهذا الجدول، وبالتالي لم تؤثر الاستعلامات الصادرة مسبقًا لإدراج الهدايا التذكارية وتحديثها على هذا الجدول. نريد ضبط القيم في صفٍ واحد من الجدول <code>collectibles_stats</code> لتقديم معلومات مُحدَّثة حول عدد الهدايا التذكارية وقيمتها الإجمالية، حيث نتأكد من تحديث محتويات الجدول بعد كل عملية إدراج <code>INSERT</code> أو تحديث <code>UPDATE</code> أو حذف <code>DELETE</code> من خلال إنشاء ثلاثة قوادح منفصلة وتنفيذها بعد الاستعلام المقابل لها. لننشئ أولًا القادح <code>AFTER INSERT</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_111" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TRIGGER stats_after_insert AFTER INSERT
mysql</span><span class="pun">&gt;</span><span class="pln"> ON collectibles
mysql</span><span class="pun">&gt;</span><span class="pln"> FOR EACH ROW
mysql</span><span class="pun">&gt;</span><span class="pln"> UPDATE collectibles_stats
mysql</span><span class="pun">&gt;</span><span class="pln"> SET count </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     SELECT COUNT</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> FROM collectibles
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">),</span><span class="pln"> </span><span class="kwd">value</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     SELECT SUM</span><span class="pun">(</span><span class="kwd">value</span><span class="pun">)</span><span class="pln"> FROM collectibles
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	سمّينا القادح بالاسم <code>stats_after_insert</code> وسيُنفَّذ بعد <code>AFTER</code> كل استعلام <code>INSERT</code> في الجدول <code>collectibles</code> مع تشغيل تعليمة <code>UPDATE</code> في جسم القادح. يؤثر استعلام <code>UPDATE</code> على جدول البيانات التلخيصية ويضبط العمودين <code>count</code> و <code>value</code> على القيم التي تعيدها الاستعلامات المتداخلة كما يلي:
</p>

<ul>
	<li>
		<strong><code>SELECT COUNT(name) FROM collectibles</code></strong>: يحصل على عدد الهدايا التذكارية
	</li>
	<li>
		<strong><code>SELECT SUM(value) FROM collectibles</code></strong>: يحصل على القيمة الإجمالية لجميع الهدايا التذكارية
	</li>
</ul>

<p>
	وتؤكّد قاعدة البيانات إنشاء القادح كما يلي:
</p>

<pre class="ipsCode" id="ips_uid_4741_113">Query OK, 0 row affected (0.009 sec)
</pre>

<p>
	نجرّب الآن إعادة إدراج <code>spaceship model</code> المحذوف مسبقًا في الجدول <code>collectibles</code> للتحقق من التحديث الصحيح لجدول البيانات التلخيصية كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_115" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO collectibles VALUES </span><span class="pun">(</span><span class="str">'spaceship model'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">15.00</span><span class="pun">);</span></pre>

<p>
	وتطبع قاعدة البيانات رسالة النجاح التالية:
</p>

<pre class="ipsCode" id="ips_uid_4741_117">Query OK, 1 row affected (0.009 sec)
</pre>

<p>
	يمكن سرد جميع الهدايا التذكارية المملوكة باستخدام الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_119" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM collectibles</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode" id="ips_uid_4741_121">+-----------------+-------+
| name            | value |
+-----------------+-------+
| AIRCRAFT MODEL  | 10.00 |
| SPACESHIP MODEL | 15.00 |
+-----------------+-------+
2 rows in set (0.000 sec)
</pre>

<p>
	يوجد نوعان من عناصر الهدايا التذكارية التي تبلغ قيمتها الإجمالية 25.00. لنفحص الآن جدول البيانات التلخيصية بعد العنصر الذي أدرجناه حديثًا من خلال تنفيذ الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_123" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM collectibles_stats</span><span class="pun">;</span></pre>

<p>
	سيسرد الجدول هذه المرة عدد جميع عناصر الهدايا التذكارية المملوكة التي هي 2 وقيمتها التراكمية 25.00، والتي تطابق الخرج السابق:
</p>

<pre class="ipsCode" id="ips_uid_4741_125">+-------+-------+
| count | value |
+-------+-------+
|     2 | 25.00 |
+-------+-------+
1 row in set (0.000 sec)
</pre>

<p>
	يُنفَّذ القادح <code>stats_after_insert</code> بعد استعلام <code>INSERT</code> ويحدّث الجدول <code>collectibles_stats</code> بالبيانات الحالية <code>count</code> و <code>value</code> للمجموعة، وتُجمَع إحصائيات محتويات المجموعة بأكملها، وليس الإدخال الأخير فقط. تحتوي المجموعة الآن على عنصرين <code>spaceship model</code> و <code>aircraft model</code>، لذا يسرد جدول البيانات التلخيصية عنصرين مع قيمتهما الإجمالية، وبالتالي ستؤدي إضافة أيّ عنصر هدية تذكارية جديد إلى الجدول <code>collectibles</code> إلى تحديث جدول البيانات التلخيصية بالقيم الصحيحة، ولكن لن يؤثر تحديث العناصر الموجودة أو حذف الهدايا التذكارية على جدول البيانات التلخيصية أبدًا، لذا سننشئ قادحين إضافيين لإجراء عمليات متطابقة ولكن تحفّزها أحداث مختلفة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_131" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TRIGGER stats_after_update AFTER UPDATE
mysql</span><span class="pun">&gt;</span><span class="pln"> ON collectibles
mysql</span><span class="pun">&gt;</span><span class="pln"> FOR EACH ROW
mysql</span><span class="pun">&gt;</span><span class="pln"> UPDATE collectibles_stats
mysql</span><span class="pun">&gt;</span><span class="pln"> SET count </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     SELECT COUNT</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> FROM collectibles
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">),</span><span class="pln"> </span><span class="kwd">value</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     SELECT SUM</span><span class="pun">(</span><span class="kwd">value</span><span class="pun">)</span><span class="pln"> FROM collectibles
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TRIGGER stats_after_delete AFTER DELETE
mysql</span><span class="pun">&gt;</span><span class="pln"> ON collectibles
mysql</span><span class="pun">&gt;</span><span class="pln"> FOR EACH ROW
mysql</span><span class="pun">&gt;</span><span class="pln"> UPDATE collectibles_stats
mysql</span><span class="pun">&gt;</span><span class="pln"> SET count </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     SELECT COUNT</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> FROM collectibles
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">),</span><span class="pln"> </span><span class="kwd">value</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     SELECT SUM</span><span class="pun">(</span><span class="kwd">value</span><span class="pun">)</span><span class="pln"> FROM collectibles
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	أنشأنا قادحين جديدين هما: <code>stats_after_update</code> و <code>stats_after_delete</code>، وسيُنفّذان مع الجدول <code>collectible_stats</code> عند تنفيذ تعليمة <code>UPDATE</code> أو <code>DELETE</code> في الجدول <code>collectibles</code>. يؤدي الإنشاء الناجح لهذين القادحين إلى طباعة الخرج التالي:
</p>

<pre class="ipsCode" id="ips_uid_4741_133">Query OK, 0 row affected (0.009 sec)
</pre>

<p>
	لنحدّث الآن قيمة السعر لإحدى الهدايا التذكارية كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_135" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> UPDATE collectibles SET </span><span class="kwd">value</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">25.00</span><span class="pln"> WHERE name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'AIRCRAFT MODEL'</span><span class="pun">;</span></pre>

<p>
	ترشّح تعليمة <code>WHERE</code> الصف المراد تحديثه حسب الاسم، وتغيّر تعليمة <code>SET</code> القيمة إلى 25.00.
</p>

<p>
	يؤكّد الخرج التالي أن التعليمة غيّرت صفًا واحدًا:
</p>

<pre class="ipsCode" id="ips_uid_4741_137">Query OK, 1 row affected (0.002 sec)
Rows matched: 1  Changed: 1  Warnings: 0
</pre>

<p>
	لنتحقق مرة أخرى من محتويات جدول البيانات التلخيصية بعد التحديث كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_139" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM collectibles_stats</span><span class="pun">;</span></pre>

<p>
	وسيسرد العمود <code>value</code> الآن القيمة 40.00، وهي القيمة الصحيحة بعد التحديث:
</p>

<pre class="ipsCode" id="ips_uid_4741_141">+-------+-------+
| count | value |
+-------+-------+
|     2 | 40.00 |
+-------+-------+
1 row in set (0.000 sec)
</pre>

<p>
	الخطوة الأخيرة هي التحقق من أن جدول البيانات التلخيصية سيظهِر حذف إحدى الهدايا التذكارية بطريقة صحيحة، فلنحاول حذف العنصر <code>aircraft model</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_143" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> DELETE FROM collectibles WHERE name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'AIRCRAFT MODEL'</span><span class="pun">;</span></pre>

<p>
	ويؤكد الخرج التالي تشغيل الاستعلام بنجاح:
</p>

<pre class="ipsCode" id="ips_uid_4741_145">Query OK, 1 row affected (0.004 sec)
</pre>

<p>
	لنسرد الآن جميع الهدايا التذكارية كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_147" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM collectibles</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode" id="ips_uid_4741_149">+-----------------+-------+
| name            | value |
+-----------------+-------+
| SPACESHIP MODEL | 15.00 |
+-----------------+-------+
1 row in set (0.000 sec)
</pre>

<p>
	لاحظ بقاء العنصر <code>SPACESHIP MODEL</code> فقط. لنتحقق الآن من القيم الموجودة في جدول البيانات التلخيصية كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_151" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM collectibles_stats</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode" id="ips_uid_4741_153">+-------+-------+
| count | value |
+-------+-------+
|     1 | 15.00 |
+-------+-------+
1 row in set (0.000 sec)
</pre>

<p>
	يعرض العمود <code>count</code> الآن هدية تذكارية واحدة فقط في الجدول الرئيسي، والقيمة الإجمالية هي 15.00، وهي مطابقة لقيمة العنصر <code>SPACESHIP MODEL</code>.
</p>

<p>
	تعمل المحفّزات الثلاثة السابقة مع بعضها البعض بعد استعلامات <code>INSERT</code> و <code>UPDATE</code> و <code>DELETE</code> للحفاظ على مزامنة جدول الملخص مع القائمة الكاملة للهدايا التذكارية.
</p>

<p>
	سنتعلم في القسم التالي كيفية معالجة القوادح الموجودة مسبقًا مع قاعدة البيانات.
</p>

<h2 id="triggers-1">
	سرد وحذف القوادح Triggers
</h2>

<p>
	أنشأنا في الأقسام السابقة قوادح جديدة، ولكن يمكنك أيضًا سردها ومعالجتها عند الحاجة لأنها كائنات مسمَّاة ومُعرَّفة في قاعدة البيانات مثل الجداول، حيث يمكنك سرد جميع القوادح من خلال تنفيذ التعليمة <code>SHOW TRIGGERS</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_155" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SHOW TRIGGERS</span><span class="pun">;</span></pre>

<p>
	وسيتضمن الخرج جميع القوادح مع أسمائها وحدث التحفيز مع الوقت قبل <code>BEFORE</code> أو بعد <code>AFTER</code> تنفيذ التعليمة، بالإضافة إلى التعليمات التي تشكّل جزءًا من جسم القادح والتفاصيل الشاملة الأخرى لتعريفه كما يلي:
</p>

<pre class="ipsCode" id="ips_uid_4741_157">+-------------------------+--------+--------------+--------(...)+--------+(...)
| Trigger                 | Event  | Table        | Statement   | Timing |(...)
+-------------------------+--------+--------------+--------(...)+--------+(...)
| uppercase_before_insert | INSERT | collectibles | SET    (...)| BEFORE |(...)
| stats_after_insert      | INSERT | collectibles | UPDATE (...)| AFTER  |(...)
| uppercase_before_update | UPDATE | collectibles | SET    (...)| BEFORE |(...)
| stats_after_update      | UPDATE | collectibles | UPDATE (...)| AFTER  |(...)
| archive_before_delete   | DELETE | collectibles | INSERT (...)| BEFORE |(...)
| stats_after_delete      | DELETE | collectibles | UPDATE (...)| AFTER  |(...)
+-------------------------+--------+--------------+--------(...)+--------+(...)
6 rows in set (0.001 sec)
</pre>

<p>
	يمكن حذف القوادح الموجودة مسبقًا من خلال استخدام تعليمات SQL التي هي <code>DROP TRIGGER</code>، فمثلًا إن لم نعد نرغب بفرض الحروف الكبيرة على أسماء الهدايا التذكارية، ولم نعد بحاجة إلى القادحين <code>uppercase_before_insert</code> و <code>uppercase_before_update</code>، فيمكننا إزالتهما من خلال تنفيذ الأمرين التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_159" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> DROP TRIGGER uppercase_before_insert</span><span class="pun">;</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> DROP TRIGGER uppercase_before_update</span><span class="pun">;</span></pre>

<p>
	ويستجيب MySQL برسالة النجاح التالية:
</p>

<pre class="ipsCode" id="ips_uid_4741_161">Query OK, 0 rows affected (0.004 sec)
</pre>

<p>
	لنجرّب الآن إضافة هدية تذكارية جديدة بحروف صغيرة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_163" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO collectibles VALUES </span><span class="pun">(</span><span class="str">'ship model'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10.00</span><span class="pun">);</span></pre>

<p>
	وستؤكد قاعدة البيانات ذلك كما يلي:
</p>

<pre class="ipsCode" id="ips_uid_4741_165">Query OK, 1 row affected (0.009 sec)
</pre>

<p>
	يمكننا التحقق من إدراج الصف من خلال تنفيذ استعلام <code>SELECT</code> التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4741_167" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM collectibles</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode" id="ips_uid_4741_169">+-----------------+-------+
| name            | value |
+-----------------+-------+
| SPACESHIP MODEL | 15.00 |
| ship model      | 10.00 |
+-----------------+-------+
2 rows in set (0.000 sec)
</pre>

<p>
	نلاحظ كتابة الهدية التذكارية المضافة حديثًا بحروف صغيرة، إذًا لم يتغيّر الاسم عن الخرج الأصلي، وبالتالي تأكّدنا من أن القادح الذي حوّل حالة الحروف سابقًا لم يَعُد قيد الاستخدام.
</p>

<h2 id="-1">
	الخلاصة
</h2>

<p>
	تعلمنا في هذا المقال ما هي القوادح Triggers في SQL وكيفية استخدامها في MySQL لمعالجة البيانات قبل استعلامات <code>INSERT</code> و <code>UPDATE</code>، وتعلّمنا كيفية استخدام قادح <code>BEFORE DELETE</code> لأرشفة الصف المحذوف في جدول منفصل، بالإضافة إلى استخدام قوادح <code>AFTER</code> بعد التعليمات لإبقاء جدول البيانات التلخيصية مُحدَّثة باستمرار.
</p>

<p>
	يمكننا استخدام الدوال لتفريغ بعض عمليات معالجة البيانات والتحقق من صحتها في محرّك قاعدة البيانات، مما يضمن سلامة البيانات أو إخفاء بعض سلوكيات قاعدة البيانات عن المستخدم الذي يستخدم قاعدة البيانات يوميًا، وقد غطّى هذا المقال أساسيات استخدام القوادح لهذا الغرض فقط، ولكن يمكنك أيضًا إنشاء قوادح معقدة تتكوّن من تعليمات متعددة واستخدام المنطق الشرطي لتنفيذ الإجراءات بدقة أكبر. ويمكن مطالعة <a href="https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html" rel="external nofollow">توثيق MySQL الخاص بالتعامل مع triggers</a> لمزيد من المعلومات.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-triggers-in-mysql" rel="external nofollow">How To Use Triggers in MySQL</a> لصاحبَيه Mateusz Papiernik و Rachel Lee.
</p>

<h2 id="-2">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/sql/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-sql-r2515/" rel="">استخدام الدوال Functions في لغة SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r602/" rel="">التعامل مع قواعد البيانات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/" rel="">مدخل إلى تصميم قواعد البيانات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-sql-r2515/" rel="">استخدام الدوال Functions في لغة SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%B9%D8%B1%D9%88%D8%B6-views-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2415/" rel="">استخدام العروض Views في لغة الاستعلام البنيوية SQL</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2520</guid><pubDate>Mon, 24 Feb 2025 15:00:00 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x62F;&#x648;&#x627;&#x644; Functions &#x641;&#x64A; &#x644;&#x63A;&#x629; SQL</title><link>https://academy.hsoub.com/programming/sql/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-sql-r2515/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_02/SQL.png.4050ac50f069c61d9b6771525575c2ae.png" /></p>
<p>
	لا يقتصر التعامل في أنظمة إدارة قواعد البيانات العلاقية Relational Database Management System ولغة الاستعلام البنيوية SQL على تخزين البيانات وإدارتها واسترجاعها من قاعدة البيانات. إذ يمكن للغة SQL أيضًا إجراء عمليات حسابية ومعالجة البيانات عن طريق الدوال Functions، حيث يمكننا على سبيل  المثال استخدام دوال محددة لاسترجاع أسعار المنتجات مع تقريب أسعارها، أو حساب متوسط عدد عمليات شراء منتج ما، أو تحديد عدد أيام انتهاء صلاحية الضمان على عملية شراء معينة.
</p>

<p>
	سنستخدم في هذا المقال دوالًا مختلفة في لغة SQL من أجل إجراء عمليات حسابية رياضية، ومعالجة السلاسل النصية والتواريخ، وحساب إحصائيات باستخدام الدوال التجميعية Aggregate Functions.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	يجب أن يكون لديك حاسوب يشغّل نظام إدارة قواعد بيانات علاقية RDBMS مستند إلى لغة SQL. وقد اختبرنا التعليمات والأمثلة الواردة في هذا المقال باستخدام البيئة التالية:
</p>

<ul>
	<li>
		خادم عامل على توزيعة أوبنتو Ubuntu مع مستخدم ذو صلاحيات مسؤول مختلف عن المستخدم الجذر وجدار حماية مضبوط باستخدام أداة UFW كما هو موضّح في <a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04" rel="external nofollow">دليل الإعداد الأولي للخادم مع الإصدار 20.04 من أوبنتو</a>
	</li>
	<li>
		نظام MySQL مُثبَّت ومؤمَّن على الخادم كما هو موضح في مقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبنتو</a>، وقد نفذنا خطوات هذا المقال من خلال مستخدم مختلف عن المستخدم الجذر وفق الطريقة الموضحة في الخطوة التالية من المقال
	</li>
	<li>
		معرفة أساسية بتنفيذ استعلامات <code>SELECT</code> لاختيار البيانات من قاعدة البيانات كما هو موضّح في مقال <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%B9%D9%86-%D8%A7%D9%84%D8%B3%D8%AC%D9%84%D8%A7%D8%AA-%D9%85%D9%86-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r2249/" rel="">كيفية الاستعلام عن السجلات من الجداول في SQL</a>
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: تجدر الإشارة إلى أنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية RDBMS لها تقديماتها الفريدة من لغة SQL، إذ ستعمل الأوامر المُقدمة في هذا المقال بنجاح مع معظم هذه الأنظمة، ولكن تحدّد صيغة SQL المعيارية عددًا محدودًا من الدوال، ويختلف دعم الصيغة المعيارية بين محرّكات قواعد البيانات المختلفة، لذا قد تجد بعض الاختلافات في الصيغة أو الناتج عند اختبارها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<p>
	سنحتاج أيضًا إلى قاعدة بيانات تحتوي على بعض الجداول المُحمَّلة ببيانات تجريبية نموذجية لنتمكّن من التدرب على استخدام الدوال، ويمكن مطالعة القسم التالي للحصول على تفاصيل حول الاتصال بخادم MySQL وإنشاء قاعدة البيانات التجريبية التي سنستخدمها في أمثلتنا.
</p>

<h2 id="mysql">
	الاتصال بخادم MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	سنتصل في هذا القسم بخادم MySQL وننشئ قاعدة بيانات تجريبية لنتمكّن من اتباع الأمثلة الواردة في هذا المقال.
</p>

<p>
	إذا كان نظام قاعدة بيانات SQL الخاص بنا يعمل على خادم بعيد، نتصل بالخادم باستخدام بروتوكول <a href="https://academy.hsoub.com/devops/security/ssh/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr></a> من جهازنا المحلي كما يلي:
</p>

<pre class="ipsCode">$ ssh user@your_server_ip
</pre>

<p>
	ثم نفتح واجهة سطر أوامر خادم MySQL مع وضع اسم حساب مستخدم MySQL الخاص بنا مكان <code>user</code>:
</p>

<pre class="ipsCode">$ mysql -u user -p
</pre>

<p>
	ننشِئ قاعدة بيانات بالاسم <code>bookstore</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7015_7" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE DATABASE bookstore</span><span class="pun">;</span></pre>

<p>
	إذا أُنشئِت قاعدة البيانات بنجاح، فسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	يمكن اختيار قاعدة البيانات <code>bookstore</code> من خلال تنفيذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7015_9" style=""><span class="pln">$ USE bookstore</span><span class="pun">;</span></pre>

<p>
	وسيَظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	اخترنا قاعدة البيانات، وسننشئ عدة جداول تجريبية ضمنها، حيث سنستخدم مكتبة افتراضية تبيع كتبًا لعدة مؤلفين. سيحتوي الجدول <code>inventory</code> على بيانات كتب ممثّلة باستخدام الأعمدة التالية:
</p>

<ul>
	<li>
		<strong><code>book_id</code></strong>: المعرّف الخاص بكل كتاب، ونمثّله بنوع البيانات <code>int</code>، وسيكون هذا العمود هو المفتاح الرئيسي Primary Key للجدول، حيث تصبح كل قيمة معرّفًا فريدًا للصف الخاص بها
	</li>
	<li>
		<strong><code>author</code></strong>: اسم مؤلف الكتاب، ونمثّله باستخدام نوع البيانات <code>varchar</code> بحد أقصى 50 محرفًا
	</li>
	<li>
		<strong><code>title</code></strong>: عنوان الكتاب الذي جرى شراؤه، ونمثّله باستخدام نوع بيانات <code>varchar</code> بحد أقصى 200 محرف
	</li>
	<li>
		<strong><code>introduction_date</code></strong>: تاريخ تقديم المكتبة لكل كتاب، ونمثّله باستخدام نوع البيانات <code>date</code>
	</li>
	<li>
		<strong><code>stock</code></strong>: يحتوي هذا العمود على عدد الكتب الموجودة في مخزن المكتبة، ونمثّله باستخدام نوع البيانات <code>int</code>
	</li>
	<li>
		<strong><code>price</code></strong>: سعر الكتاب، ونمثّله باستخدام نوع البيانات <code>decimal</code> بحد أقصى 5 قيم قبل الفاصلة العشرية وقيمتين بعدها
	</li>
</ul>

<p>
	ننشئ هذا الجدول التجريبي باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7015_11" style=""><span class="pln">CREATE TABLE inventory </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     book_id </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     author varchar</span><span class="pun">(</span><span class="lit">50</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     title varchar</span><span class="pun">(</span><span class="lit">200</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     introduction_date date</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     stock </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     price </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     PRIMARY KEY </span><span class="pun">(</span><span class="pln">book_id</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	إذا كان الخرج كما يلي، فهذا يعني إنشاء الجدول الأول بنجاح:
</p>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.00 sec)
</pre>

<p>
	نحمّل جدول المشتريات ببعض البيانات التجريبية من خلال تشغيل عملية <code>INSERT INTO</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7015_13" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO inventory
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Oscar Wilde'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'The Picture of Dorian Gray'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-01'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20.83</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Jane Austen'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Pride and Prejudice'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-04'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">42.13</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Herbert George Wells'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'The Time Machine'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-09-23'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">21.99</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Mary Shelley'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Frankenstein'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-07-23'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="lit">17.43</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Mark Twain'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'The Adventures of Huckleberry Finn'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-01'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">14</span><span class="pun">,</span><span class="pln"> </span><span class="lit">23.15</span><span class="pun">);</span></pre>

<p>
	ستضيف عملية <code>INSERT INTO</code> ‏خمسة كتب إلى الجدول <code>inventory</code>، حيث يشير الخرج التالي إلى إضافة جميع الصفوف الخمسة بنجاح:
</p>

<pre class="ipsCode">الخرج
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0
</pre>

<p>
	نحن الآن جاهزون لمتابعة هذا المقال والبدء في استخدام الدوال في لغة SQL.
</p>

<h2 id="sql">
	فهم الدوال في لغة SQL
</h2>

<p>
	<a href="https://wiki.hsoub.com/SQL/functions" rel="external">الدوال functions</a> هي عدة تعابير مجمعة ولها اسم محدد، قد تأخذ قيمة واحدة أوعدة قيم تمرر كوسطاء بين قوسين بعد اسم الدالة، وتجري عمليات حسابية أو تحويلات عليها، ثم تعيد قيمة جديدة كنتيجة للدالة، حيث تشبه دوال SQL الدوال الرياضية، فمثلًا تأخذ الدالة <code>log(x)‎</code> قيمة <code>x</code> وتعيد قيمة لوغاريتم هذه القيمة <code>x</code>.
</p>

<p>
	يمكننا استرجاع المعلومات من قاعدة بيانات علاقية دون تحويلها من خلال استخدام <a href="https://academy.hsoub.com/programming/sql/%D8%AC%D9%84%D8%A8-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85%D8%A7%D8%AA-%D8%B9%D8%A8%D8%B1-select-%D9%81%D9%8A-sql-r845/" rel="">استعلام SELECT</a>، حيث نطلب من قاعدة البيانات إعادة قيم للأعمدة الفردية التي نريدها من خلال تحديد أسماء الأعمدة في الاستعلام، فمثلًا إذا أردنا استرجاع جميع عناوين الكتب مع أسعارها وترتيبها من الأكثر إلى الأقل تكلفة، فيمكن تنفيذ التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7015_17" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT title</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> introduction_date FROM inventory ORDER BY price DESC</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
+------------------------------------+-------+-------------------+
| title                              | price | introduction_date |
+------------------------------------+-------+-------------------+
| Pride and Prejudice                | 42.13 | 2022-10-04        |
| The Adventures of Huckleberry Finn | 23.15 | 2022-10-01        |
| The Time Machine                   | 21.99 | 2022-09-23        |
| The Picture of Dorian Gray         | 20.83 | 2022-10-01        |
| Frankenstein                       | 17.43 | 2022-07-23        |
+------------------------------------+-------+-------------------+
5 rows in set (0.000 sec)
</pre>

<p>
	تكون <code>title</code> و <code>price</code> و <code>introduction_date</code> في التعليمة السابقة أسماء الأعمدة، وتعطي قاعدة البيانات في الخرج الناتج قيمًا غير مُعدَّلة مُسترجَعة من تلك الأعمدة لكل كتاب وهي عنوان الكتاب، والسعر ، وتاريخ إدخال الكتاب إلى المكتبة.
</p>

<p>
	قد نرغب في استرجاع القيم من قاعدة البيانات بعد إجراء بعض المعالجة، فقد نكون مهتمين بأسعار الكتب مع تقريبها إلى أقرب سعر بالدولار، أو كتابة عناوين الكتب بحروف كبيرة، أو عرض سنة تقديم الكتاب دون تضمين الشهر أو اليوم، لذا يجب أن نستخدم دوال مناسبة لذلك.
</p>

<h2 id="sql-1">
	أشهر الدوال في لغة SQL
</h2>

<p>
	يمكن تصنيف <a href="https://wiki.hsoub.com/SQL/functions" rel="external">دوال SQL</a> إلى عدة مجموعات اعتمادًا على نوع البيانات التي نعمل عليها، ونوضح فيما يلي الدوال الأكثر استخدامًا في SQL.
</p>

<h3 id="-1">
	الدوال الرياضية
</h3>

<p>
	هي الدوال التي تعمل على معالجة القيم العددية وتجري العمليات الحسابية عليها، مثل التقريب أو اللوغاريتمات أو الجذور التربيعية أو الرفع لأس
</p>

<h3 id="-2">
	دوال معالجة السلاسل النصية
</h3>

<p>
	هي دوال تعمل على السلاسل والحقول النصية، وتجري تحويلات نصية مثل تحويل أحرف النص إلى حروف كبيرة، أو إزالة الفراغات من بدايات ونهايت السلاسل النصية Trimming، أو استبدال الكلمات في النص بكلمات أخرى.
</p>

<h3 id="-3">
	دوال التاريخ والوقت
</h3>

<p>
	هي دوال تتعامل مع الحقول التي تحتوي على تواريخ، وتُجري هذه الدوال عمليات حسابية وتحويلات مثل إضافة عدد من الأيام إلى التاريخ المُحدَّد، أو أخذ سنة واحدة فقط من التاريخ الكامل.
</p>

<h3 id="aggregatefunctions">
	الدوال التجميعية Aggregate Functions
</h3>

<p>
	هي حالة خاصة من الدوال الرياضية التي تعمل على قيم قادمة من صفوف متعددة مثل حساب متوسط السعر لجميع الصفوف.
</p>

<h2>
	مثال على استدعاء دالة
</h2>

<p>
	يوضّح المثال التالي الصيغة العامة لاستخدام دالة وهمية بالاسم <code>EXAMPLE</code> لتغيير نتائج قيم السعر <code>price</code> في قاعدة بيانات المكتبة باستخدام استعلام <code>SELECT</code> التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7015_21" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT EXAMPLE</span><span class="pun">(</span><span class="pln">price</span><span class="pun">)</span><span class="pln"> AS new_price FROM inventory</span><span class="pun">;</span></pre>

<p>
	تأخذ الدالة <code>EXAMPLE</code> اسم العمود <code>price</code> كوسيط ، حيث يخبر هذا الجزء من الاستعلام قاعدة البيانات بتنفيذ الدالة <code>EXAMPLE</code> على قيم العمود <code>price</code> وإعادة نتائج هذه العملية، ويخبر جزء الاستعلام <code>AS new_price</code> قاعدة البيانات بإسناد اسم مؤقت <code>new_price</code> للقيم المحسوبة خلال فترة تنفيذ الاستعلام، وبذلك يمكننا تمييز نتائج الدالة في الخرج والإشارة إلى القيم الناتجة باستخدام تعلميتي <code>WHERE</code> و <code>ORDER BY</code>.
</p>

<p>
	سنستخدم في القسم التالي الدوال الرياضية لإجراء العمليات الحسابية شائعة الاستخدام.
</p>

<h2 id="-4">
	استخدام الدوال الرياضية
</h2>

<p>
	تعمل الدوال الرياضية على القيم العددية مثل سعر الكتاب أو عدد الكتب الموجودة في المخزن ضمن قاعدة البيانات التجريبية، ويمكن استخدامها لإجراء العمليات الحسابية في قاعدة البيانات لمطابقة النتائج مع متطلباتنا.
</p>

<p>
	يُعَد التقريب من التطبيقات الأكثر استخدامًا للدوال الرياضية في لغة SQL، فلنفترض مثلًا أننا نريد استرجاع أسعار جميع الكتب، ولكن نريدها أن تكون مُقرَّبة لأقرب عدد صحيح، لذا نستخدم الدالة <code>ROUND</code> التي تطبّق عملية التقريب، ونجرّب تنفيذ التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7015_23" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT title</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> ROUND</span><span class="pun">(</span><span class="pln">price</span><span class="pun">)</span><span class="pln"> AS rounded_price FROM inventory</span><span class="pun">;</span></pre>

<p>
	سيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
+------------------------------------+-------+---------------+
| title                              | price | rounded_price |
+------------------------------------+-------+---------------+
| The Picture of Dorian Gray         | 20.83 |            21 |
| Pride and Prejudice                | 42.13 |            42 |
| The Time Machine                   | 21.99 |            22 |
| Frankenstein                       | 17.43 |            17 |
| The Adventures of Huckleberry Finn | 23.15 |            23 |
+------------------------------------+-------+---------------+
5 rows in set (0.000 sec)
</pre>

<p>
	يختار الاستعلام السابق قيم الأعمدة <code>title</code> و <code>price</code> دون تعديل، بالإضافة إلى العمود <code>rounded_price</code> المؤقت الذي يحتوي على نتائج الدالة <code>ROUND(price)‎</code>. تأخذ هذه الدالة وسيطًا واحدًا هو اسم العمود <code>price</code> في مثالنا، وتعيد القيم من هذا العمود في الجدول مع تقريبها إلى أقرب قيمة عددية صحيحة.
</p>

<p>
	يمكن لدالة التقريب أيضًا قبول وسيط إضافي يحدّد عدد المنازل العشرية التي يجب تطبيق التقريب عليها، ويمكن إضافة عمليات حسابية بدلًا من الاكتفاء باسم عمود واحد. لنجرّب مثلًا تشغيل الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7015_25" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT title</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> ROUND</span><span class="pun">(</span><span class="pln">price </span><span class="pun">*</span><span class="pln"> stock</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> AS stock_price FROM inventory</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
+------------------------------------+-------+-------+-------------+
| title                              | stock | price | stock_price |
+------------------------------------+-------+-------+-------------+
| The Picture of Dorian Gray         |     4 | 20.83 |        83.3 |
| Pride and Prejudice                |    12 | 42.13 |       505.6 |
| The Time Machine                   |     7 | 21.99 |       153.9 |
| Frankenstein                       |     9 | 17.43 |       156.9 |
| The Adventures of Huckleberry Finn |    14 | 23.15 |       324.1 |
+------------------------------------+-------+-------+-------------+
5 rows in set (0.000 sec)
</pre>

<p>
	يؤدي تنفيذ الدالة <code>ROUND(price * stock, 1)‎</code> أولًا إلى ضرب سعر الكتاب الواحد بعدد الكتب الموجودة في المخزن، ثم تقريب السعر الناتج إلى أول منزلة عشرية، وستُعرَض النتيجة في العمود المؤقت <code>stock_price</code>.
</p>

<p>
	تتضمّن الدوال الرياضية الأخرى المُضمَّنة في MySQL الدوال المثلثية والجذور التربيعية والرفع لقوة واللوغاريتمات والدوال الأسية. ويمكن معرفة المزيد حول استخدام الدوال الرياضية في لغة SQL في مقال <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%AC%D9%85%D9%8A%D8%B9%D9%8A%D8%A9-%D9%81%D9%8A-sql-r2410/" rel="">كيفية استخدام التعابير الرياضية والدوال التجميعية في لغة SQL</a>.
</p>

<h2 id="-5">
	استخدام دوال معالجة السلاسل النصية
</h2>

<p>
	تمكّننا دوال معالجة السلاسل النصية في لغة SQL من تعديل القيم المُخزَّنة في الأعمدة التي تحتوي على نصوص عند معالجة استعلام SQL، ويمكن استخدامها لتحويل حالة الحروف، أو دمج بيانات من أعمدة متعددة، أو إجراء عمليات البحث والاستبدال.
</p>

<p>
	سنستخدم مثلًا دوال السلاسل النصية لاسترجاع جميع عناوين الكتب المُحوَّلة إلى حروف صغيرة، وننفّذ التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7015_27" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT LOWER</span><span class="pun">(</span><span class="pln">title</span><span class="pun">)</span><span class="pln"> AS title_lowercase FROM inventory</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
+------------------------------------+
| title_lowercase                    |
+------------------------------------+
| the picture of dorian gray         |
| pride and prejudice                |
| the time machine                   |
| frankenstein                       |
| the adventures of huckleberry finn |
+------------------------------------+
5 rows in set (0.001 sec)
</pre>

<p>
	تأخذ دالة SQL التي هي <code>LOWER</code> وسيطًا واحدًا وتحوّل محتوياته إلى حروف صغيرة، وستُعرَض البيانات الناتجة في العمود المؤقت <code>title_lowercase</code> باستخدام تعليمة الاسم البديل للعمود <code>AS title_lowercase</code>.
</p>

<p>
	لنسترجع الآن جميع أسماء المؤلفين مع تحويلها إلى حروف كبيرة، لذا نجرّب تشغيل استعلام SQL التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7015_30" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT UPPER</span><span class="pun">(</span><span class="pln">author</span><span class="pun">)</span><span class="pln"> AS author_uppercase FROM inventory</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
+----------------------+
| author_uppercase     |
+----------------------+
| OSCAR WILDE          |
| JANE AUSTEN          |
| HERBERT GEORGE WELLS |
| MARY SHELLEY         |
| MARK TWAIN           |
+----------------------+
5 rows in set (0.000 sec)
</pre>

<p>
	استخدمنا الدالة <code>UPPER</code> بدلًا من الدالة <code>LOWER</code>، حيث تعمل الدالة <code>UPPER</code> بطريقة مشابهة ولكنها تحوّل النص إلى حروف كبيرة، ويمكن استخدام كلتا الدالتين إذا أردنا ضمان تناسق حالة الحروف عند استرجاع البيانات.
</p>

<p>
	تأخذ الدالة <code>CONCAT</code> وسطاء متعددة تحتوي على قيم نصية وتضعها مع بعضها بعضًا، لذا سنجرب استرجاع مؤلفي الكتب وعناوين الكتب ودمجها في عمود واحد من خلال تنفيذ التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7015_32" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT CONCAT</span><span class="pun">(</span><span class="pln">author</span><span class="pun">,</span><span class="pln"> </span><span class="str">': '</span><span class="pun">,</span><span class="pln"> title</span><span class="pun">)</span><span class="pln"> AS full_title FROM inventory</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
+------------------------------------------------+
| full_title                                     |
+------------------------------------------------+
| Oscar Wilde: The Picture of Dorian Gray        |
| Jane Austen: Pride and Prejudice               |
| Herbert George Wells: The Time Machine         |
| Mary Shelley: Frankenstein                     |
| Mark Twain: The Adventures of Huckleberry Finn |
+------------------------------------------------+
5 rows in set (0.001 sec)
</pre>

<p>
	دمجت الدالة <code>CONCAT</code> السلاسل النصية المتعددة مع بعضها البعض، وقد نفّذناها باستخدام ثلاثة وسطاء: الوسيط الأول <code>author</code> يشير إلى العمود <code>author</code> الذي يحتوي على أسماء المؤلفين، والوسيط الثاني قيمة سلسلة نصية عشوائية للفصل بين المؤلفين وعناوين الكتب بنقطتين، والوسيط الأخير <code>title</code> يشير إلى العمود الذي يحتوي عناوين الكتب. تكون نتيجة هذا الاستعلام هي المؤلفين والعناوين في عمود مؤقت واحد بالاسم <code>full_title</code>.
</p>

<p>
	تتضمّن دوال السلاسل النصية الأخرى في MySQL دوال البحث عن السلاسل النصية واستبدالها، واسترجاع السلاسل النصية الفرعية، وإضافة حاشية Padding إلى السلاسل النصية وإزالة الفراغات Trimming من بداية ونهاية السلاسل النصية، وتطبيق التعابير النمطية Regular Expressions وغيرها من الدوال. ويمكنكم مطالعة المزيد حول استخدام دوال SQL لضم قيم متعددة في مقال <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AF%D9%88%D8%A7%D9%84-cast-%D9%88%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%B6%D9%85-%D9%81%D9%8A-sql-r2418/" rel="">كيفية معالجة البيانات باستخدام دوال CAST وتعابير الضم في SQL</a>، ويمكنك أيضًا الرجوع إلى دليل <a href="https://dev.mysql.com/doc/refman/8.0/en/string-functions.html" rel="external nofollow">دوال ومعاملات السلاسل النصية</a> في توثيق MySQL.
</p>

<h2 id="-6">
	استخدام دوال التاريخ والوقت
</h2>

<p>
	تمكّننا دوال التاريخ والوقت في لغة SQL من معالجة القيم المُخزَّنة في الأعمدة التي تحتوي على التواريخ والعلامات الزمنية عند معالجة استعلام SQL، ويمكن استخدامها لاستخراج أجزاء من معلومات التاريخ، أو إجراء عمليات حسابية على التواريخ، أو تنسيقها بطريقة محددة.
</p>

<p>
	لنفترض أننا نحتاج لتقسيم أو فصل تاريخ تقديم الكتاب إلى السنة والشهر واليوم بدلًا من وجود عمود تاريخ واحد في الخرج. لنجرّب تنفيذ التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7015_34" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT introduction_date</span><span class="pun">,</span><span class="pln">
YEAR</span><span class="pun">(</span><span class="pln">introduction_date</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> year</span><span class="pun">,</span><span class="pln">
MONTH</span><span class="pun">(</span><span class="pln">introduction_date</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> month</span><span class="pun">,</span><span class="pln">
DAY</span><span class="pun">(</span><span class="pln">introduction_date</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> day
FROM inventory</span><span class="pun">;</span></pre>

<p>
	سيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
+-------------------+------+-------+------+
| introduction_date | year | month | day  |
+-------------------+------+-------+------+
| 2022-10-01        | 2022 |    10 |    1 |
| 2022-10-04        | 2022 |    10 |    4 |
| 2022-09-23        | 2022 |     9 |   23 |
| 2022-07-23        | 2022 |     7 |   23 |
| 2022-10-01        | 2022 |    10 |    1 |
+-------------------+------+-------+------+
5 rows in set (0.000 sec)
</pre>

<p>
	استخدمنا في تعليمة SQL السابقة ثلاث دوال فردية هي: <code>YEAR</code> و <code>MONTH</code> و <code>DAY</code>، وتأخذ كل دالة اسم العمود بحيث تُخزَّن التواريخ كوسطاء، وتستخرج جزءًا واحدًا فقط من التاريخ الكامل: السنة أو الشهر أو اليوم على التوالي، وبالتالي يمكننا الوصول إلى أجزاء التاريخ الفردية ضمن استعلامات SQL باستخدام هذه الدوال.
</p>

<p>
	تسمح الدالة <code>DATEDIFF</code> باسترجاع عدد الأيام بين تاريخين، فلنحاول التحقق من عدد الأيام المنقضية بين تاريخ تقديم كل كتاب والتاريخ الحالي، لذا نشغّل الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7015_36" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT introduction_date</span><span class="pun">,</span><span class="pln">
DATEDIFF</span><span class="pun">(</span><span class="pln">introduction_date</span><span class="pun">,</span><span class="pln"> CURRENT_DATE</span><span class="pun">())</span><span class="pln"> AS days_since FROM inventory</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
+-------------------+------------+
| introduction_date | days_since |
+-------------------+------------+
| 2022-10-01        |        -30 |
| 2022-10-04        |        -27 |
| 2022-09-23        |        -38 |
| 2022-07-23        |       -100 |
| 2022-10-01        |        -30 |
+-------------------+------------+
5 rows in set (0.000 sec)
</pre>

<p>
	تأخذ الدالة <code>DATEDIFF</code> وسيطين هما: تاريخ البدء وتاريخ الانتهاء، وتحسب هذه الدالة عدد الأيام التي تفصل بينهما، وقد تكون النتيجة عددًا سالبًا إذا جاء تاريخ الانتهاء أولًا. الوسيط الأول في المثال السابق هو اسم العمود <code>introduction_date</code> الذي يحتوي على التواريخ في الجدول <code>inventory</code>، والوسيط الثاني هو الدالة <code>CURRENT_DATE</code> التي تمثل تاريخ النظام الحالي، ويؤدي تنفيذ هذا الاستعلام إلى استرجاع عدد الأيام بين هذين التاريخين، ويضع النتائج في العمود المؤقت <code>days_since</code>.
</p>

<p>
	<strong>ملاحظة</strong>: لا تُعَد الدالة <code>DATEDIFF</code> جزءًا من مجموعة دوال SQL المعيارية الرسمية، حيث تدعم العديد من قواعد البيانات هذه الدالة، ولكن قد تختلف الصيغة بين محرّكات قواعد البيانات المختلفة، وقد اتبعنا في هذا المثال صيغة MySQL الأصيلة.
</p>

<p>
	تتضمن دوال معالجة التاريخ الأخرى المُضمَّنة في MySQL جمع وطرح فترات التواريخ والوقت، أو تنسيق التواريخ بتنسيقات لغات مختلفة، أو استرجاع اسم اليوم والشهر أو إنشاء قيم تواريخ جديدة. ويمكن معرفة المزيد حول التعامل مع التواريخ في لغة SQL في مقال <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%B1%D9%8A%D8%AE-%D9%88%D8%A7%D9%84%D8%A3%D9%88%D9%82%D8%A7%D8%AA-%D9%81%D9%8A-sql-r2411/" rel="">كيفية التعامل مع التواريخ والأوقات في SQL</a>، ويمكن أيضًا الاطلاع على <a href="https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html" rel="external nofollow">دوال التاريخ والوقت</a> في توثيق MySQL.
</p>

<h2 id="aggregatefunctions-1">
	استخدام الدوال التجميعية Aggregate Functions
</h2>

<p>
	استخدمنا في الأمثلة السابقة دوال SQL لتطبيق التحويلات أو العمليات الحسابية على قيم الأعمدة الفردية في صفٍ واحد، والذي يمثّل كتابًا في مكتبة، وتوفر لغة SQL طريقة لإجراء العمليات الحسابية على صفوف متعددة لمساعدتنا في العثور على معلومات مُجمَّعة حول مجموعة البيانات بأكملها.
</p>

<p>
	تتضمّن الدوال التجميعية الأساسية في لغة SQL الدوال التالية:
</p>

<ul>
	<li>
		<strong><code>AVG</code></strong>: لإيجاد متوسط القيم التي نجري العمليات الحسابية عليها
	</li>
	<li>
		<strong><code>COUNT</code></strong>: لإيجاد عدد القيم التي نجري العمليات الحسابية عليها
	</li>
	<li>
		<strong><code>MAX</code></strong>: لإيجاد القيمة الكبرى من مجموعة قيم
	</li>
	<li>
		<strong><code>MIN</code></strong>: لإيجاد القيمة الصغرى من مجموعة قيم
	</li>
	<li>
		<strong><code>SUM</code></strong>: لإيجاد مجموع جميع القيم
	</li>
</ul>

<p>
	يمكننا دمج دوال تجميعية متعددة في استعلام <code>SELECT</code>، فلنفترض أننا نريد التحقق من عدد الكتب المدرجَة في المكتبة، والسعر الأكبر لأي كتاب متاح، ومتوسط الأسعار لكافة الكتب، لذا ننفّذ التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7015_40" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT COUNT</span><span class="pun">(</span><span class="pln">title</span><span class="pun">)</span><span class="pln"> AS count</span><span class="pun">,</span><span class="pln">
MAX</span><span class="pun">(</span><span class="pln">price</span><span class="pun">)</span><span class="pln"> AS max_price</span><span class="pun">,</span><span class="pln">
AVG</span><span class="pun">(</span><span class="pln">price</span><span class="pun">)</span><span class="pln"> AS avg_price
FROM inventory</span><span class="pun">;</span></pre>

<p>
	تعيد هذه التعليمة الخرج التالي:
</p>

<pre class="ipsCode">الخرج
+-------+-----------+-----------+
| count | max_price | avg_price |
+-------+-----------+-----------+
|     5 |     42.13 | 25.106000 |
+-------+-----------+-----------+
1 row in set (0.001 sec)
</pre>

<p>
	يستخدم الاستعلام السابق ثلاث دوال تجميعية في الوقت نفسه، حيث تحسب الدالة <code>COUNT</code> عدد الصفوف التي يبحث عنها الاستعلام، ومرّرنا اسم العمود <code>title</code> كوسيط لهذه الدالة، ولكن يمكنك استخدام أي اسم عمود آخر كوسيط لها لأن عدد الصفوف هو نفسه لكل الأعمدة. تحسب الدالة <code>MAX</code> القيمة الكبرى للعمود <code>price</code>، ويكون اسم العمود مهمًا في هذه الحالة، بسبب إجراء الحسابات على قيم هذا العمود. الدالة الأخيرة هي الدالة <code>AVG</code> التي تحسب المتوسط الحسابي لجميع الأسعار من العمود <code>price</code>.
</p>

<p>
	يؤدي استخدام الدوال التجميعية بهذه الطريقة إلى إعادة قاعدة البيانات لصف واحد يحتوي على أعمدة مؤقتة تمثّل قيم العمليات الحسابية المُجمَّعة، حيث تستخدم العمليات الحسابية الصفوف المصدر داخليًا ولكن لا يعيدها الاستعلام.
</p>

<p>
	يمكن باستخدام لغة SQL أيضًا تقسيم الصفوف في الجدول إلى مجموعات، ثم حساب القيم المُجمَّعة لكل مجموعة على حِدة، فمثلًا يمكننا حساب متوسط أسعار الكتب لمؤلفين مختلفين لمعرفة المؤلف الذي ينشر العناوين ذات السعر الأعلى، حيث يمكنك معرفة المزيد حول تجميع الصفوف لمثل هذه العمليات الحسابية في مقال <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AA%D8%B9%D9%84%D9%8A%D9%85%D8%AA%D9%8A-group-by-%D9%88-order-by-%D9%81%D9%8A-sql-r2503/" rel="">كيفية استخدام تعليمتي GROUP BY و ORDER BY في لغة SQL</a>، ويمكن مطالعة مزيد من التفاصيل حول استخدام الدوال التجميعية في مقال <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%AC%D9%85%D9%8A%D8%B9%D9%8A%D8%A9-%D9%81%D9%8A-sql-r2410/" rel="">كيفية استخدام التعابير الرياضية والدوال التجميعية في SQL</a>.
</p>

<h2 id="-7">
	الخلاصة
</h2>

<p>
	تعلّمنا في هذا المقال ما هي دوال SQL وكيفية استخدامها لمعالجة الأعداد والسلاسل النصية والتواريخ من خلال الأمثلة العملية، ويمكن بالطبع استخدام الدوال لمعالجة وتحليل البيانات في محرّك قاعدة البيانات بطرق عديدة أخرى، ولكننا غطينا في هذا المقال الدوال الأساسية فقط. وإذا أردنا التعامل مع البيانات وتحليلها بطرق فعّالة، فيمكننا دمج الدوال مع الاستعلامات الشرطية، ومع تعليمات التجميع في لغة SQL.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-functions-in-sql" rel="external nofollow">How To Use Functions in SQL</a> لصاحبَيه Mateusz Papiernik و Rachel Lee.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AF%D9%85%D8%AC-union-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-sql-r2511/" rel="">استخدام عمليات الدمج Union في لغة SQL</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/SQL/datatype" rel="external">أنواع البيانات في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%AA%D8%AC%D9%85%D9%8A%D8%B9-%D9%88%D8%A7%D9%84%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8-%D9%81%D9%8A-sql-r846/" rel="">التجميع والترتيب في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D9%88%D8%A7%D9%84%D8%AA%D9%86%D9%82%D9%8A%D8%A8-%D9%88%D8%A7%D9%84%D8%AA%D8%B1%D8%B4%D9%8A%D8%AD-%D9%81%D9%8A-sql-r848/" rel="">البحث والتنقيب والترشيح في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r855/" rel="">دوال التعامل مع البيانات في SQL</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2515</guid><pubDate>Thu, 20 Feb 2025 15:00:02 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x639;&#x645;&#x644;&#x64A;&#x627;&#x62A; &#x627;&#x644;&#x62F;&#x645;&#x62C; Union &#x641;&#x64A; &#x644;&#x63A;&#x629; SQL</title><link>https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AF%D9%85%D8%AC-union-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-sql-r2511/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_02/UnionSQL.png.74b009011f9c483e356e6e299d41cac0.png" /></p>
<p>
	تنشر العديد من قواعد البيانات المعلومات على جداول مختلفة بحسب معناها وسياقها، وبالتالي قد نحتاج إلى الإشارة إلى أكثر من جدول واحد عند استرجاع معلومات حول البيانات الموجودة في قاعدة البيانات، لذا توفّر لغة الاستعلام البنيوية Structured Query Language -أو SQL اختصارًا- طرقًا متعددة لاسترجاع البيانات من جداول مختلفة مثل عمليات المجموعة Set Operations، وخاصةً معامل المجموعة <code>UNION</code> الذي تدعمه معظم أنظمة قواعد البيانات العلاقية Relational Database Systems والذي يأخذ نتائج استعلامين لأعمدة متطابقة ويدمجهما في استعلام واحد.
</p>

<p>
	سنشرح في هذا المقال طريقة استخدام المعامل <code>UNION</code> لاسترجاع ودمج البيانات من أكثر من جدول، ونتعلم كيفية دمجه مع عملية الترشيح Filtering لترتيب النتائج.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	يجب أن يكون لديك حاسوب يشغّل نظام <a href="https://academy.hsoub.com/programming/sql/%D9%81%D9%87%D9%85-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D9%82%D9%8A%D8%A9-r2216/" rel="">إدارة قواعد البيانات العلاقية Relational Database Management System -أو RDBMS اختصارًا-</a> المستند إلى لغة SQL. اختبرنا التعليمات والأمثلة الواردة في هذا المقال باستخدام البيئة التالية:
</p>

<ul>
	<li>
		خادم عامل على توزيعة أوبنتو Ubuntu مع مستخدم بصلاحيات مسؤول مختلف عن المستخدم الجذر وجدار حماية مضبوط باستخدام أداة UFW كما هو موضح في <a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04" rel="external nofollow">دليل الإعداد الأولي للخادم مع الإصدار 20.04 من أوبنتو</a>، ومقال <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A9-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-%D9%85%D9%86-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A8%D8%A3%D8%A8%D8%B3%D8%B7-%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-r575/" rel="">كيفية تثبيت توزيعة أوبنتو من لينكس بأبسط طريقة</a>
	</li>
	<li>
		نظام MySQL مُثبَّت ومؤمَّن على الخادم كما هو موضّح في مقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبنتو</a>، وقد نفذنا خطوات هذا المقال باستخدام مستخدم MySQL مختلف عن المستخدم الجذر وفق الطريقة الموضحة في الفقرة التالية من المقال.
	</li>
	<li>
		معرفة أساسية بتنفيذ استعلامات <code><a href="https://wiki.hsoub.com/SQL/select" rel="external">SELECT</a></code> لاختيار البيانات من قاعدة البيانات كما هو موضّح في مقال <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%B9%D9%86-%D8%A7%D9%84%D8%B3%D8%AC%D9%84%D8%A7%D8%AA-%D9%85%D9%86-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r2249/" rel="">كيفية الاستعلام عن السجلات من الجداول في SQL</a>
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: تجدر الإشارة إلى أنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية RDBMS لها تقديماتها الفريدة من لغة SQL، إذ ستعمل الأوامر المُقدّمة في هذا المقال بنجاح مع معظم هذه الأنظمة وهي جزء من صيغة لغة SQL المعيارية، ولكن قد تجد بعض الاختلافات في الصيغة أو الناتج عند اختبارها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<p>
	سنحتاج أيضًا إلى قاعدة بيانات تحتوي على بعض الجداول المُحمَّلة ببيانات تجريبية نموذجية لنتمكّن من التدرب على استخدام عمليات <code>UNION</code>. وسنوضح في القسم التالي تفاصيل حول الاتصال بخادم MySQL وإنشاء قاعدة بيانات تجريبية، والتي سنستخدمها في أمثلة هذا المقال.
</p>

<h2 id="mysql">
	الاتصال بخادم MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	سنتصل في هذا القسم بخادم MySQL وسننشئ قاعدة بيانات تجريبية لنتمكّن من اتباع الأمثلة الواردة في هذا المقال.
</p>

<p>
	إذا كانت قاعدة بيانات SQL الخاصة بنا تعمل على خادم بعيد، علينا الاتصال بالخادم مُستخدمين بروتوكول <a href="https://academy.hsoub.com/devops/security/ssh/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr></a> من جهازنا المحلي كما يلي:
</p>

<pre class="ipsCode">$ ssh user@your_server_ip
</pre>

<p>
	ثم نفتح واجهة سطر أوامر خادم MySQL مع وضع اسم حساب مستخدم MySQL الخاص بنا مكان <code>user</code>:
</p>

<pre class="ipsCode">$ mysql -u user -p
</pre>

<p>
	ننشئ قاعدة بيانات بالاسم <code>bookstore</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8346_10" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE DATABASE bookstore</span><span class="pun">;</span></pre>

<p>
	إذا أُنشئِت قاعدة البيانات بنجاح، فسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	يمكن اختيار قاعدة البيانات <code>bookstore</code> من خلال تنفيذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8346_12" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> USE bookstore</span><span class="pun">;</span></pre>

<p>
	وسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	اخترنا قاعدة البيانات، وسننشئ عدة جداول تجريبية ضمنها، حيث سنستخدم في هذا المقال مكتبة افتراضية تقدّم خدمة شراء الكتب وتأجيرها، وتُدار كل خدمة من هاتين الخدمتين على حدة، وبالتالي ستُخزَّن البيانات المتعلقة بشراء الكتب وتأجيرها في جداول منفصلة.
</p>

<p>
	<strong>ملاحظة</strong>: بسّطنا مخطط قاعدة البيانات لهذا المثال للتوضيح، إذ تكون هياكل الجداول أكثر تعقيدًا في الحياة الواقعية مع تضمين مفاتيح رئيسية Primary Keys ومفاتيح خارجية Foreign Keys ضمنها. ويمكن مطالعة مقال <a href="https://academy.hsoub.com/programming/sql/%D9%81%D9%87%D9%85-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D9%82%D9%8A%D8%A9-r2216/" rel="">فهم قواعد البيانات العلاقية</a> لمزيد من المعلومات حول كيفية تنظيم البيانات.
</p>

<p>
	يحتوي الجدول الأول <code>book_purchases</code> على بيانات حول الكتب المُشتراة والعملاء الذين أجروا عمليات الشراء، إذ سيحتوي على أربعة أعمدة هي:
</p>

<ul>
	<li>
		<strong><code>purchase_id</code></strong>: يحتوي هذا العمود على معرّف عملية الشراء ونمثّله بنوع البيانات <code>int</code>، وسيكون هذا العمود هو المفتاح الرئيسي للجدول، حيث تصبح كل قيمة معرّفًا فريدًا للصف الخاص بها
	</li>
	<li>
		<strong><code>customer_name</code></strong>: يحتوي هذا العمود على اسم العميل، ونمثّله باستخدام نوع البيانات <code>varchar</code> بحد أقصى 30 محرفًا
	</li>
	<li>
		<strong><code>book_title</code></strong>: يحتوي هذا العمود على عنوان الكتاب الذي جرى شراؤه، ونمثّله باستخدام نوع البيانات <code>varchar</code> بحد أقصى 200 محرف
	</li>
	<li>
		<strong><code>date</code></strong>: يستخدم هذا العمود نوع البيانات <code>date</code>، ويحتوي على تاريخ كل عملية شراء
	</li>
</ul>

<p>
	ننشئ هذا الجدول التجريبي باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8346_15" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE book_purchases </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">    purchase_id </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">    customer_name varchar</span><span class="pun">(</span><span class="lit">30</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">    book_title varchar</span><span class="pun">(</span><span class="lit">40</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">    date date</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">    PRIMARY KEY </span><span class="pun">(</span><span class="pln">purchase_id</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	إذا كان الخرج كما يلي، فهذا يعني إنشاء الجدول الأول بنجاح:
</p>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.00 sec)
</pre>

<p>
	يحزّن الجدول الثاني <code>book_leases</code> معلومات حول الكتب المُستعارة، وتكون بنيته مشابهة للجدول السابق، ولكن نميّز عملية تأجير الكتاب بتاريخين مختلفين هما تاريخ التأجير ومدته، وبالتالي سيحتوي هذا الجدول على خمسة أعمدة هي:
</p>

<ul>
	<li>
		<strong><code>lease_id</code></strong>: يحتوي هذا العمود على معرّف عملية التأجير، ونمثّله بنوع البيانات <code>int</code>، ويكون هذا العمود هو المفتاح الرئيسي للجدول، حيث تصبح كل قيمة معرّفًا فريدًا للصف الخاص بها
	</li>
	<li>
		<strong><code>customer_name</code></strong>: يحتوي هذا العمود على اسم العميل، ونمثّله باستخدام نوع البيانات <code>varchar</code> بحد أقصى 30 محرفًا
	</li>
	<li>
		<strong><code>book_title</code></strong>: يحتوي هذا العمود على عنوان الكتاب المُستعار، ونمثّله باستخدام نوع البيانات <code>varchar</code> بحد أقصى 200 محرف
	</li>
	<li>
		<strong><code>date_from</code></strong>: يستخدم نوع البيانات <code>date</code>، ويحتوي هذا العمود على تاريخ بدء عملية التأجير
	</li>
	<li>
		<code><strong>date_to</strong></code>: يستخدم نوع البيانات <code>date</code>، ويحتوي هذا العمود على تاريخ انتهاء عملية التأجير
	</li>
</ul>

<p>
	لننشئ الجدول الثاني باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8346_17" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE book_leases </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     lease_id </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     customer_name varchar</span><span class="pun">(</span><span class="lit">30</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     book_title varchar</span><span class="pun">(</span><span class="lit">40</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     date_from date</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     date_to date</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln">     PRIMARY KEY </span><span class="pun">(</span><span class="pln">lease_id</span><span class="pun">)</span><span class="pln">
</span><span class="pun">);</span></pre>

<p>
	يؤكّد الخرج التالي إنشاء الجدول الثاني:
</p>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.00 sec)
</pre>

<p>
	لنحمّل الآن الجدول <code>book_purchases</code> ببعض البيانات التجريبية من خلال تشغيل عملية <code>INSERT INTO</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8346_19" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO book_purchases
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'sammy'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'The Picture of Dorian Gray'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-10-01'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'sammy'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Pride and Prejudice'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-10-04'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="str">'sammy'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'The Time Machine'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-09-23'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bill'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Frankenstein'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-07-23'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bill'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'The Adventures of Huckleberry Finn'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-10-01'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="str">'walt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'The Picture of Dorian Gray'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-04-15'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="str">'walt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Frankenstein'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-10-13'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="str">'walt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Pride and Prejudice'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-10-19'</span><span class="pun">);</span></pre>

<p>
	ستضيف عملية <code>INSERT INTO</code> السابقة 8 عمليات شراء مع قيمها المحدَّدة إلى جدول <code>book_purchases</code>، حيث يشير الخرج التالي إلى إضافة جميع الصفوف الثمانية:
</p>

<pre class="ipsCode">الخرج
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0
</pre>

<p>
	ثم ندخِل بعض البيانات التجريبية في الجدول <code>book_leases</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8346_21" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO book_leases
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'sammy'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Frankenstein'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-09-14'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-11-14'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'sammy'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Pride and Prejudice'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-10-01'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-12-31'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="str">'sammy'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'The Adventures of Huckleberry Finn'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-10-01'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-12-01'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bill'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'The Picture of Dorian Gray'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-09-03'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-09-18'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bill'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Crime and Punishment'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-09-27'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-12-05'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="str">'kim'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'The Picture of Dorian Gray'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-10-01'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-11-15'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="str">'kim'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Pride and Prejudice'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-09-08'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-11-17'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="str">'kim'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'The Time Machine'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-09-04'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-10-23'</span><span class="pun">);</span></pre>

<p>
	وسيظهر الخرج التالي الذي يؤكّد إضافة هذه البيانات:
</p>

<pre class="ipsCode">الخرج
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0
</pre>

<p>
	تتعلق عمليات التأجير والشراء بعملاء وعناوين كتب متماثلة، مما سيكون مفيدًا لتوضيح سلوك المعامل <code>UNION</code>، وبذلك سنكون جاهزين لمتابعة هذا المقال والبدء في استخدام عمليات <code>UNION</code> في لغة SQL.
</p>

<h2 id="union">
	فهم صيغة المعامل UNION
</h2>

<p>
	يطلب المعامل <code>UNION</code> في لغة SQL من قاعدة البيانات أخذ مجموعتي نتائج منفصلتين ومسترجعتين من استعلامات <code>SELECT</code> فردية ودمجهما في مجموعة نتائج واحدة تحتوي على صفوف مُعادة من كلا الاستعلامين.
</p>

<p>
	<strong>ملاحظة</strong>: يمكن أن تكون استعلامات <code>SELECT</code> المستخدمة مع العملية <code>UNION</code> معقدة وتتطلب استخدام تعليمات <code>JOIN</code> أو دوال تجميعية Aggregations أو استعلامات الفرعية Subqueries. إذ يمكننا استخدام عمليات <code>UNION</code> لدمج نتائج الاستعلامات مهما كانت معقدة، ولكن للسهولة سنستخدم في أمثلتنا التالية استعلامات <code>SELECT</code> بسيطة للتركيز على كيفية تصرف المعامل <code>UNION</code>.
</p>

<p>
	يوضّح المثال التالي الصيغة العامة لتعليمة SQL التي تتضمن المعامل <code>UNION</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8346_23" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT column1</span><span class="pun">,</span><span class="pln"> column2 FROM table1
mysql</span><span class="pun">&gt;</span><span class="pln"> UNION
mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT column1</span><span class="pun">,</span><span class="pln"> column2 FROM table2</span><span class="pun">;</span></pre>

<p>
	تبدأ تعليمة SQL السابقة بتعليمة <code>SELECT</code> التي تعيد عمودين من الجدول <code>table1</code>، ونتبعها بالمعامل <code>UNION</code> وتعليمة <code>SELECT</code> ثانية، ويعيد استعلام <code>SELECT</code> الثاني أيضًا عمودين من الجدول <code>table2</code>. تخبر الكلمة المفتاحية <code>UNION</code> قاعدة البيانات بأخذ الاستعلامات السابقة واللاحقة، وتنفيذها تنفيذًا منفصلًا، ثم ضم مجموعات النتائج الخاصة بها ضمن مجموعة واحدة. يُعَد جزء الشيفرة البرمجية السابق بأكمله -بما في ذلك استعلامات <code>SELECT</code> والكلمة المفتاحية <code>UNION</code> بينهما- تعليمة SQL واحدة، لذلك لا ينتهي استعلام <code>SELECT</code> الأول بفاصلة منقوطة، والتي تظهر بعد اكتمال التعليمة فقط.
</p>

<p>
	لنفترض مثلًا أننا نريد إدخال جميع العملاء الذين اشتروا كتابًا أو استأجروه، حيث يحتفظ الجدول <code>book_purchases</code> بسجلات عمليات الشراء، بينما يخزّن الجدول <code>book_leases</code> عمليات تأجير الكتب، لذا لنشغّل الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8346_25" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT customer_name FROM book_purchases
mysql</span><span class="pun">&gt;</span><span class="pln"> UNION
mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT customer_name FROM book_leases</span><span class="pun">;</span></pre>

<p>
	وتكون مجموعة نتائج هذا الاستعلام كما يلي:
</p>

<pre class="ipsCode">الخرج
+---------------+
| customer_name |
+---------------+
| sammy         |
| bill          |
| walt          |
| kim           |
+---------------+
4 rows in set (0.000 sec)
</pre>

<p>
	يشير هذا الخرج إلى أن Sammy و Bill و Walt و Kim اشتروا كتبًا أو استأجروها. دعونا نجرّب تنفيذ تعليمتي <code>SELECT</code> تنفيذًا منفصلًا، مرةً لعمليات الشراء ومرةً لعمليات التأجير لفهم كيفية إنشاء مجموعة هذه النتائج.
</p>

<p>
	لنشغّل أولًا الاستعلام التالي لإعادة العملاء الذين اشتروا كتبًا فقط:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8346_27" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT customer_name FROM book_purchases</span><span class="pun">;</span></pre>

<p>
	وسيكون الخرج كما يلي:
</p>

<pre class="ipsCode">الخرج
+---------------+
| customer_name |
+---------------+
| sammy         |
| sammy         |
| sammy         |
| bill          |
| bill          |
| walt          |
| walt          |
| walt          |
+---------------+
8 rows in set (0.000 sec)
</pre>

<p>
	اشترى Sammy و Bill و Walt كتبًا، لكن Kim لم يفعل ذلك.
</p>

<p>
	ثم لنشغّل الاستعلام التالي لإعادة العملاء الذين استأجروا كتبًا فقط:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8346_29" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT customer_name FROM book_leases</span><span class="pun">;</span></pre>

<p>
	وسيكون الخرج كما يلي:
</p>

<pre class="ipsCode">الخرج
+---------------+
| customer_name |
+---------------+
| sammy         |
| sammy         |
| sammy         |
| bill          |
| bill          |
| kim           |
| kim           |
| kim           |
+---------------+
8 rows in set (0.000 sec)
</pre>

<p>
	يشير الجدول <code>book_leases</code> إلى أن Sammy و Bill و Kim يستعيرون كتبًا، ولكن Walt لا يستعير كتبًا أبدًا. إذا جمعنا بين الإجابتين، فيمكننا الحصول على البيانات لكل من عمليات الشراء والتأجير. الفرق المهم بين استخدام عملية <code>UNION</code> وتنفيذ استعلامين تنفيذًا منفصلًا هو أن عملية <code>UNION</code> تزيل القيم المكررة، حيث تُدمَج النتائج دون تكرار أسماء العملاء في النتيجة.
</p>

<p>
	يجب أن يعيد كلا الاستعلامين النتائج بالتنسيق نفسه لاستخدام عملية <code>UNION</code> لدمج نتائج استعلامين منفصلين بطريقة صحيحة، إذ ستؤدي التعارضات إلى حدوث أخطاء في محرّك قاعدة البيانات أو إلى ظهور نتائج لا تتطابق مع الهدف من الاستعلام كما سنوضّح في المثالين التاليين.
</p>

<h3 id="union-1">
	تنفيذ عملية UNION مع عدد غير متطابق من الأعمدة
</h3>

<p>
	لنجرّب تنفيذ عملية <code>UNION</code> مع تعليمة <code>SELECT</code> تعيد عمودًا واحدًا وتعليمة <code>SELECT</code> أخرى تعيد عمودين:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8346_31" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT purchase_id</span><span class="pun">,</span><span class="pln"> customer_name FROM book_purchases
mysql</span><span class="pun">&gt;</span><span class="pln"> UNION
mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT customer_name FROM book_leases</span><span class="pun">;</span></pre>

<p>
	سيستجيب خادم <a href="https://academy.hsoub.com/devops/servers/databases/%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-database/" rel="">قاعدة البيانات</a> بالخطأ التالي:
</p>

<pre class="ipsCode">الخرج
The used SELECT statements have a different number of columns
</pre>

<p>
	لا يمكن إجراء عمليات <code>UNION</code> على مجموعات النتائج التي لها أعداد أعمدة مختلفة.
</p>

<h3 id="union-2">
	تنفيذ عملية UNION مع ترتيب غير متطابق للأعمدة
</h3>

<p>
	لنجرّب الآن تنفيذ عملية <code>UNION</code> مع تعلميتي <code>SELECT</code> تعيدان القيم نفسها ولكن بترتيب مختلف كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8346_34" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT customer_name</span><span class="pun">,</span><span class="pln"> book_title FROM book_purchases
mysql</span><span class="pun">&gt;</span><span class="pln"> UNION
mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT book_title</span><span class="pun">,</span><span class="pln"> customer_name FROM book_leases</span><span class="pun">;</span></pre>

<p>
	لن يعيد خادم قاعدة البيانات خطأ، ولكن لن تكون مجموعة النتائج صحيحة كما يلي:
</p>

<pre class="ipsCode">الخرج
+------------------------------------+------------------------------------+
| customer_name                      | book_title                         |
+------------------------------------+------------------------------------+
| sammy                              | The Picture of Dorian Gray         |
| sammy                              | Pride and Prejudice                |
| sammy                              | The Time Machine                   |
| bill                               | Frankenstein                       |
| bill                               | The Adventures of Huckleberry Finn |
| walt                               | The Picture of Dorian Gray         |
| walt                               | Frankenstein                       |
| walt                               | Pride and Prejudice                |
| Frankenstein                       | sammy                              |
| Pride and Prejudice                | sammy                              |
| The Adventures of Huckleberry Finn | sammy                              |
| The Picture of Dorian Gray         | bill                               |
| Crime and Punishment               | bill                               |
| The Picture of Dorian Gray         | kim                                |
| Pride and Prejudice                | kim                                |
| The Time Machine                   | kim                                |
+------------------------------------+------------------------------------+
16 rows in set (0.000 sec)
</pre>

<p>
	تدمج عملية <code>UNION</code> في هذا المثال العمود الأول من الاستعلام الأول مع العمود الأول من الاستعلام الثاني وتفعل الشيء نفسه بالنسبة للعمود الثاني، وبالتالي ستخلط أسماء العملاء وعناوين الكتب مع بعضها بعضًا.
</p>

<h2 id="whereunion">
	استخدام الشروط وترتيب النتائج باستخدام UNION
</h2>

<p>
	دمجنا في المثال السابق مجموعات النتائج التي تمثل جميع الصفوف في جدولين متطابقين، ولكن ستحتاج في كثير من الأحيان إلى ترشيح الصفوف وفق شروط محددة قبل دمج النتائج، حيث يمكن لتعليمات <code>SELECT</code> المدموجة باستخدام المعامل <code>UNION</code> استخدام تعليمة <code>WHERE</code> لتطبيق ذلك.
</p>

<p>
	لنفترض مثلًا أننا نريد معرفة الكتب التي يقرأها Sammy من خلال شرائها أو استئجارها، لذا نشغّل الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8346_36" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT book_title FROM book_purchases
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE customer_name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Sammy'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> UNION
mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT book_title FROM book_leases
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE customer_name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Sammy'</span><span class="pun">;</span></pre>

<p>
	يتضمن كلا الاستعلامين تعليمة <code>WHERE</code>، مما يؤدي إلى ترشيح الصفوف من الجدولين المنفصلين لتضمين عمليات الشراء والتأجير التي أجراها Sammy فقط، وستكون نتائج هذا الاستعلام كما يلي:
</p>

<pre class="ipsCode">الخرج
+------------------------------------+
| book_title                         |
+------------------------------------+
| The Picture of Dorian Gray         |
| Pride and Prejudice                |
| The Time Machine                   |
| Frankenstein                       |
| The Adventures of Huckleberry Finn |
+------------------------------------+
5 rows in set (0.000 sec)
</pre>

<p>
	تضمن عملية <code>UNION</code> عدم وجود تكرارات في قائمة النتائج. يمكننا استخدام تعليمات <code>WHERE</code> أيضًا لتحديد الصفوف المُعادة في استعلامي <code>SELECT</code> أو أحدهما فقط، ويمكن أن تشير تعليمة <code>WHERE</code> إلى أعمدة وشروط مختلفة في كل من الاستعلامين.
</p>

<p>
	إضافةً لذلك، لا تتبع النتائج التي تعيدها عملية <code>UNION</code> ترتيبًا محدَّدًا، ولكن يمكننا تغيير ذلك من خلال استخدام تعليمة <code>ORDER BY</code>، حيث ينفَّذ الترتيب على النتائج النهائية المدموجة وليس على الاستعلامات الفردية. يمكننا فرز عناوين الكتب أبجديًا بعد استرجاع قائمة بجميع الكتب التي اشتراها أو استأجرها Sammy من خلال تنفيذ الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8346_38" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT book_title FROM book_purchases
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE customer_name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Sammy'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> UNION
mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT book_title FROM book_leases
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE customer_name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Sammy'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> ORDER BY book_title</span><span class="pun">;</span></pre>

<p>
	وسيكون الخرج كما يلي:
</p>

<pre class="ipsCode">الخرج
+------------------------------------+
| book_title                         |
+------------------------------------+
| Frankenstein                       |
| Pride and Prejudice                |
| The Adventures of Huckleberry Finn |
| The Picture of Dorian Gray         |
| The Time Machine                   |
+------------------------------------+
5 rows in set (0.001 sec)
</pre>

<p>
	نلاحظ أن إعادة النتائج بالترتيب يعتمد على العمود <code>book_title</code> الذي يحتوي على النتائج الناتجة عن دمج استعلامَي <code>SELECT</code>.
</p>

<h2 id="unionall">
	استخدام العملية UNION ALL للاحتفاظ بالنسخ المكررة
</h2>

<p>
	يزيل المعامل <code>UNION</code> تلقائيًا الصفوف المكررة من النتائج كما أظهرت الأمثلة السابقة، ولكن قد لا يكون هذا السلوك هو ما نريد تحقيقه باستخدام الاستعلام، فمثلًا لنفترض أننا نريد معرفة الكتب المُشتراة أو المستأجرة في 1 من شهر 11 من عام 2023، حيث يمكننا استرجاع هذه العناوين من خلال اتباع المثال التالي المشابه لما سبق:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8346_40" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT book_title FROM book_purchases
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE date </span><span class="pun">=</span><span class="pln"> </span><span class="str">'2022-10-01'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> UNION
mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT book_title FROM book_leases
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE date_from </span><span class="pun">=</span><span class="pln"> </span><span class="str">'2022-10-01'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> ORDER BY book_title</span><span class="pun">;</span></pre>

<p>
	وسنحصل على النتائج التالية:
</p>

<pre class="ipsCode">الخرج
+------------------------------------+
| book_title                         |
+------------------------------------+
| Pride and Prejudice                |
| The Adventures of Huckleberry Finn |
| The Picture of Dorian Gray         |
+------------------------------------+
3 rows in set (0.001 sec)
</pre>

<p>
	عناوين الكتب المُعادة هنا صحيحة، ولكن لن تخبرنا النتائج فيما إذا كانت هذه الكتب مشتراة فقط أم مستأجرة فقط أم كليهما، حيث ستظهر عناوين الكتب في الجدولين <code>book_purchases</code> و <code>book_leases</code> في الحالات التي جرى فيها شراء بعض الكتب واستئجارها، ولكن ستُفقَد هذه المعلومات في النتيجة، لأن المعامل <code>UNION</code> يزيل الصفوف المكررة.
</p>

<p>
	تمتلك لغة SQL طريقة لتغيير هذا السلوك والاحتفاظ بالصفوف المكررة، حيث يمكننا استخدام المعامل <code>UNION ALL</code> لدمج النتائج من استعلامين دون إزالة الصفوف المكررة، ويعمل المعامل <code>UNION ALL</code> بطريقة مشابهة للمعامل <code>UNION</code>، ولكن إذا وُجِدت تكرارات متعددة للقيم نفسها، فستكون جميعها موجودة في النتيجة.
</p>

<p>
	لنشغّل الاستعلام السابق نفسه مع وضع <code>UNION ALL</code> مكان <code>UNION</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8346_42" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT book_title FROM book_purchases
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE date </span><span class="pun">=</span><span class="pln"> </span><span class="str">'2022-10-01'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> UNION ALL
mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT book_title FROM book_leases
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE date_from </span><span class="pun">=</span><span class="pln"> </span><span class="str">'2022-10-01'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> ORDER BY book_title</span><span class="pun">;</span></pre>

<p>
	ستكون القائمة الناتجة أطول هذه المرة كما يلي:
</p>

<pre class="ipsCode">الخرج
+------------------------------------+
| book_title                         |
+------------------------------------+
| Pride and Prejudice                |
| The Adventures of Huckleberry Finn |
| The Adventures of Huckleberry Finn |
| The Picture of Dorian Gray         |
| The Picture of Dorian Gray         |
+------------------------------------+
5 rows in set (0.000 sec)
</pre>

<p>
	يظهَر الكتابان <code>The Adventures of Huckleberry Finn</code> و <code>The Picture of Dorian Gray</code> مرتين في مجموعة النتائج، وهذا يعني أنهما ظهرا في الجدولين <code>book_purchases</code> و <code>book_leases</code>، وبالتالي يمكننا افتراض أنه جرى تأجيرهما وشراؤهما في ذلك اليوم.
</p>

<p>
	يمكن الاختيار بين المعاملين <code>UNION</code> و <code>UNION ALL</code> والتبديل بينهما اعتمادًا على ما إذا أردنا إزالة التكرارات أو الاحتفاظ بها.
</p>

<p>
	<strong>ملاحظة</strong>: تنفيذ المعامل <code>UNION ALL</code> أسرع من تنفيذ المعامل <code>UNION</code>، إذ لا تحتاج قاعدة البيانات إلى فحص مجموعة النتائج للعثور على التكرارات. فإذا أردنا دمج نتائج استعلامات <code>SELECT</code> التي نعرف أنها لن تحتوي على أي صفوف مكررة، فيمكن أن يحقق استخدام المعامل <code>UNION ALL</code> أداءً أفضل مع مجموعات البيانات الأكبر حجمًا.
</p>

<h2 id="-1">
	الخلاصة
</h2>

<p>
	تعرّفنا في هذا المقال على كيفية استرجاع البيانات من جداول متعددة باستخدام عمليات <code>UNION</code> و <code>UNION ALL</code>، واستخدمنا أيضًا <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D9%86%D9%89-where-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2258/" rel="">تعليمات WHERE </a>لترشيح النتائج <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AA%D8%B9%D9%84%D9%8A%D9%85%D8%AA%D9%8A-group-by-%D9%88-order-by-%D9%81%D9%8A-sql-r2503/" rel="">وتعليمات ORDER BY</a> لترتيبها، وتعلّمنا الأخطاء المحتمَلة والسلوكيات غير المتوقعة إذا أنتجت تعليمات <code>SELECT</code> تنسيقات بيانات مختلفة.
</p>

<p>
	يجب أن تعمل الأوامر الواردة في هذا المقال على معظم قواعد البيانات العلاقية، ولكن يجب أن نتذكر أن كل قاعدة بيانات SQL تستخدم تقديمها الفريد للغة، لذا ننصح بالاطلاع على مقال <a href="https://academy.hsoub.com/devops/servers/databases/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D9%82%D9%8A%D8%A9-sqlite-%D9%85%D8%B9-mysql-%D9%85%D8%B9-postgresql-r72/" rel="">مقارنة بين أنظمة إدارة قواعد البيانات العلاقية: SQLite مع MySQL مع PostgreSQL</a> لمزيد من المعلومات، كما ننصح بالرجوع إلى توثيق RDBMS الرسمي للحصول على شرح مفصّل لكل أمر ومجموعة خياراته الكاملة.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-unions-in-sql" rel="external nofollow">How To Use Unions in SQL</a> لصاحبَيه Mateusz Papiernik و Rachel Lee.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/sql/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AA%D8%AF%D8%A7%D8%AE%D9%84%D8%A9-nested-queries-%D9%81%D9%8A-sql-r2505/" rel="">استخدام الاستعلامات المتداخلة Nested Queries في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%AF%D9%85%D8%AC-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r849/" rel="">الدمج بين الجداول في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/" rel="">أنواع قواعد البيانات وأهم مميزاتها واستخداماتها</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D9%8A%D9%84-%D8%B9%D9%84%D9%89-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r851/" rel="">معالجة الأخطاء والتعديل على قواعد البيانات في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%AC%D8%AF%D9%88%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-common-table-expressions-%D9%81%D9%8A-sql-r856/" rel="">التعابير الجدولية الشائعة Common Table Expressions في SQL</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2511</guid><pubDate>Fri, 14 Feb 2025 15:00:00 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x627;&#x633;&#x62A;&#x639;&#x644;&#x627;&#x645;&#x627;&#x62A; &#x627;&#x644;&#x645;&#x62A;&#x62F;&#x627;&#x62E;&#x644;&#x629; Nested Queries &#x641;&#x64A; SQL</title><link>https://academy.hsoub.com/programming/sql/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AA%D8%AF%D8%A7%D8%AE%D9%84%D8%A9-nested-queries-%D9%81%D9%8A-sql-r2505/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_02/SQL.png.f45410b9f03217d0e2ead426f99a856e.png" /></p>
<p>
	تفيدنا لغة الاستعلام البنيوية Structured Query Language -أو SQL اختصارًا- في إدارة البيانات المخزنة في نظام إدارة قواعد بيانات علاقية Relational Database Management System -أو RDBMS اختصارًا- ومن أبرز الوظائف المفيدة في SQL هي إنشاء استعلام ضمن استعلام آخر، والذي يُعرَف باسم الاستعلام الفرعي Subquery أو الاستعلام المتداخل Nested وهو موضوع مقالنا اليوم.
</p>

<h2 id="">
	متطلبات العمل
</h2>

<p>
	يجب توفر حاسوب يشغّل أحد أنواع أنظمة إدارة قواعد البيانات العلاقية RDBMS التي تستخدم <a href="https://wiki.hsoub.com/SQL" rel="external">لغة SQL</a>. وبالنسبة لهذا المقال فقد اختبرنا التعليمات والأمثلة الواردة فيه باستخدام البيئة التالية:
</p>

<ul>
	<li>
		خادم عامل على أحدث إصدار من توزيعة أوبنتو Ubuntu مع مستخدم ذو صلاحيات مسؤول مختلف عن المستخدم الجذر، وجدار حماية مُفعَّل كما هو موضح في <a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04" rel="external nofollow">دليل الإعداد الأولي للخادم مع الإصدار 20.04 من أوبنتو</a>
	</li>
	<li>
		نظام MySQL مُثبَّت ومؤمَّن على الخادم كما هو موضّح في مقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبنتو</a>
	</li>
</ul>

<p>
	وقد نفذنا خطوات هذا المقال باستخدام مستخدم MySQL مختلف عن المستخدم الجذر وفق الطريقة الموضحة في الخطوة 3 من المقال.
</p>

<p>
	<strong>ملاحظة</strong>: تجدر الإشارة إلى أنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية RDBMS لها تقديماتها الفريدة من لغة SQL، إذ ستعمل الأوامر المُقدمة في هذا المقال بنجاح مع معظم هذه الأنظمة، ولكن قد نجد بعض الاختلافات في الصيغة أو الناتج عند اختبارها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<p>
	سنحتاج أيضًا إلى قاعدة بيانات تحتوي على جدول يحتوي بيانات تجريبية نموذجية لنتمكّن من التدرب على استخدام الاستعلامات المتداخلة، وفي حال لم تكن متوفرة فيمكن مطالعة القسم التالي لمعرفة كيفية إنشاء قاعدة البيانات والجدول المستخدَمَين في أمثلة هذا المقال.
</p>

<h2 id="mysql">
	الاتصال بخادم MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	إذا كانت قاعدة بيانات SQL الخاصة بنا تعمل على خادم بعيد، علينا الاتصال بالخادم باستخدام بروتوكول <a href="https://academy.hsoub.com/devops/security/ssh/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr></a> من جهازنا المحلي كما يلي:
</p>

<pre class="ipsCode">$ ssh user@your_server_ip
</pre>

<p>
	ثم نفتح واجهة سطر أوامر خادم MySQL مع وضع اسم حساب مستخدم MySQL الخاص بنا مكان <code>user</code>:
</p>

<pre class="ipsCode">$ mysql -u user -p
</pre>

<p>
	ننشئ قاعدة بيانات بالاسم <code>zooDB</code>:
</p>

<pre class="ipsCode">mysql&gt; CREATE DATABASE zooDB;
</pre>

<p>
	إذا أُنشئِت قاعدة البيانات بنجاح، فسيظهر الخرج التالي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	يمكن اختيار قاعدة البيانات <code>zooDB</code> من خلال تنفيذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode">mysql&gt; USE zooDB;
</pre>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	اخترنا قاعدة البيانات، وسننشئ جدولًا ضمنها، حيث سننشئ جدولًا يخزّن المعلومات حول الزوار الذين يزورون حديقة الحيوان، وسيحتوي هذا الجدول على الأعمدة السبعة التالية:
</p>

<ul>
	<li>
		<code>guest_id</code>: يخزّن قيمًا للزوار الذين يزورون حديقة الحيوان ويستخدم نوع البيانات <code>int</code>، ويمثّل المفتاح الرئيسي Primary Key للجدول، ممّا يعني أن كل قيمة في هذا العمود ستمثّل معرّفًا فريدًا للصف الخاص بها
	</li>
	<li>
		<code>first_name</code>: الاسم الأول لكل زائر ويستخدم نوع البيانات <code>varchar</code> بحد أقصى 30 محرف
	</li>
	<li>
		<code>last_name</code>: الاسم الأخير لكل زائر ويستخدم نوع البيانات <code>varchar</code> بحد أقصى 30 محرف
	</li>
	<li>
		<code>guest_type</code>: يحدد هل الزائر بالغ أو طفل ويستخدم نوع البيانات <code>varchar</code> بحد أقصى 15 محرف
	</li>
	<li>
		<code>membership_type</code>: نوع العضوية لكل زائر ويستخدم نوع بيانات <code>varchar</code> بحد أقصى 30 محرف
	</li>
	<li>
		<code>membership_cost</code>: تكلفة أنواع العضوية المختلفة، ويستخدم نوع البيانات <code>decimal</code> بدقة 5 ومقياس 2، أي أن القيم الموجودة في هذا العمود يمكن أن تحتوي على خمسة أرقام مع وجود رقمين على يمين الفاصلة العشرية
	</li>
	<li>
		<code>total_visits</code>: يسجل إجمالي عدد الزيارات لكل زائر ويستخدم نوع البيانات <code>int</code>
	</li>
</ul>

<p>
	<a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r2224/" rel="">سننشئ جدولًا</a> باسم <code>guests</code> يحتوي على الأعمدة السابقة من خلال تشغيل الأمر <code>CREATE TABLE</code> التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2281_11" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE guests </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> guest_id </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> first_name varchar</span><span class="pun">(</span><span class="lit">30</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> last_name varchar</span><span class="pun">(</span><span class="lit">30</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> guest_type varchar</span><span class="pun">(</span><span class="lit">15</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> membership_type varchar</span><span class="pun">(</span><span class="lit">30</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> membership_cost </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="lit">2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> total_visits </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> PRIMARY KEY </span><span class="pun">(</span><span class="pln">guest_id</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	ثم ندخل بعض البيانات التجريبية في هذا الجدول الفارغ كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2281_14" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO guests
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">guest_id</span><span class="pun">,</span><span class="pln"> first_name</span><span class="pun">,</span><span class="pln"> last_name</span><span class="pun">,</span><span class="pln"> guest_type</span><span class="pun">,</span><span class="pln"> membership_type</span><span class="pun">,</span><span class="pln"> membership_cost</span><span class="pun">,</span><span class="pln"> total_visits</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Judy'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Hopps'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Adult'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Resident Premium Pass'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">110.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">168</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Nick'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Wilde'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Adult'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Day Pass'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">62.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Duke'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Weaselton'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Adult'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Resident Pass'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">85.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Tommy'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Yax'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Child'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Youth Pass'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">67.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">30</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Lizzie'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Yax'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Adult'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Guardian Pass'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">209.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">30</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Jenny'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Bellwether'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Adult'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Resident Premium Pass'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">110.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Idris'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Bogo'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Child'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Youth Pass'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">67.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">79</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Gideon'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Grey'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Child'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Youth Pass'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">67.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Nangi'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Reddy'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Adult'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Guardian Champion'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">400.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">241</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Octavia'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Otterton'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Adult'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Resident Pass'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">85.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">11</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Calvin'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Roo'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Adult'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Resident Premium Pass'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">110.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">173</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Maurice'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Big'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Adult'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Guardian Champion'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">400.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">13</span><span class="pun">,</span><span class="pln"> </span><span class="str">'J.K.'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Lionheart'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Child'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Day Pass'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">52.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">14</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Priscilla'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Bell'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Child'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Day Pass'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">104.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">15</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Tommy'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Finnick'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Adult'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Day Pass'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">62.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
Query OK, 15 rows affected (0.01 sec)
Records: 15  Duplicates: 0  Warnings: 0
</pre>

<p>
	نحن الآن جاهزون لبدء استخدام الاستعلامات المتداخلة في لغة SQL.
</p>

<h2 id="-1">
	ما هو الاستعلام المتداخل
</h2>

<p>
	الاستعلام في لغة SQL هو عملية تسترجع البيانات من جدول في قاعدة بيانات وتتضمن تعليمة <code>SELECT</code> دائمًا، أما الاستعلام المتداخل nested query فهو استعلام داخل استعلام آخر، بمعنى آخر الاستعلام المتداخل هو تعليمة <code>SELECT</code> توضع بين قوسين عادة، وتُضمَّن في عملية <code>SELECT</code> أو <code>INSERT</code> أو <code>DELETE</code> رئيسية، ويُعَد الاستعلام المتداخل مفيدًا في الحالات التي نريد فيها تنفيذ أوامر متعددة في تعليمة استعلام واحدة بدلًا من كتابة عدة أوامر لإعادة النتيجة المطلوبة، ويمكننا من خلال الاستعلامات المتداخلة إتمام عمليات معقدة على البيانات بطريقة أسهل.
</p>

<p>
	سنستخدم في هذا المقال الاستعلامات المتداخلة مع تعليمات <code>SELECT</code> و <code>INSERT</code> و <code>DELETE</code>، كما سنستخدم الدوال التجميعية Aggregate Functions ضمن استعلام متداخل لمقارنة قيم البيانات بقيم البيانات المفروزة التي حدّدناها باستخدام تعلميتي <code>WHERE</code> و <code>LIKE</code>.
</p>

<h2 id="select">
	استخدام الاستعلامات المتداخلة مع تعليمة SELECT
</h2>

<p>
	لنوضّح فائدة الاستعلامات المتداخلة عمليًا باستخدام البيانات التجريبية التي أضفناها في خطوة سابقة، ولنفترض مثلًا أننا نريد البحث عن جميع الزوار في الجدول <code>guests</code> الذين زاروا حديقة الحيوان بعدد مرات أكبر من متوسط الزيارات. قد نفترض أن بإمكاننا العثور على هذه المعلومات من خلال الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2281_16" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT first_name</span><span class="pun">,</span><span class="pln"> last_name</span><span class="pun">,</span><span class="pln"> total_visits
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM guests
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE total_visits </span><span class="pun">&gt;</span><span class="pln"> AVG</span><span class="pun">(</span><span class="pln">total_visits</span><span class="pun">);</span></pre>

<p>
	ولكن سيعيد الاستعلام الذي يستخدم الصيغة السابقة خطأ كما يلي:
</p>

<pre class="ipsCode">الخرج
ERROR 1111 (HY000): Invalid use of group function
</pre>

<p>
	سبب هذا الخطأ هو أن <a href="https://academy.hsoub.com/programming/sql/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r855/" rel="">الدوال التجميعية</a> مثل الدالة <code>AVG()‎</code> لا تعمل إلا إذا كانت مُنفَّذة ضمن تعليمة <code>SELECT</code>.
</p>

<p>
	أحد الخيارات لاسترجاع هذه المعلومات هو تشغيل استعلام للعثور على متوسط عدد زيارات الزوار أولًا، ثم تشغيل استعلام آخر للعثور على النتائج بناءً على تلك القيمة كما هو الحال في المثالين التاليين:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2281_19" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT AVG</span><span class="pun">(</span><span class="pln">total_visits</span><span class="pun">)</span><span class="pln"> FROM guests</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-----------------+
| avg(total_visits) |
+-----------------+
| 57.5333 |
+-----------------+
1 row in set (0.00 sec)
</pre>

<pre class="ipsCode">mysql&gt; SELECT first_name, last_name, total_visits
mysql&gt; FROM guests
mysql&gt; WHERE total_visits &gt; 57.5333;
</pre>

<pre class="ipsCode">الخرج
+----------+---------+------------+
| first_name | last_name | total_visits |
+----------+---------+------------+
| Judy | Hopps | 168 |
| Idris | Bogo | 79 |
| Gideon | Grey | 100 |
| Nangi | Reddy | 241 |
| Calvin | Roo | 173 |
+----------+---------+------------+
5 rows in set (0.00 sec)
</pre>

<p>
	ولكن يمكننا الحصول على مجموعة النتائج نفسها باستخدام استعلام واحد من خلال تداخل الاستعلام الأول (<code>SELECT AVG(total_visits) FROM guests;‎</code>) مع الاستعلام الثاني. ينبغي أن نضع في الحسبان أن استخدام العدد المناسب من الأقواس مع الاستعلامات المتداخلة أمر ضروري لإكمال العملية التي نريد تنفيذها، لأن الاستعلام المتداخل هو أول عملية تُنفَّذ:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2281_21" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT first_name</span><span class="pun">,</span><span class="pln"> last_name</span><span class="pun">,</span><span class="pln"> total_visits
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM guests
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE total_visits </span><span class="pun">&gt;</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">SELECT AVG</span><span class="pun">(</span><span class="pln">total_visits</span><span class="pun">)</span><span class="pln"> FROM guests</span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
+------------+-----------+--------------+
| first_name | last_name | total_visits |
+------------+-----------+--------------+
| Judy       | Hopps     |          168 |
| Idris      | Bogo      |           79 |
| Gideon     | Grey      |          100 |
| Nangi      | Reddy     |          241 |
| Calvin     | Roo       |          173 |
+------------+-----------+--------------+
5 rows in set (0.00 sec)
</pre>

<p>
	يوضّح المثال السابق أهمية استخدام استعلام متداخل في تعليمة واحدة كاملة للحصول على النتائج المطلوبة بدلًا من الاضطرار إلى تشغيل استعلامين منفصلين، وفق الخرج الذي حصلنا عليه فقد زار خمسة زوار حديقة الحيوان بعدد مرات أكبر من متوسط الزيارات، ويمكن أن تقدّم هذه المعلومات نظرة مفيدة للتفكير في طرق إبداعية لضمان استمرار الأعضاء الحاليين في زيارة حديقة الحيوان وتجديد عضويتهم كل سنة.
</p>

<h2 id="insert">
	استخدام الاستعلامات المتداخلة مع تعليمة INSERT
</h2>

<p>
	لا يقتصر الأمر على تضمين الاستعلام المتداخل في تعليمات <code>SELECT</code> أخرى فقط، إذ يمكننا أيضًا استخدام الاستعلامات المتداخلة لإدخال البيانات في جدول موجود مسبقًا من خلال تضمين الاستعلام المتداخل في عملية <code>INSERT</code>.
</p>

<p>
	لنفترض وجود حديقة حيوانات ما تطلب بعض المعلومات عن زوار حديقة الحيوان خاصة بنا وتود تقديم خصم بنسبة 15% للزوار الذين يشترون العضوية الدائمة في موقعها، لذا سنستخدم تعليمة <code>CREATE TABLE</code> لإنشاء جدول جديد بالاسم <code>upgrade_guests</code> والذي يحتوي على ستة أعمدة، وننتبه جيدًا لأنواع البيانات مثل <code>int</code> و <code>varchar</code> والحد الأقصى من المحارف التي يمكن الاحتفاظ بها، فإن لم تكن متماشية مع أنواع البيانات الأصلية من الجدول <code>guests</code> الذي أنشأناه في قسم إعداد قاعدة بيانات نموذجية، فسنتلقى خطأً عند محاولة إدخال بيانات من الجدول <code>guests</code> باستخدام استعلام متداخل ولن تُنقَل البيانات بطريقة صحيحة.
</p>

<p>
	لننشئ هذا الجدول مع المعلومات التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2281_24" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE upgrade_guests </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> guest_id </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> first_name varchar</span><span class="pun">(</span><span class="lit">30</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> last_name varchar</span><span class="pun">(</span><span class="lit">30</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> membership_type varchar</span><span class="pun">(</span><span class="lit">30</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> membership_cost </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="lit">2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> total_visits </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> PRIMARY KEY </span><span class="pun">(</span><span class="pln">guest_id</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	احتفظنا بمعظم معلومات نوع البيانات في هذا الجدول كما كانت في الجدول <code>guests</code> من أجل التناسق والدقة، وحذفنا أي أعمدة إضافية لا نريدها في الجدول الجديد. أصبح هذا الجدول الفارغ جاهزًا للبدء، والخطوة التالية هي إدخال قيم البيانات المطلوبة في الجدول.
</p>

<p>
	نكتب التعليمة <code>INSERT INTO</code> مع الجدول <code>upgrade_guests</code> الجديد مع وجود اتجاه واضح لمكان إدخال البيانات، ثم نكتب الاستعلام المتداخل باستخدام تعليمة <code>SELECT</code> لاسترجاع قيم البيانات ذات الصلة وتعليمة <code>FROM</code> للتأكد من أن البيانات تأتي من الجدول <code>guests</code>.
</p>

<p>
	سيطبّق خصم بقيمة 15% على الأعضاء الدائمين من خلال تضمين عملية الضرب الرياضية <code>*</code> للضرب بالعدد 0.85 ضمن الاستعلام المتداخل <code>(membership_cost * 0.85)</code>، وتفيدنا تعليمة <code>WHERE</code> في فرز القيم الموجودة في العمود <code>membership_type</code>. يمكننا تضييق نطاق النتائج لتشمل فقط نتائج الأعضاء الدائمين باستخدام تعليمة <code>LIKE</code> ووضع رمز النسبة المئوية <code>%</code> قبل وبعد الكلمة "Resident" بين علامتي اقتباس مفردتين لتحديد نوع العضوية التي تتبع النمط أو المفردات نفسها، وسيُكتَب هذا الاستعلام كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2281_28" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO upgrade_guests
mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT guest_id</span><span class="pun">,</span><span class="pln"> first_name</span><span class="pun">,</span><span class="pln"> last_name</span><span class="pun">,</span><span class="pln"> membership_type</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">membership_cost </span><span class="pun">*</span><span class="pln"> </span><span class="lit">0.85</span><span class="pun">),</span><span class="pln"> total_visits
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM guests
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE membership_type LIKE </span><span class="str">'%resident%'</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
Query OK, 5 rows affected, 5 warnings (0.01 sec)
Records: 5  Duplicates: 0  Warnings: 5
</pre>

<p>
	يشير الخرج السابق إلى إضافة خمسة سجلات إلى الجدول <code>upgrade_guests</code> الجديد. يمكن التأكد من نقل البيانات التي طلبتها بنجاح من الجدول <code>guests</code> إلى الجدول <code>upgrade_guests</code> الفارغ الذي أنشأناه من خلال تشغيل الأمر التالي مع الشروط التي حدّدناها مع الاستعلام المتداخل وتعليمة <code>WHERE</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2281_30" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM upgrade_guests</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+----------+------------+------------+-----------------------+-----------------+--------------+
| guest_id | first_name | last_name  | membership_type       | membership_cost | total_visits |
+----------+------------+------------+-----------------------+-----------------+--------------+
|        1 | Judy       | Hopps      | Resident Premium Pass |           93.50 |          168 |
|        3 | Duke       | Weaselton  | Resident Pass         |           72.25 |            4 |
|        6 | Jenny      | Bellwether | Resident Premium Pass |           93.50 |           20 |
|       10 | Octavia    | Otterton   | Resident Pass         |           72.25 |           11 |
|       11 | Calvin     | Roo        | Resident Premium Pass |           93.50 |          173 |
+----------+------------+------------+-----------------------+-----------------+--------------+
5 rows in set (0.01 sec)
</pre>

<p>
	نلاحظ الإدراج الصحيح لمعلومات عضوية الزائر الدائم "Resident" ذات الصلة من الجدول <code>guest</code> في الجدول <code>upgrade_guests</code>، مع إعادة حساب التكلفة <code>membership_cost</code> الجديدة مع تطبيق خصم 15%، وبذلك ساعدت هذه العملية في تقسيم الجمهور المناسب واستهدافه، وأصبحت الأسعار المخفَّضة متاحة بسهولة لمشاركتها مع الأعضاء الجدد المحتملين.
</p>

<h2 id="delete">
	استخدام الاستعلامات المتداخلة مع تعليمة DELETE
</h2>

<p>
	لنفترض أننا نريد إزالة الزوار المعتادين والتركيز فقط على الترويج لخصم البطاقة المميزة للأعضاء الذين لا يزورون حديقة الحيوان كثيرًا حاليًا. نبدأ هذه العملية باستخدام تعليمة <code>DELETE FROM</code> بحيث يكون من الواضح المكان الذي ستحذف البيانات منه، وهو الجدول <code>upgrade_guests</code> في حالتنا، ثم نستخدم تعليمة <code>WHERE</code> لفرز أي قيمة من العمود <code>total_visits</code> تزيد عن الكمية المحدَّدة في الاستعلام المتداخل. نستخدم تعليمة <code>SELECT</code> في استعلامنا المتداخل المُضمَّن للعثور على المتوسط الحسابي <code>AVG</code> للعمود <code>total_visits</code> بحيث تحتوي تعليمة <code>WHERE</code> السابقة على قيم البيانات المناسبة للمقارنة معها. وأخيرًا، نستخدم تعليمة <code>FROM</code> لاسترجاع تلك المعلومات من الجدول <code>guests</code>، وسيكون الاستعلام الكامل كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2281_32" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> DELETE FROM upgrade_guests
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE total_visits </span><span class="pun">&gt;</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">SELECT AVG</span><span class="pun">(</span><span class="pln">total_visits</span><span class="pun">)</span><span class="pln"> FROM guests</span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
Query OK, 2 rows affected (0.00 sec)
</pre>

<p>
	لنتأكّد من حذف هذه السجلات بنجاح من الجدول <code>upgrade_guests</code>، ونستخدم تعليمة <code>ORDER BY</code> لتنظيم النتائج وفق العمود <code>total_visits</code> بترتيب رقمي تصاعدي.
</p>

<p>
	<strong>ملاحظة</strong>: لن يؤدي استخدام تعليمة <code>DELETE</code> لحذف السجلات من جدولنا الجديد إلى حذفها من الجدول الأصلي، حيث يمكننا تشغيل التعليمة <code>SELECT * FROM original_table</code> للتأكد من وجود جميع السجلات الأصلية، حتى إن كانت محذوفة من الجدول الجديد.
</p>

<pre class="ipsCode">mysql&gt; SELECT * FROM upgrade_guests ORDER BY total_visits;
</pre>

<pre class="ipsCode">الخرج
+----------+------------+------------+-----------------------+-----------------+--------------+
| guest_id | first_name | last_name  | membership_type       | membership_cost | total_visits |
+----------+------------+------------+-----------------------+-----------------+--------------+
|        3 | Duke       | Weaselton  | Resident Pass         |           72.25 |            4 |
|       10 | Octavia    | Otterton   | Resident Pass         |           72.25 |           11 |
|        6 | Jenny      | Bellwether | Resident Premium Pass |           93.50 |           20 |
+----------+------------+------------+-----------------------+-----------------+--------------+
3 rows in set (0.00 sec)
</pre>

<p>
	يشير هذا الخرج إلى نجاح تعليمة <code>DELETE</code> والاستعلام المتداخل في حذف قيم البيانات المُحدَّدة، لذا سيحتوي هذا الجدول الآن على معلومات الزوار الثلاثة الذين عدد زياراتهم أقل من متوسط عدد الزيارات، وهو ما يُعَد نقطة انطلاق لموظف حديقة الحيوان للتواصل مع هؤلاء الزوار بشأن الترقية إلى التذاكر المميزة بسعر مخفَّض لتشجيعهم على الذهاب أكثر إلى حديقة الحيوان.
</p>

<h2 id="-2">
	الخلاصة
</h2>

<p>
	شرحنا في هذا المقال الاستعلامات المتداخلة ووضحنا فائدتها في الحصول على نتائج دقيقة لم نكن سنتمكن من الحصول عليها إلا من خلال تشغيل استعلامات منفصلة، وعرضنا أمثلة عملية لاستخدام تعليمات <code>SELECT</code> و<code>INSERT</code> و <code>DELETE</code> مع الاستعلامات المتداخلة لتوفير طريقة أخرى لإدخال البيانات أو حذفها في خطوة واحدة.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-nested-queries" rel="external nofollow">How To Use Nested Queries in SQL</a> لصاحبته Jeanelle Horcasitas.
</p>

<h2 id="-3">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AA%D8%B9%D9%84%D9%8A%D9%85%D8%AA%D9%8A-group-by-%D9%88-order-by-%D9%81%D9%8A-sql-r2503/" rel="">استخدام تعليمتي GROUP BY و ORDER BY في SQL</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/SQL/subquery" rel="external">الاستعلامات الفرعية Subqueries في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-%D9%86%D9%85%D9%88%D8%B0%D8%AC-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D8%A6%D9%82%D9%8A%D8%A9-rdm-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-%D8%A7%D9%84%D9%85%D9%87%D9%85%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r522/" rel="">مفاهيم نموذج البيانات العلائقية RDM الأساسية</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/SQL/functions" rel="external">بعض الدوال المساعدة في SQL</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2505</guid><pubDate>Fri, 07 Feb 2025 15:05:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x62A;&#x639;&#x644;&#x64A;&#x645;&#x62A;&#x64A; GROUP BY &#x648; ORDER BY &#x641;&#x64A; SQL</title><link>https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AA%D8%B9%D9%84%D9%8A%D9%85%D8%AA%D9%8A-group-by-%D9%88-order-by-%D9%81%D9%8A-sql-r2503/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_02/GROUPBYORDERBYSQL.png.43bf85c4d138732009bb23e5975fc710.png" /></p>
<p>
	يمكن لقواعد بيانات لغة الاستعلام البنيوية Structured Query Language -أو SQL اختصارًا- تخزين وإدارة بيانات كثيرة لعدد من الجداول، لذا يجب فهم كيفية فرز البيانات مع مجموعات البيانات الكبيرة لتحليل مجموعات النتائج أو تنظيم البيانات للتقارير أو الاتصالات الخارجية.
</p>

<p>
	توجد تعليمتان شائعتان في لغة SQL تساعدان في فرز البيانات هما <code>GROUP BY</code> و <code>ORDER BY</code>، حيث تفرز التعليمة <code>GROUP BY</code> البيانات من خلال تجميعها بناء على العمود أو الأعمدة التي نحددها في الاستعلام، وتُستخدَم هذه التعليمة مع دوال التجميع Aggregate Functions، وتسمح التعليمة <code>ORDER BY</code> بتنظيم مجموعات النتائج أبجديًا أو رقميًا وبترتيب تصاعدي أو تنازلي.
</p>

<p>
	سنفرز في هذا المقال نتائج استعلام لغة SQL باستخدام تعليمتي <code>GROUP BY</code> و <code>ORDER BY</code>، وسنتدرب على تنفيذ الدوال التجميعية وتعليمة <code>WHERE</code> في استعلاماتنا لفرز النتائج.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	يجب توفر حاسوب لتشغيل أحد أنواع أنظمة إدارة قواعد البيانات العلاقية Relational Database Management System -أو RDBMS اختصارًا- التي تستخدم لغة SQL. وفي مقالنا الحالي اختبرنا التعليمات والأمثلة الواردة باستخدام البيئة التالية:
</p>

<ul>
	<li>
		خادم عامل على أحدث إصدار من توزيعة أوبنتو Ubuntu مع مستخدم ذو صلاحيات مسؤول مختلف عن المستخدم الجذر وجدار حماية مُفعَّل كما هو موضح في <a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04" rel="external nofollow">دليل الإعداد الأولي للخادم مع الإصدار 20.04 من أوبنتو</a>
	</li>
	<li>
		نظام MySQL مُثبَّت ومؤمَّن على الخادم كما هو موضّح في مقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبنتو</a>، وقد نفذنا الخطوات باستخدام مستخدم MySQL مختلف عن المستخدم الجذر وفق الطريقة الموضحة في الخطوة التالية من المقال
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: تجدر الإشارة إلى أن الكثير من أنظمة إدارة قواعد البيانات العلاقية RDBMS لها تقديماتها الفريدة من لغة SQL، إذ ستعمل الأوامر المُقدّمة في هذا المقال بنجاح مع معظم هذه الأنظمة، ولكن قد توجد بعض الاختلافات في الصيغة أو الناتج عند اختبارها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<p>
	سنحتاج أيضًا إلى قاعدة بيانات تحتوي على جدول يتضمن بيانات تجريبية نموذجية لنتمكّن من التدرب على فرز نتائج البيانات، ولكن إن لم يكن لدينا قاعدة بيانات جاهزة، فيمكن مطالعة القسم التالي لمعرفة كيفية إنشاء قاعدة البيانات والجداول المستخدمة في أمثلتنا.
</p>

<h2 id="mysql">
	الاتصال بخادم MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	إذا كانت قاعدة بيانات SQL الخاصة بنا تعمل على خادم بعيد، فعلينا الاتصال بالخادم باستخدام بروتوكول <a href="https://academy.hsoub.com/devops/security/ssh/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr></a> من جهازنا المحلي كما يلي:
</p>

<pre class="ipsCode">$ ssh user@your_server_ip
</pre>

<p>
	ثم نفتح واجهة سطر أوامر خادم MySQL مع وضع اسم حساب مستخدم MySQL الخاص بنا مكان <code>user</code>:
</p>

<pre class="ipsCode">$ mysql -u user -p
</pre>

<p>
	ننشئ قاعدة بيانات بالاسم <code>movieDB</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8669_8" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE DATABASE movieDB</span><span class="pun">;</span></pre>

<p>
	إذا أُنشئِت قاعدة البيانات بنجاح، فسيظهر خرج كما يلي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	يمكن اختيار قاعدة البيانات <code>movieDB</code> من خلال تنفيذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8669_11" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> USE movieDB</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	بعد أن اخترنا قاعدة البيانات، سننشئ جدولًا ضمنها كي يخزّن معلومات عروض دور السينما المحلية، وسيحتوي هذا الجدول على الأعمدة السبعة التالية:
</p>

<ul>
	<li>
		<strong><code>theater_id</code></strong>: يخزّن قيمًا من نوع <code>int</code> لكل من قاعات عرض الأفلام، ويمثّل المفتاح الرئيسي Primary Key للجدول
	</li>
	<li>
		<strong><code>date</code></strong>: يستخدم نوع البيانات <code>DATE</code> لتخزين التاريخ المُحدَّد حسب السنة والشهر واليوم لعرض الفيلم
	</li>
	<li>
		<strong><code>time</code></strong>: يمثّل العرض المُجدوَل للفيلم، ويستخدم نوع البيانات <code>TIME</code> للساعات والدقائق والثواني
	</li>
	<li>
		<strong><code>movie_name</code></strong>: يخزّن اسم الفيلم باستخدام نوع البيانات <code>varchar</code> بحد أقصى 40 محرف
	</li>
	<li>
		<strong><code>movie_genre</code></strong>: يستخدم نوع البيانات <code>varchar</code> بحد أقصى 30 محرفًا للاحتفاظ بمعلومات حول النوع الخاص بكل فيلم
	</li>
	<li>
		<strong><code>guest_total</code></strong>: يستخدم نوع البيانات <code>int </code>لعرض العدد الإجمالي لروّاد السينما الذين حضروا عرض الفيلم
	</li>
	<li>
		<strong><code>ticket_cost</code></strong>: يستخدم نوع البيانات <code>decimal</code> ويمثّل تكلفة تذكرة عرض الفيلم
	</li>
</ul>

<p>
	لننشئ جدولًا بالاسم <code>movie_theater</code> يحتوي على الأعمدة السابقة من خلال تشغيل أمر <code>CREATE TABLE</code> التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8669_13" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE movie_theater </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> theater_id </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> date DATE</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> time TIME</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> movie_name varchar</span><span class="pun">(</span><span class="lit">40</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> movie_genre varchar</span><span class="pun">(</span><span class="lit">30</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> guest_total </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> ticket_cost </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="lit">2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> PRIMARY KEY </span><span class="pun">(</span><span class="pln">theater_id</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	ثم ندخل بعض البيانات التجريبية في هذا الجدول الفارغ كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8669_15" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO movie_theater
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">theater_id</span><span class="pun">,</span><span class="pln"> date</span><span class="pun">,</span><span class="pln"> time</span><span class="pun">,</span><span class="pln"> movie_name</span><span class="pun">,</span><span class="pln"> movie_genre</span><span class="pun">,</span><span class="pln"> guest_total</span><span class="pun">,</span><span class="pln"> ticket_cost</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-05-27'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'10:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Top Gun Maverick'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Action'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">131</span><span class="pun">,</span><span class="pln"> </span><span class="lit">18.00</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-05-27'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'10:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Downton Abbey A New Era'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Drama'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">90</span><span class="pun">,</span><span class="pln"> </span><span class="lit">18.00</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-05-27'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'10:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Men'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Horror'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">18.00</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-05-27'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'10:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'The Bad Guys'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Animation'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">83</span><span class="pun">,</span><span class="pln"> </span><span class="lit">18.00</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-05-28'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'09:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Top Gun Maverick'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Action'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">112</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8.00</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-05-28'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'09:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Downton Abbey A New Era'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Drama'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">137</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8.00</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-05-28'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'09:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Men'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Horror'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8.00</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-05-28'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'09:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'The Bad Guys'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Animation'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">142</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8.00</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-05-28'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'05:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Top Gun Maverick'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Action'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">150</span><span class="pun">,</span><span class="pln"> </span><span class="lit">13.00</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-05-28'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'05:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Downton Abbey A New Era'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Drama'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">118</span><span class="pun">,</span><span class="pln"> </span><span class="lit">13.00</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">11</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-05-28'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'05:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Men'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Horror'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">88</span><span class="pun">,</span><span class="pln"> </span><span class="lit">13.00</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-05-28'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'05:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'The Bad Guys'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Animation'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">130</span><span class="pun">,</span><span class="pln"> </span><span class="lit">13.00</span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
Query OK, 12 rows affected (0.00 sec)
Records: 12  Duplicates: 0  Warnings: 0
</pre>

<p>
	أصبحت الجداول جاهزة ومعبأة بالبيانات، ونحن جاهزون الآن لبدء فرز نتائج الاستعلام باستخدام تعليمات لغة SQL.
</p>

<h2 id="groupby">
	استخدام التعليمة GROUP BY
</h2>

<p>
	وظيفة التعليمة <code>GROUP BY</code> هي تجميع السجلات ذات القيم المشتركة، وتُستخدَم دائمًا مع دالة تجميعية، حيث تلخّص الدالة التجميعية المعلومات وتعيد نتيجة واحدة، فمثلًا يمكننا الاستعلام عن العدد الإجمالي أو مجموع قيم العمود وستنتج قيمة واحدة. ويمكن باستخدام التعليمة <code>GROUP BY</code> تطبيق الدالة التجميعية للحصول على قيمة نتيجة واحدة لكل مجموعة نريدها.
</p>

<p>
	تُعَد التعليمة <code>GROUP BY</code> مفيدة لإعادة النتائج المرغوبة المتعددة مرتبة حسب مجموعة محددة أو عدة مجموعات بدلًا من عمود واحد فقط. ويجب أن تأتي التعليمة <code>GROUP BY</code> دائمًا بعد التعليمة <code>FROM</code> والتعليمة <code>WHERE</code> إذا اخترنا استخدام إحداهما. فيما يلي مثال يوضّح بنية الاستعلام باستخدام التعليمة <code>GROUP BY</code> والدالة التجميعية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8669_17" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> GROUP BY syntax
mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT column_1</span><span class="pun">,</span><span class="pln"> AGGREGATE_FUNCTION</span><span class="pun">(</span><span class="pln">column_2</span><span class="pun">)</span><span class="pln"> FROM table GROUP BY mysql</span><span class="pun">&gt;</span><span class="pln"> column_1</span><span class="pun">;</span></pre>

<p>
	لنوضّح كيفية استخدام التعليمة <code>GROUP BY</code> من خلال مثال عملي، لنفترض أننا نقود حملة لتسويق عدة إصدارات من الأفلام، ونريد تقييم نجاح جهودنا التسويقية من خلال الطلب من دار السينما المحلية مشاركة البيانات التي جمعتها من روّاد السينما يومي الجمعة والسبت. سنبدأ بمطالعة البيانات من خلال تشغيل التعليمة <code>SELECT</code> مع الرمز <code>*</code> لتحديد جميع الأعمدة في الجدول <code>movie_theater</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8669_19" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM movie_theater</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+------------+------------+----------+-------------------------+-------------+-------------+-------------+
| theater_id | date       | time     | movie_name              | movie_genre | guest_total | ticket_cost |
+------------+------------+----------+-------------------------+-------------+-------------+-------------+
|          1 | 2022-05-27 | 10:00:00 | Top Gun Maverick        | Action      |         131 |       18.00 |
|          2 | 2022-05-27 | 10:00:00 | Downton Abbey A New Era | Drama       |          90 |       18.00 |
|          3 | 2022-05-27 | 10:00:00 | Men                     | Horror      |         100 |       18.00 |
|          4 | 2022-05-27 | 10:00:00 | The Bad Guys            | Animation   |          83 |       18.00 |
|          5 | 2022-05-28 | 09:00:00 | Top Gun Maverick        | Action      |         112 |        8.00 |
|          6 | 2022-05-28 | 09:00:00 | Downton Abbey A New Era | Drama       |         137 |        8.00 |
|          7 | 2022-05-28 | 09:00:00 | Men                     | Horror      |          25 |        8.00 |
|          8 | 2022-05-28 | 09:00:00 | The Bad Guys            | Animation   |         142 |        8.00 |
|          9 | 2022-05-28 | 05:00:00 | Top Gun Maverick        | Action      |         150 |       13.00 |
|         10 | 2022-05-28 | 05:00:00 | Downton Abbey A New Era | Drama       |         118 |       13.00 |
|         11 | 2022-05-28 | 05:00:00 | Men                     | Horror      |          88 |       13.00 |
|         12 | 2022-05-28 | 05:00:00 | The Bad Guys            | Animation   |         130 |       13.00 |
+------------+------------+----------+-------------------------+-------------+-------------+-------------+
12 rows in set (0.00 sec)
</pre>

<p>
	هذه البيانات مفيدة، ولكننا نريد إجراء تقييم أعمق وفرز النتائج لبعض الأعمدة المُحدَّدة، فقد نكون مهتمين بمعرفة مدى تقبّل رواد السينما لهذه الأفلام ذات الأنواع المختلفة مثل معرفة متوسط عدد الأشخاص الذين شاهدوا كل نوع من الأفلام.
</p>

<p>
	سنستخدم التعليمة <code>SELECT</code> لاسترجاع أنواع الأفلام المختلفة من العمود <code>movie_genre</code>، ثم نطبّق الدالة التجميعية <code>AVG</code> على العمود <code>guest_total</code>، ونستخدم التعليمة <code>AS</code> لإنشاء اسم بديل للعمود بالاسم <code>average</code>، ونضمّن التعليمة <code>GROUP BY</code> لتجميع النتائج وفق العمود <code>movie_genre</code>، إذ سيوفّر تجميعها بهذه الطريقة متوسط النتائج لكل نوع من الأفلام كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8669_21" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT movie_genre</span><span class="pun">,</span><span class="pln"> AVG</span><span class="pun">(</span><span class="pln">guest_total</span><span class="pun">)</span><span class="pln"> AS average
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM movie_theater
mysql</span><span class="pun">&gt;</span><span class="pln"> GROUP BY movie_genre</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------+----------+
| movie_genre | average  |
+-------------+----------+
| Action      | 131.0000 |
| Drama       | 115.0000 |
| Horror      |  71.0000 |
| Animation   | 118.3333 |
+-------------+----------+
4 rows in set (0.00 sec)
</pre>

<p>
	يعطي الخرج السابق المتوسطات الأربعة لكل نوع من الأفلام ضمن المجموعة <code>movie_genre</code>، حيث جذبت أفلام الحركة <code>Action</code> أعلى متوسط لعدد المشاهدين لكل عرض بناء على هذه المعلومات.
</p>

<p>
	لنفترض أننا نريد قياس الإيرادات على مدى يومين منفصلين، حيث يعيد الاستعلام التالي القيم من العمود <code>date</code> والقيم التي تعيدها الدالة التجميعية <code>SUM</code> التي تحتوي على معادلة رياضية بين قوسين لضرب عدد إجمالي روّاد السينما في تكلفة التذكرة، والتي نمثّلها على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8669_23" style=""><span class="pln"> SUM</span><span class="pun">(</span><span class="pln">guest_total </span><span class="pun">*</span><span class="pln"> ticket_cost</span><span class="pun">)</span></pre>

<p>
	يتضمن الاستعلام التالي التعليمةَ <code>AS</code> لتوفير الاسم البديل <code>total_revenue</code> للعمود الذي تعيده الدالة التجميعية، ونكمل الاستعلام باستخدام التعليمة <code>GROUP BY</code> لتجميع نتائج الاستعلام وفق العمود <code>date</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8669_25" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT date</span><span class="pun">,</span><span class="pln"> SUM</span><span class="pun">(</span><span class="pln">guest_total </span><span class="pun">*</span><span class="pln"> ticket_cost</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> AS total_revenue
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM movie_theater
mysql</span><span class="pun">&gt;</span><span class="pln"> GROUP BY date</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+------------+---------------+
| date       | total_revenue |
+------------+---------------+
| 2022-05-27 |       7272.00 |
| 2022-05-28 |       9646.00 |
+------------+---------------+
2 rows in set (0.00 sec)
</pre>

<p>
	استخدمنا هنا التعليمة <code>GROUP BY</code> لتجميع العمود <code>date</code>، لذا يمثّل الخرج نتائج إجمالي الإيرادات في مبيعات التذاكر لكل يوم، والتي هي 7272 دولارًا أمريكيًا ليوم الجمعة 27 من الشهر الخامس، و 9646 دولارًا أمريكيًا ليوم السبت 28 من الشهر الخامس.
</p>

<p>
	لنفترض الآن أننا نريد التركيز على فيلم واحد وتحليله وليكن فيلم الرسوم المتحركة The Bad Guys حيث نريد معرفة كيفية تأثير التوقيت والأسعار على اختيار الأسرة لمشاهدة فيلم رسوم متحركة. سنستخدم في هذا الاستعلام الدالة التجميعية <code>MAX</code> لاسترجاع الحد الأقصى لتكلفة التذكرة <code>ticket_cost</code>، مع التأكّد من تضمين التعليمة <code>AS</code> لإنشاء الاسم البديل للعمود وهو <code>price_data</code>، ثم نستخدم التعليمة <code>WHERE</code> لتضييق نطاق النتائج وفق العمود <code>movie_name</code> للحصول على اسم الفيلم فقط، ونستخدم التعليمة <code>AND</code> أيضًا لتحديد أوقات الأفلام الأكثر شيوعًا اعتمادًا على أرقام العمود <code>guest_total</code> التي كانت أكثر من 100 باستخدام معامل المقارنة ‎<code>&gt;</code>‎، ثم نكمل الاستعلام باستخدام التعليمة <code>GROUP BY</code> ونجمّع النتائج وفق العمود <code>time</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8669_27" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT time</span><span class="pun">,</span><span class="pln"> MAX</span><span class="pun">(</span><span class="pln">ticket_cost</span><span class="pun">)</span><span class="pln"> AS price_data
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM movie_theater
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE movie_name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"The Bad Guys"</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> AND guest_total </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">100</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> GROUP BY time</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+----------+------------+
| time     | price_data |
+----------+------------+
| 09:00:00 |       8.00 |
| 05:00:00 |      13.00 |
+----------+------------+
2 rows in set (0.00 sec)
</pre>

<p>
	نلاحظ وفقًا لهذا الخرج حضور عدد أكبر من روّاد السينما لها الفيلم في وقت مبكر من العرض الصباحي في الساعة 9:00 صباحًا، حين كان سعره أقل تكلفة وهو 8.00 دولارات أمريكية لكل تذكرة، ولكن تظهر هذه النتائج أيضًا أن روّاد الفيلم دفعوا سعر التذكرة الأعلى وهو 13.00 دولارًا أمريكيًا في الساعة 5:00 مساءً، مما يشير إلى أن العائلات تفضل العروض التي تكون وقت مبكر من المساء وستدفع سعرًا أكبر قليلًا مقابل التذكرة. يمكن لهذه المعلومات أن تفيد مدير دار السينما وتشير له لأن فتح المزيد من الفترات في المساء الباكر يمكن أن يزيد عدد العائلات التي تختار الحضور بناء على الوقت المفضل والسعر.
</p>

<p>
	تُستخدَم التعليمة <code>GROUP BY</code> دائمًا مع دالة تجميعية، ولكن قد تكون هناك استثناءات، لذا إذا أردنا تجميع النتائج دون استخدام دالة تجميعية، فيمكن استخدام التعليمة <code><a href="https://wiki.hsoub.com/SQL/distinct" rel="external">DISTINCT</a></code> لتحقيق النتيجة نفسها. حيث تزيل التعليمة <code>DISTINCT</code> أيّ تكرارات في مجموعة النتائج من خلال إعادة القيم الفريدة في العمود، ولا يمكن استخدامها إلّا مع التعليمة <code>SELECT</code>، فمثلًا إذا أردنا تجميع جميع الأفلام وفق الاسم، فيمكننا ذلك باستخدام الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8669_30" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT DISTINCT movie_name FROM movie_theater</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------------------+
| movie_name              |
+-------------------------+
| Top Gun Maverick        |
| Downton Abbey A New Era |
| Men                     |
| The Bad Guys            |
+-------------------------+
4 rows in set (0.00 sec)
</pre>

<p>
	هناك نسخ مكررة لأسماء الأفلام نظرًا لوجود عروض متعددة كما وضحنا عند عرض كافة البيانات الموجودة في الجدول، لذا أزالت التعليمة <code>DISTINCT</code> تلك التكرارات وجمّعت لنا القيم الفريدة بفعالية ضمن عمود واحد هو <code>movie_name</code>، ويُعَد هذا مطابقًا فعليًا للاستعلام التالي الذي يتضمن<br>
	التعليمة <code>GROUP BY</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8669_32" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT movie_name FROM movie_theater GROUP BY movie_name</span><span class="pun">;</span></pre>

<p>
	بعد أن شرحنا بالتفصيل طريقة استخدام التعليمة <code>GROUP BY</code> مع الدوال التجميعية، لنتعلّم الآن كيفية فرز نتائج الاستعلام باستخدام التعليمة <code>ORDER BY</code>.
</p>

<h2 id="orderby">
	استخدام التعليمة ORDER BY
</h2>

<p>
	تتمثل وظيفة التعليمة <code><a href="https://wiki.hsoub.com/SQL/order_by" rel="external">ORDER BY</a></code> في فرز النتائج بترتيب تصاعدي أو تنازلي اعتمادًا على العمود أو الأعمدة التي نحدّدها في الاستعلام، حيث ستنظّم هذه التعليمة البيانات بترتيب أبجدي أو رقمي اعتمادًا على نوع البيانات التي يخزّنها العمود الذي نحدّده بعدها. تفرز التعليمة <code>ORDER BY</code> النتائج بترتيب تصاعدي افتراضيًا، ولكن إذا أردنا استخدام الترتيب التنازلي، فيجب تضمين الكلمة المفتاحية <code>DESC</code> في استعلامنا. يمكن أيضًا استخدام التعليمة <code>ORDER BY</code> مع التعليمة <code>GROUP BY</code>، ولكن يجب أن تأتي بعدها لكي تعمل بنجاح، ويجب أن تأتي التعليمة <code>ORDER BY</code> بعد التعليمة <code>FROM</code> والتعليمة <code>WHERE</code> كما هو الحال مع التعليمة <code>GROUP BY</code>. تكون الصيغة العامة لاستخدام التعليمة <code>ORDER BY</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8669_35" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT column_1</span><span class="pun">,</span><span class="pln"> column_2 FROM table ORDER BY column_1</span><span class="pun">;</span></pre>

<p>
	لنستخدم البيانات التجريبية النموذجية الخاصة بالسينما ونتدرب على فرز النتائج باستخدام التعليمة <code>ORDER BY</code>، ولنبدأ بالاستعلام التالي الذي يسترجع القيم من العمود <code>guest_total</code> وينظّم تلك القيم العددية باستخدام التعليمة <code>ORDER BY</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8669_37" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT guest_total FROM movie_theater
mysql</span><span class="pun">&gt;</span><span class="pln"> ORDER BY guest_total</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------+
| guest_total |
+-------------+
|          25 |
|          83 |
|          88 |
|          90 |
|         100 |
|         112 |
|         118 |
|         130 |
|         131 |
|         137 |
|         142 |
|         150 |
+-------------+
12 rows in set (0.00 sec)
</pre>

<p>
	حدّد الاستعلام  السابق عمودًا يحتوي على قيم عددية، لذا فقد نظّمت التعليمة <code>ORDER BY</code> النتائج حسب الترتيب الرقمي والتصاعدي بدءًا من القيمة 25 ضمن العمود <code>guest_total</code>.
</p>

<p>
	إذا أردنا ترتيب العمود تنازليًا، فيمكننا إضافة الكلمة المفتاحية <code>DESC</code> في نهاية الاستعلام، وإذا أردنا ترتيب البيانات حسب قيم المحارف ضمن العمود <code>movie_name</code>، فيمكن تحديد ذلك في الاستعلام الذي نكتبه. لنطبّق هذا النوع من الاستعلامات باستخدام التعليمة <code>ORDER BY</code> لترتيب العمود <code>movie_name</code> حسب قيم المحارف تنازليًا، ونفرز النتائج أيضًا من خلال تضمين التعليمة <code>WHERE</code> لاسترجاع البيانات الخاصة بالأفلام المعروضة في الساعة 10:00 مساء من العمود <code>time</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8669_39" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT movie_name FROM movie_theater
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE time </span><span class="pun">=</span><span class="pln"> </span><span class="str">'10:00:00'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> ORDER BY movie_name DESC</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------------------+
| movie_name              |
+-------------------------+
| Top Gun Maverick        |
| The Bad Guys            |
| Men                     |
| Downton Abbey A New Era |
+-------------------------+
4 rows in set (0.01 sec)
</pre>

<p>
	توضّح هذه المجموعة من النتائج عروض الأفلام الأربعة المختلفة في الساعة 10:00 مساءً بترتيب أبجدي تنازليًا بدءًا من الفيلم Top Gun Maverick إلى الفيلم Downtown Abbey A New Era.
</p>

<p>
	لندمج الآن تعليمتي <code>ORDER BY</code> و <code>GROUP BY</code> مع <a href="https://wiki.hsoub.com/SQL/functions" rel="external">الدالة</a> التجميعية <code>SUM</code> لتوليد نتائج حول إجمالي الإيرادات المُستلَمة لكل فيلم، ولكن لنفترض أن دار السينما أخطأت في حساب إجمالي روّاد السينما ونسيت تضمين الحفلات الخاصة المدفوعة مسبقًا وحجز التذاكر لمجموعة مكونة من 12 شخصًا في كل عرض.
</p>

<p>
	سنستخدم في هذا الاستعلام الدالة <code>SUM</code> ونضمّن 12 زائرًا إضافيًا لكل فيلم معروض من خلال تطبيق معامل الجمع <code>+</code> ثم نجمع القيمة 12 مع العمود <code>guest_total</code>، ونتأكّد من وضع ذلك بين قوسين، ثم نضرب المجموع بالعمود <code>ticket_cost</code>، ونكمل المعادلة الرياضية بإغلاق القوسين في النهاية. نضيف التعليمة <code>AS</code> لإنشاء الاسم البديل للعمود الجديد بعنوان <code>total_revenue</code>، ثم نستخدم التعليمة <code>GROUP BY</code> لتجميع نتائج العمود <code>total_revenue</code> لكل فيلم بناءً على البيانات المسترجَعة من العمود <code>movie_name</code>. وأخيرًا، نستخدم التعليمة <code>ORDER BY</code> لتنظيم النتائج ضمن العمود الجديد <code>total_revenue</code> بترتيب تصاعدي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8669_43" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT movie_name</span><span class="pun">,</span><span class="pln"> SUM</span><span class="pun">((</span><span class="pln">guest_total </span><span class="pun">+</span><span class="pln"> </span><span class="lit">12</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> ticket_cost</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> AS total_revenue
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM movie_theater
mysql</span><span class="pun">&gt;</span><span class="pln"> GROUP BY movie_name
mysql</span><span class="pun">&gt;</span><span class="pln"> ORDER BY total_revenue</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------------------+---------------+
| movie_name              | total_revenue |
+-------------------------+---------------+
| Men                     |       3612.00 |
| Downton Abbey A New Era |       4718.00 |
| The Bad Guys            |       4788.00 |
| Top Gun Maverick        |       5672.00 |
+-------------------------+---------------+
4 rows in set (0.00 sec)
</pre>

<p>
	تعطينا هذه المجموعة من النتائج إجمالي الإيرادات لكل فيلم مع مبيعات تذاكر الزوار الإضافية التي يبلغ عددها 12 تذكرة، وتنظّم إجمالي مبيعات التذاكر بترتيب تصاعدي من الأقل إلى الأعلى، وبالتالي حصل الفيلم Top Gun Maverick على أكبر عدد من مبيعات التذاكر، بينما حصل فيلم Men على المبيعات الأقل، وكانت أفلام The Bad Guys و Downton Abbey A New Era متقاربة جدًا في إجمالي مبيعات التذاكر.
</p>

<p>
	إلى هنا نكون قد وصلنا لنهاية هذا القسم الذي تعرفنا فيه على طرق مختلفة لتطبيق تعليمة <code>ORDER BY</code> وكيفية تحديد الترتيب الذي نفضّله مثل الترتيب التصاعدي والتنازلي لكل من قيم البيانات المحرفية والرقمية، وتعلّمنا أيضًا كيفية تضمين التعليمة <code>WHERE</code> لتضييق نطاق النتائج، وأجرينا استعلامًا باستخدام كلّ من تعليمات <code>GROUP BY</code> و <code>ORDER BY</code> مع دالة تجميعية ومعادلة رياضية.
</p>

<h2 id="-1">
	الخلاصة
</h2>

<p>
	يُعَد فهم كيفية استخدام تعليمتي <code>GROUP BY</code> و <code>ORDER BY</code> أمرًا مهمًا لفرز النتائج والبيانات، حيث يمكننا من خلالهما تنظيم نتائج متعددة ضمن مجموعة واحدة أو تنظيم أحد الأعمدة بترتيب أبجدي وتنازلي أو تطبيق الأمرين معًا في وقت واحد، وتعلّمنا أيضًا طرقًا أخرى لفرز النتائج باستخدام التعليمة <code>WHERE</code>. ولمعرفة المزيد ننصح بمطالعة مقال <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D8%A7%D9%84%D8%A8%D8%AF%D9%84-wildcards-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2401/" rel="">كيفية استخدام محارف البدل Wildcards في لغة SQL</a> للتدرب على ترشيح النتائج باستخدام التعليمة <code>LIKE</code>، كما ننصح بالاطلاع على <a href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20%D8%AA%D8%B9%D9%84%D9%85%20sql/" rel="">سلسلة تعلم SQL</a> في أكاديمية حسوب للمزيد حول كيفية التعامل مع لغة SQL.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-groupby-and-orderby-in-sql" rel="external nofollow">How To Use GROUP BY and ORDER BY in SQL</a> لصاحبته Jeanelle Horcasitas.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%B9%D8%B1%D9%88%D8%B6-views-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2415/" rel="">استخدام العروض Views في لغة SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r855/" rel="">دوال التعامل مع البيانات في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%AA%D8%AC%D9%85%D9%8A%D8%B9-%D9%88%D8%A7%D9%84%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8-%D9%81%D9%8A-sql-r846/" rel="">التجميع والترتيب في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%B1%D9%8A%D8%AE-%D9%88%D8%A7%D9%84%D8%A3%D9%88%D9%82%D8%A7%D8%AA-%D9%81%D9%8A-sql-r2411/" rel="">كيفية التعامل مع التواريخ والأوقات في SQL</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2503</guid><pubDate>Mon, 03 Feb 2025 15:00:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x62A;&#x639;&#x627;&#x628;&#x64A;&#x631; CASE &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x627;&#x644;&#x627;&#x633;&#x62A;&#x639;&#x644;&#x627;&#x645; &#x627;&#x644;&#x628;&#x646;&#x64A;&#x648;&#x64A;&#x629; SQL</title><link>https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-case-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2424/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_10/----CASE-----SQL.png.680d51f3d3f7413ac3dcf7d22f949be2.png" /></p>
<p>
	سنتناول في هذا المقال العبارات الشرطية، التي تعد واحدة من العناصر الأساسية في مختلف لغات البرمجة. حيث سنستعرض كيفية استخدام العبارات الشرطية بشكل فعّال في لغة الاستعلام البنيوية SQL ونتعلم كيفية تطبيقها في مختلف سيناريوهات البرمجة.
</p>

<p>
	تتضمّن لغات البرمجة عادةً عباراتٍ شرطية، وهي عبارة عن أمر أو عدة أوامر تُنفّذ فعل مُحدّد لدى تحقق شرط معيّن. ولعلّ من أشهر العبارات الشرطية<br>
	عبارة <code>if, then, else</code> والتي تتبع عادةً المنطق التالي:
</p>

<pre class="ipsCode">if condition=true

    then action A

    else action B
</pre>

<p>
	إذ يُترجم منطق هذه العبارة لغويًا على النحو التالي: "إذا كان الشرط محققًا، نفّذ الأمر أو مجموعة الأوامر A. وإلّا في حال كون الشرط غير محققًا، نفّذ الأمر أو مجموعة الأوامر B."
</p>

<p>
	تُعدّ تعابير <code>CASE</code> ميزة في لغة الاستعلام البنيوية <a href="https://wiki.hsoub.com/SQL" rel="external">SQL</a>، فهي تتيح لنا إمكانية تطبيق منطق مماثل للعبارات الشرطية على استعلامات قواعد البيانات، وتعيين شروط لكيفية إرجاع أو عرض القيم في مجموعة النتائج الخاصة بنا. ولمعرفة كيفية استخدام تعبير <code>CASE</code> لتعيين شروط على البيانات باستخدام كل من الكلمات المفتاحية <code>WHEN</code> و<code>THEN</code> و<code>ELSE</code> و<code>END</code> تابع الفقرات التالية من المقال.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	لمتابعة الخطوات في هذا المقال، ستحتاج إلى:
</p>

<ul>
	<li>
		خادم عامل على توزيعة أوبنتو، مع مستخدم ذو صلاحيات مسؤول من نوع <code>sudo</code> مختلف عن المستخدم <strong>الجذر</strong>، وجدار حماية مُفعّل، كما هو موضح في المقال <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A9-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-%D9%85%D9%86-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A8%D8%A3%D8%A8%D8%B3%D8%B7-%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-r575/" rel="">كيفية تثبيت توزيعة أوبنتو من لينكس بأبسط طريقة</a>.
	</li>
	<li>
		نظام إدارة قواعد بيانات MySQL مثبت ومؤمن على الخادم، كما هو موضح في المقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبونتو</a>. وقد نفذنا خطوات هذا المقال باستخدام مستخدم MySQL مختلف عن المستخدم الجذر، مُنشأ وفق الطريقة الموضحة في الخطوة 3 من هذا المقال.
	</li>
</ul>

<p>
	<strong>ملاحظة:</strong> تجدر الإشارة إلى أنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية لها تقديماتها الفريدة من لغة <a href="https://academy.hsoub.com/programming/sql/" rel="">SQL</a>. فبالرغم من كون الأوامر المُقدمة في هذا المقال ستعمل مع معظم هذه الأنظمة، ولكن قد تجد بعض الاختلافات في الصيغة أو الناتج عند تنفيذها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<p>
	وبالعودة إلى مستلزمات العمل، ستحتاج أيضًا إلى قاعدة بيانات وجدول مُحمّل ببعض البيانات التجريبية النموذجية لتتمكن من التدرب على استخدام تعابير <code>CASE</code> في هذا المقال. لذا ننصحك بمتابعة الفقرة التالية بعنوان <strong>الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية</strong> للمزيد من التفاصيل حول كيفية إنشاء قاعدة بيانات وجدول لاستخدامهما في الأمثلة خلال هذا المقال.
</p>

<h2 id="mysql">
	الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	إذا كان نظام قاعدة بيانات SQL الخاص بك يعمل على خادم عن بُعد، اتصل بالخادم مُستخدمًا بروتوكول <a href="https://academy.hsoub.com/devops/security/ssh/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> </a>من جهازك المحلي على النحو التالي:
</p>

<pre class="ipsCode">$ ssh ssh user@your_server_ip
</pre>

<p>
	ثم افتح واجهة سطر الأوامر في خادم MySQL، مُستبدلًا <code>user</code> باسم حساب مستخدم MySQL الخاص بك:
</p>

<pre class="ipsCode">$ mysql -u user -p
</pre>

<p>
	أنشئ قاعدة بيانات باسم <code>caseDB</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1761_11" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE DATABASE caseDB</span><span class="pun">;</span></pre>

<p>
	وبمجرّد إنشاء قاعدة البيانات بنجاح ستحصل على خرج كالتالي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	ولاختيار قاعدة البيانات <code>caseDB</code>، نفّذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1761_14" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> USE caseDB</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	الآن وبعد اختيار قاعدة البيانات <code>caseDB</code>، لننشئ جدولًا ضمنها باستخدام الأمر <code><a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r2224/" rel="">CREATE TABLE</a></code>.
</p>

<p>
	كمثال في مقالنا هذا، سنُنشئ جدولًا يحتفظ ببيانات حول الألبومات الموسيقية العشر الأكثر مبيعًا على مر الزمان. إذ سيحتوي هذا الجدول على الأعمدة الستة التالية:
</p>

<ul>
	<li>
		<strong><code>music_id</code></strong>: يُمثّل قيمًا من نمط بيانات الأعداد الصحيحة <code>int</code> وسيكون المفتاح الأساسي للجدول، ما يعني أن كل قيمة في هذا العمود ستلعب دور المعرّف الفريد لسجلها.
	</li>
	<li>
		<strong><code>artist_name</code></strong>: مُخصص لتخزين أسماء الفنان أو مجموعة الفنانين المشاركين في الألبوم باستخدام نمط البيانات <code>varchar</code> بحد أقصى 30 محرفًا.
	</li>
	<li>
		<code>album_name</code>: يستخدم نمط البيانات <code>varchar</code>، بحد أقصى 30 محرفًا أيضًا لتخزين أسم كل ألبوم.
	</li>
	<li>
		<strong><code>release_date</code></strong>: يتتبع تاريخ إصدار كل ألبوم باستخدام نمط البيانات <code>DATE</code>، الذي يستخدم تنسيق التاريخ <code>YYYY-MM-DD</code> (اليوم بخانتين-الشهر بخانتين-السنة بأربع خانات).
	</li>
	<li>
		<strong><code>genre_type</code></strong>: يعرض تصنيف النوع الموسيقي لكل ألبوم باستخدام نمط البيانات <code>varchar</code> بحد أقصى 25 محرفًا.
	</li>
	<li>
		<strong><code>copies_sold</code></strong>: يستخدم نمط البيانات <code>decimal</code> لتخزين العدد الإجمالي لنسخ الألبوم المباعة بالملايين. إذ سنحدّد لدى تعريف هذا العمود الدقة Precision لتساوي أربعة أرقام، بواقع رقم واحد إلى يمين الفاصلة العشرية. ما يعني أن القيم في هذا العمود يمكن أن تتضمّن أربعة أرقام، واحد منها على يمين الفاصلة العشرية.
	</li>
</ul>

<p>
	لننشئ الآن جدولاً باسم <code>top_albums</code> يتضمّن هذه الأعمدة من خلال تنفيذ أمر <code>CREATE TABLE</code> التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1761_17" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE top_albums </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> music_id </span><span class="kwd">int</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> artist_name varchar</span><span class="pun">(</span><span class="lit">30</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> album_name varchar</span><span class="pun">(</span><span class="lit">30</span><span class="pun">),</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> release_date DATE</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> genre_type varchar</span><span class="pun">(</span><span class="lit">25</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> copies_sold </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="lit">1</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> PRIMARY KEY </span><span class="pun">(</span><span class="pln">music_id</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	والآن لنملأ هذا الجدول الفارغ ببعض البيانات التجريبية النموذجية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1761_19" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO top_albums
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">music_id</span><span class="pun">,</span><span class="pln"> artist_name</span><span class="pun">,</span><span class="pln"> album_name</span><span class="pun">,</span><span class="pln"> release_date</span><span class="pun">,</span><span class="pln"> genre_type</span><span class="pun">,</span><span class="pln"> copies_sold</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Michael Jackson'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Thriller'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1982-11-30'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Pop'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">49.2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Eagles'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Hotel California'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1976-12-08'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Soft Rock'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">31.5</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Pink Floyd'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'The Dark Side of the Moon'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1973-03-01'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Progressive Rock'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">21.7</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Shania Twain'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Come On Over'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1997-11-04'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Country'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">29.6</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'AC/DC'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Back in Black'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1980-07-25'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Hard Rock'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">29.5</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Whitney Houston'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'The Bodyguard'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1992-11-25'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'R&amp;B'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">32.4</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Fleetwood Mac'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Rumours'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1977-02-04'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Soft Rock'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">27.9</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Meat Loaf'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Bat Out of Hell'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1977-10-11'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Hard Rock'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">21.7</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Eagles'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Their Greatest Hits 1971-1975'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1976-02-17'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Country Rock'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">41.2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Bee Gees'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Saturday Night Fever'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1977-11-15'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Disco'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">21.6</span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
Query OK, 10 rows affected (0.01 sec)
Records: 10  Duplicates: 0  Warnings: 0
</pre>

<p>
	وبمجرّد إدخالك للبيانات تغدو مستعدًا لبدء تعلّم كيفية استخدام تعابير <code>CASE</code> في SQL.
</p>

<h2 id="case">
	فهم صياغة تعابير CASE
</h2>

<p>
	تتيح لنا تعابير <code>CASE</code> تحديد شروط لبياناتنا واستخدام منطق مشابه لجمل <code>if-then</code> للبحث ضمنها ومقارنة القيم وتقييم ما إذا كانت تحقق الشروط التي حددناها، بمعنى أنها تجعل هذه الشروط تُقيّم كمحققة "True". وفيما يلي مثال على الصياغة العامة لتعبير <code>CASE</code>:
</p>

<pre class="ipsCode">الصيغة العامّة لتعبير CASE
. . .
CASE 
    WHEN condition_1 THEN outcome_1
    WHEN condition_2 THEN outcome_2
    WHEN condition_3 THEN outcome_3
    ELSE else_outcome
END 
. . .
</pre>

<p>
	كما سنستخدم الكلمات المفتاحية التالية ضمن تعبير <code>CASE</code> وذلك اعتمادًا على عدد الشروط التي نريد تحديدها لبياناتنا:
</p>

<ul>
	<li>
		<code>WHEN</code>: تقيّم هذه الكلمة المفتاحية البيانات في الجدول وتقارنها بالشروط أو المعايير المُحدّدة، وهي مُشابهة بالمبدأ للتعبير <code>if</code> في بنية الجمل الشرطية النموذجية (<code>if-then-else</code>).
	</li>
	<li>
		<code>THEN</code>: تُستخدم لفحص كل شرط لتحديد ما إذا كانت قيمة معينة لا تستوفي المعايير المطلوبة.
	</li>
	<li>
		<code>ELSE</code>: إذا لم تحقق قيمة البيانات أيًا من الشروط المحددة بعد التحقق من جمل <code>WHEN</code> و<code>THEN</code>، فعندها تُستخدم هذه الكلمة المفتاحية لتحديد الشرط النهائي الذي ستُصنّف القيمة تحته.
	</li>
	<li>
		<code>END</code>: لإنهاء وتنفيذ تعبير <code>CASE</code> بنجاح ولتحديد شروطك، لابدّ من اختتام التعبير بالكلمة المفتاحية <code>END</code>.
	</li>
</ul>

<h2 id="case-1">
	استخدام تعابير <code>CASE</code>
</h2>

<p>
	تخيل أنك مُنسّق موسيقي تُعد قائمة أغاني من أجل حفلة لأحد أقاربك من أصحاب الذوق الموسيقي الصعب. لذا قررت أن تبحث عن أفضل عشر ألبومات موسيقية مبيعًا على مر الزمان للاستئناس بها وتوجيه قراراتك الموسيقية.
</p>

<p>
	لنراجع بدايةً القائمة التي جمعناها في جدول أفضل الألبومات <code>top_albums</code> بتنفيذ استعلام <code>SELECT</code> واستخدام رمز <code>*</code> لعرض كافّة البيانات من كل عمود:
</p>

<pre class="ipsCode">mysql&gt; SELECT * FROM top_albums;
</pre>

<pre class="ipsCode">الخرج
+----------+-----------------+-------------------------------+--------------+------------------+-------------+
| music_id | artist_name     | album_name                    | release_date | genre_type       | copies_sold |
+----------+-----------------+-------------------------------+--------------+------------------+-------------+
|        1 | Michael Jackson | Thriller                      | 1982-11-30   | Pop              |        49.2 |
|        2 | Eagles          | Hotel California              | 1976-12-08   | Soft Rock        |        31.5 |
|        3 | Pink Floyd      | The Dark Side of the Moon     | 1973-03-01   | Progressive Rock |        21.7 |
|        4 | Shania Twain    | Come On Over                  | 1997-11-04   | Country          |        29.6 |
|        5 | AC/DC           | Back in Black                 | 1980-07-25   | Hard Rock        |        29.5 |
|        6 | Whitney Houston | The Bodyguard                 | 1992-11-25   | R&amp;B              |        32.4 |
|        7 | Fleetwood Mac   | Rumours                       | 1977-02-04   | Soft Rock        |        27.9 |
|        8 | Meat Loaf       | Bat Out of Hell               | 1977-10-11   | Hard Rock        |        21.7 |
|        9 | Eagles          | Their Greatest Hits 1971-1975 | 1976-02-17   | Country Rock     |        41.2 |
|       10 | Bee Gees        | Saturday Night Fever          | 1977-11-15   | Disco            |        21.6 |
+----------+-----------------+-------------------------------+--------------+------------------+-------------+
10 rows in set (0.00 sec)
</pre>

<p>
	وبما أنّ قريبك ولد في عام 1957، فلا بدّ وأنّه قد استمتع بالكثير من الموسيقى الناجحة في شبابه زمن السبعينيات والثمانينيات. وأنت تعلم أنّه من محبي أنماط موسيقى pop وsoft rock وdisco، لذا سنعطي هذه الأنماط الأولوية في قائمة الأغاني.
</p>

<p>
	الأمر الذي يمكننا تنفيذه باستخدام تعبير <code>CASE</code> لتعيين شرط يحدد "أولوية عالية (High Priority)" لهذه الأنماط الموسيقية، من خلال الاستعلام عن قيم البيانات هذه من عمود النمط الموسيقي <code>genre_type</code>. يُنفّذ الاستعلام التالي ذلك، ويُنشئ اسمًا بديلاً للعمود الناتج من تعبير <code>CASE</code>، ليكون <code>priority</code> (الأولوية). كما يتضمّن هذا الاستعلام كل من اسم الفنان <code>artist_name</code> واسم الألبوم <code>album_name</code> وتاريخ الإصدار <code>release_date</code> لتوفير المزيد من المعلومات، ولم ننس استخدام الكلمة المفتاحية <code>END</code> لإكمال تعبير <code>CASE</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1761_21" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT artist_name</span><span class="pun">,</span><span class="pln"> album_name</span><span class="pun">,</span><span class="pln"> release_date</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> CASE WHEN genre_type </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Pop'</span><span class="pln"> THEN </span><span class="str">'High Priority'</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> WHEN genre_type </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Soft Rock'</span><span class="pln"> THEN </span><span class="str">'High Priority'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> WHEN genre_type </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Disco'</span><span class="pln"> THEN </span><span class="str">'High Priority'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">END</span><span class="pln"> AS priority
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM top_albums</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-----------------+-------------------------------+--------------+---------------+
| artist_name     | album_name                    | release_date | priority      |
+-----------------+-------------------------------+--------------+---------------+
| Michael Jackson | Thriller                      | 1982-11-30   | High Priority |
| Eagles          | Hotel California              | 1976-12-08   | High Priority |
| Pink Floyd      | The Dark Side of the Moon     | 1973-03-01   | NULL          |
| Shania Twain    | Come On Over                  | 1997-11-04   | NULL          |
| AC/DC           | Back in Black                 | 1980-07-25   | NULL          |
| Whitney Houston | The Bodyguard                 | 1992-11-25   | NULL          |
| Fleetwood Mac   | Rumours                       | 1977-02-04   | High Priority |
| Meat Loaf       | Bat Out of Hell               | 1977-10-11   | NULL          |
| Eagles          | Their Greatest Hits 1971-1975 | 1976-02-17   | NULL          |
| Bee Gees        | Saturday Night Fever          | 1977-11-15   | High Priority |
+-----------------+-------------------------------+--------------+---------------+
10 rows in set (0.00 sec)
</pre>

<p>
	على الرغم من أنّ هذا الخرج يعكس الشروط المُحددة للأنماط الموسيقية ذات الأولوية العالية <code>High Priority</code>، ولكن ونظرًا لعدم استخدامنا للكلمة المفتاحية <code>ELSE</code> فقد ظهرت قيم بيانات غير معروفة أو مفقودة، والتي تُعرف بالقيم الخالية <code><a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D8%A7%D9%84%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D9%88%D8%A7%D9%84%D8%B9%D8%A7%D9%85%D9%84-is-null-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2270/" rel="">NULL</a></code>. ففي حين قد لا يكون استخدام الكلمة المفتاحية <code>ELSE</code> ضروريًا في حال كانت قيم البيانات تلبي جميع الشروط المُحددّة في التعبير <code>CASE</code>، إلّا أنّها مفيدة لأي بيانات متبقية (لا تلبي الشروط المُحددة)، إذ باستخدامها يمكن تصنيفها تحت شرطٍ آخر واحد.
</p>

<p>
	للاستعلام التالي، سنكتب نفس تعبير <code>CASE</code> السابق، ولكن سنحدد هذه المرة شرطًا باستخدام الكلمة المفتاحية <code>ELSE</code>. إذ تُصنّف تعليمة <code>ELSE</code> في المثال التالي أي قيم بيانات أنماط موسيقية غير مُصنّفة ضمن الأولوية العالية <code>High Priority</code> على أنّها "<code>Maybe</code> أي قد تؤخذ بالحسبان":
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1761_24" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT artist_name</span><span class="pun">,</span><span class="pln"> album_name</span><span class="pun">,</span><span class="pln"> release_date</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> CASE WHEN genre_type </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Pop'</span><span class="pln"> THEN </span><span class="str">'High Priority'</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> WHEN genre_type </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Soft Rock'</span><span class="pln"> THEN </span><span class="str">'High Priority'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> WHEN genre_type </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Disco'</span><span class="pln"> THEN </span><span class="str">'High Priority'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> ELSE </span><span class="str">'Maybe'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">END</span><span class="pln"> AS priority
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM top_albums</span><span class="pun">;</span></pre>

<pre class="ipsCode">[sceondary_label Output]
+-----------------+-------------------------------+--------------+---------------+
| artist_name     | album_name                    | release_date | priority      |
+-----------------+-------------------------------+--------------+---------------+
| Michael Jackson | Thriller                      | 1982-11-30   | High Priority |
| Eagles          | Hotel California              | 1976-12-08   | High Priority |
| Pink Floyd      | The Dark Side of the Moon     | 1973-03-01   | Maybe         |
| Shania Twain    | Come On Over                  | 1997-11-04   | Maybe         |
| AC/DC           | Back in Black                 | 1980-07-25   | Maybe         |
| Whitney Houston | The Bodyguard                 | 1992-11-25   | Maybe         |
| Fleetwood Mac   | Rumours                       | 1977-02-04   | High Priority |
| Meat Loaf       | Bat Out of Hell               | 1977-10-11   | Maybe         |
| Eagles          | Their Greatest Hits 1971-1975 | 1976-02-17   | Maybe         |
| Bee Gees        | Saturday Night Fever          | 1977-11-15   | High Priority |
+-----------------+-------------------------------+--------------+---------------+
10 rows in set (0.00 sec)
</pre>

<p>
	وبذلك يُعبّر هذا الخرج على نحوٍ أفضل عن الشروط التي وضعناها لتحديد لألبومات ذات الأولوية الأعلى وتلك بدون أولوية. وعلى الرغم من أنّ ذلك يساعد في إعطاء الأولوية لأفضل أربع ألبومات — وهي <code>Thriller</code> و<code>Hotel California</code> و<code>Rumours</code> و<code>Saturday Night Fever</code>، ومع ذلك أنت مقتنع بأنّه ينبغي تنويع قائمة الأغاني أكثر، ولكن في هذه الحالة سيتوجب عليك إقناع قريبك بالأمر أيضًا.
</p>

<p>
	لذا تقرّر إجراء تجربة بسيطة، طالبًا من قريبك توسيع ذائقته الموسيقية مُستمعًا إلى الألبومات المتبقية. فتقدّم هذه الألبومات له دون أن تعطيه أي تصوّر مُسبق عنها، وتطلب منه تقييمها بأمانة كهادئة Mellow أو ممتعة Fun أو مملة Boring. بعد انتهائه، يُعطيك قائمة مكتوبة بخط اليد تحتوي على تقييماته. وبذلك أصبح لديك المعلومات اللازمة لتحديد الشروط لاستعلامك كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1761_26" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT artist_name</span><span class="pun">,</span><span class="pln"> album_name</span><span class="pun">,</span><span class="pln"> release_date</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> CASE WHEN genre_type </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Hard Rock'</span><span class="pln"> THEN </span><span class="str">'Boring'</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> WHEN genre_type </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Country Rock'</span><span class="pln"> THEN </span><span class="str">'Mellow'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> WHEN genre_type </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Progressive Rock'</span><span class="pln"> THEN </span><span class="str">'Fun'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> WHEN genre_type </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Country'</span><span class="pln"> THEN </span><span class="str">'Fun'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> WHEN genre_type </span><span class="pun">=</span><span class="pln"> </span><span class="str">'R&amp;B'</span><span class="pln"> THEN </span><span class="str">'Boring'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> ELSE </span><span class="str">'High Priority'</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">END</span><span class="pln"> AS score
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM top_albums</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-----------------+-------------------------------+--------------+---------------+
| artist_name     | album_name                    | release_date | score         |
+-----------------+-------------------------------+--------------+---------------+
| Michael Jackson | Thriller                      | 1982-11-30   | High Priority |
| Eagles          | Hotel California              | 1976-12-08   | High Priority |
| Pink Floyd      | The Dark Side of the Moon     | 1973-03-01   | Fun           |
| Shania Twain    | Come On Over                  | 1997-11-04   | Fun           |
| AC/DC           | Back in Black                 | 1980-07-25   | Boring        |
| Whitney Houston | The Bodyguard                 | 1992-11-25   | Boring        |
| Fleetwood Mac   | Rumours                       | 1977-02-04   | High Priority |
| Meat Loaf       | Bat Out of Hell               | 1977-10-11   | Boring        |
| Eagles          | Their Greatest Hits 1971-1975 | 1976-02-17   | Mellow        |
| Bee Gees        | Saturday Night Fever          | 1977-11-15   | High Priority |
+-----------------+-------------------------------+--------------+---------------+
10 rows in set (0.00 sec)
</pre>

<p>
	بناءً على هذا الخرج، يبدو أنّ قريبك مستعد لتجربة موسيقا جديدة، وقد سرّك على وجه الخصوص تقييمه الجيد لفرقة Pink Floyd. لكنكَ شعرتَ بخيبة أمل لعدم إظهاره الاهتمام الكافي بأغاني AC/DC وMeat Loaf وWhitney Houston الرائعة.
</p>

<p>
	ولربما سيكون قريبك أكثر تقبّلاً للتغيير إذا أظهرتَ له أنّ بعض الألبومات تحظى بشعبية أكبر من غيرها على نحوٍ موضوعيّ، لذا تقرّر أن تعرض بعض الأرقام لدعم وجهة نظرك. فهذه الألبومات في الواقع هي الأكثر مبيعًا، إذ حققت مبيعات تُقدّر بملايين النسخ على مرّ العقود. لذا، في استعلامنا القادم، سنُنشئ تعبير <code>CASE</code> جديد يُحدّد تقييمًا استنادًا إلى البيانات الرقمية من عمود عدد النسخ المُباعة <code>copies_sold</code> للألبومات التي تم بيعها حتى الآن.
</p>

<p>
	سنستخدم تعبير <code>CASE</code> لتحديد الشروط بحيث نُعيّن الألبومات التي حققت ما لا يقل عن 35 مليون نسخة على أنّها "الأفضل best"، وتلك التي بيع منها 25 مليون نسخة على أنّها "ممتازة great"، وتلك التي بيع منها 20 مليون على أنّها "جيدة good"، وأي شيء أقل من ذلك على أنًه "متوسط mediocre"، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1761_28" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT artist_name</span><span class="pun">,</span><span class="pln"> album_name</span><span class="pun">,</span><span class="pln"> release_date</span><span class="pun">,</span><span class="pln"> CASE WHEN copies_sold </span><span class="pun">&gt;</span><span class="lit">35.0</span><span class="pln"> THEN </span><span class="str">'best'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> WHEN copies_sold </span><span class="pun">&gt;</span><span class="lit">25.0</span><span class="pln"> THEN </span><span class="str">'great'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> WHEN copies_sold </span><span class="pun">&gt;</span><span class="lit">20.0</span><span class="pln"> THEN </span><span class="str">'good'</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> ELSE </span><span class="str">'mediocre'</span><span class="pln"> </span><span class="kwd">END</span><span class="pln"> AS score FROM top_albums</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-----------------+-------------------------------+--------------+-------+
| artist_name     | album_name                    | release_date | score |
+-----------------+-------------------------------+--------------+-------+
| Michael Jackson | Thriller                      | 1982-11-30   | best  |
| Eagles          | Hotel California              | 1976-12-08   | great |
| Pink Floyd      | The Dark Side of the Moon     | 1973-03-01   | good  |
| Shania Twain    | Come On Over                  | 1997-11-04   | great |
| AC/DC           | Back in Black                 | 1980-07-25   | great |
| Whitney Houston | The Bodyguard                 | 1992-11-25   | great |
| Fleetwood Mac   | Rumours                       | 1977-02-04   | great |
| Meat Loaf       | Bat Out of Hell               | 1977-10-11   | good  |
| Eagles          | Their Greatest Hits 1971-1975 | 1976-02-17   | best  |
| Bee Gees        | Saturday Night Fever          | 1977-11-15   | good  |
+-----------------+-------------------------------+--------------+-------+
10 rows in set (0.00 sec)
</pre>

<p>
	وفق الخرج السابق لم يُصنّف أي من الألبومات على أنّه "متوسط mediocre"، نظرًا لأنّ كل منها قد حقق مبيعات تزيد عن 20 مليون نسخة. ومع ذلك وبناءً على هذه التقييمات، تبرز بعض الألبومات مقارنةً بالبقية. وبذلك يمكنك الآن أن تقدّم لقريبك دليلًا قويًا يبرّر تشغيل أغاني الفنانين AC/DC أو Whitney Houston، إذ أنّ ألبوماتهم قد حققت مبيعات تزيد عن 25 مليون نسخة، مما يجعلها من أهم الأعمال الموسيقية الموجودة.
</p>

<p>
	وبذلك تشكّل لديك فهم لكيفية استخدام تعبير <code>CASE</code> لتحديد شروط تعود لأغراض متنوعة، بحيث تتعامل مع قيم بيانات محرفية ورقمية. كما غدوت على معرفة بكيفية استخدام التعبير <code>CASE</code> لنفس منطق عبارة <code>if-then</code> لمقارنة تلك القيم وإنشاء الردود المناسبة بناءً على الشروط التي ترغب بها.
</p>

<h2 id="-1">
	الخلاصة
</h2>

<p>
	لعلّ فهم كيفية استخدام تعبير <code>CASE</code> قد يساعد على تصفية بياناتك وفقًا للشروط التي تضعها. فسواء كنت تريد تحديد أولويات مختلفة لقيمٍ معينة أو تقييمها بناءً على معايير تعكس الرأي العام أو أرقام مُحدّدة، فإنّها توفّر مرونة تلبي احتياجاتك. وإذا كنت ترغب في معرفة طرق أخرى يمكنك من خلالها معالجة قيم البيانات في مجموعات النتائج الخاصة بك، اطّلع على مقال حول كيفية <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AF%D9%88%D8%A7%D9%84-cast-%D9%88%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%B6%D9%85-%D9%81%D9%8A-sql-r2418/" rel="">معالجة البيانات باستخدام دوال CAST وتعابير الضم في SQL</a>.
</p>

<p>
	وللمزيد حول SQL، نشجعك على متابعة <a href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20%D8%AA%D8%B9%D9%84%D9%85%20sql/" rel="">سلسلة تعلم SQL</a> في أكاديمية حسوب.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-case-expressions-in-sql" rel="external nofollow">How To Use CASE Expressions in SQL</a> لصاحبه Jeanelle Horcasitas.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق:  <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AF%D9%88%D8%A7%D9%84-cast-%D9%88%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%B6%D9%85-%D9%81%D9%8A-sql-r2418/" rel="">كيفية معالجة البيانات باستخدام دوال CAST وتعابير الضم في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%AA%D8%B9%D9%84%D9%8A%D9%85%D8%A7%D8%AA-%D8%B4%D8%B1%D8%B7%D9%8A%D8%A9-%D8%B9%D8%A8%D8%B1-case-%D9%81%D9%8A-sql-r847/" rel="">تنفيذ تعليمات شرطية عبر CASE في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%AA%D8%AC%D9%85%D9%8A%D8%B9-%D9%88%D8%A7%D9%84%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8-%D9%81%D9%8A-sql-r846/" rel="">التجميع والترتيب في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r855/" rel="">دوال التعامل مع البيانات في SQL</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2424</guid><pubDate>Thu, 10 Oct 2024 15:09:04 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x645;&#x639;&#x627;&#x644;&#x62C;&#x629; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x62F;&#x648;&#x627;&#x644; CAST &#x648;&#x62A;&#x639;&#x627;&#x628;&#x64A;&#x631; &#x627;&#x644;&#x636;&#x645; &#x641;&#x64A; SQL</title><link>https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AF%D9%88%D8%A7%D9%84-cast-%D9%88%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%B6%D9%85-%D9%81%D9%8A-sql-r2418/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_10/-----CAST----SQL.png.1e1a18950c5f0d5933f3f104beb60d5b.png" /></p>
<p>
	لدى إنشاء جدول في قاعدة بيانات SQL للمرة الأولى، يجب تحديد بنيته العامّة، وذلك من خلال سرد جميع الأعمدة التي نريد لهذا الجدول أن يحتويها ونمط البيانات التي ستخزنها كل منها. بعدها وعند إضافة بيانات إلى الجدول، يجب أن تتطابق القيم التي نُدخلها مع أنماط البيانات المُحددّة لكل عمود على حدة. إذ يمكن لقاعدة بيانات SQL ضمان عدم إدخال أي قيم على نحو خاطئ من خلال فرض إدخال قيم تتماشى مع البنية المُعرّفة مُسبقًا للجدول. ورغم ذلك، قد تجعل هذه البنية الصارمة الأمور أصعب لدى مقارنة قيمتين بأنماط بيانات مختلفة أو لدى محاولة تجميع قيم من أعمدة متعددة لتظهر كقيمة ناتجة واحدة.
</p>

<p>
	نشرح في هذا المقال كيفية معالجة البيانات باستخدام دوال <code>CAST</code> التي تفيد في تحويل نمط بيانات قيمة معينة -أو مجموعة قيم- إلى نمط بيانات آخر، واستخدام تعبير ضم concatenation expression لربط قيم بيانات محرفية ورقمية معًا كسلسلة. كما سنتدرب على تنفيذ دالة <code>CAST</code> وتعبير الضم في نفس الاستعلام للحصول على تعليمة كاملة.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	لمتابعة الخطوات في هذا المقال، ستحتاج إلى:
</p>

<ul>
	<li>
		خادم عامل على توزيعة أوبنتو، مع مستخدم ذو صلاحيات مسؤول من نوع <code>sudo</code> مختلف عن المستخدم <strong>الجذر</strong>، وجدار حماية مُفعّل، كما هو موضح في المقال <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A9-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-%D9%85%D9%86-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A8%D8%A3%D8%A8%D8%B3%D8%B7-%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-r575/" rel="">كيفية تثبيت توزيعة أوبنتو من لينكس بأبسط طريقة</a>.
	</li>
	<li>
		MySQL مثبتة ومؤمنة على الخادم، كما هو موضح في المقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبونتو</a>. وقد نفذنا خطوات هذا المقال باستخدام مستخدم MySQL مختلف عن المستخدم الجذر، مُنشأ وفق الطريقة الموضحة في الخطوة 3 من هذا المقال.
	</li>
</ul>

<p>
	<strong>ملاحظة:</strong> تجدر الإشارة إلى أنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية لها تقديماتها الفريدة من لغة <a href="https://academy.hsoub.com/programming/sql/" rel="">SQL</a>. فبالرغم من كون الأوامر المُقدمة في هذا المقال ستعمل مع معظم هذه الأنظمة، ولكن قد تجد بعض الاختلافات في الصيغة أو الناتج عند تنفيذها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<p>
	وبالعودة إلى مستلزمات العمل، ستحتاج أيضًا إلى قاعدة بيانات وجدول مُحمّل ببعض البيانات التجريبية النموذجية لتتمكن من التدرب على استخدام دوال <code>CAST</code> وتعابير الضم في هذا المقال. لذا ننصحك بمتابعة الفقرة التالية <strong>الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية</strong> للمزيد من التفاصيل حول كيفية إنشاء قاعدة بيانات وجدول لاستخدامهما في الأمثلة خلال هذا المقال.
</p>

<h2 id="mysql">
	الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	إذا كان نظام قاعدة بيانات SQL الخاص بك يعمل على خادم عن بُعد، اتصل بالخادم مُستخدمًا بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> من جهازك المحلي على النحو التالي:
</p>

<pre class="ipsCode">$ ssh ssh user@your_server_ip
</pre>

<p>
	ثم افتح واجهة سطر الأوامر في خادم MySQL، مُستبدلًا <code>user</code> باسم حساب مستخدم MySQL الخاص بك:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5845_8" style=""><span class="pln">$ mysql </span><span class="pun">-</span><span class="pln">u user </span><span class="pun">-</span><span class="pln">p</span></pre>

<p>
	أنشئ قاعدة بيانات باسم <code>castconDB</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5845_10" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE DATABASE castconDB</span><span class="pun">;</span></pre>

<p>
	وبمجرّد إنشاء قاعدة البيانات بنجاح ستحصل على خرج كالتالي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	ولاختيار قاعدة البيانات <code>castconDB</code>، نفّذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5845_12" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> USE castconDB</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	الآن وبعد اختيار قاعدة البيانات <code>castconDB</code>، لننشئ جدولًا ضمنها باستخدام الأمر <code><a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r2224/" rel="">CREATE TABLE</a></code>.
</p>

<p>
	كمثال في مقالنا هذا، سننشئ جدولًا يُسجّل الدرجات خلال فترة فصل الخريف الدراسي لصف الأستاذ فريد لطلاب الصف السادس. سيضم الجدول الأعمدة الخمسة عشر التالية:
</p>

<ul>
	<li>
		<strong><code>student_id</code></strong>: يُمثّل قيمًا من نمط بيانات الأعداد الصحيحة <code>int</code> وسيكون المفتاح الأساسي للجدول، ما يعني أن كل قيمة في هذا العمود ستلعب دور المعرّف الفريد لسجلها.
	</li>
	<li>
		<strong><code>first_name</code></strong>: مُخصص لأسماء الطلاب الأولى وذلك باستخدام نمط البيانات <code>varchar</code> بحد أقصى 20 محرفًا.
	</li>
	<li>
		<strong><code>last_name</code></strong>: مُخصص لتخزين أسماء الطلاب الأخيرة وذلك باستخدام نمط البيانات <code>varchar</code> بحد أقصى أيضًا 20 محرفًا.
	</li>
	<li>
		<code><strong>email_addres</strong>s</code>: لتخزين البريد الإلكتروني لكل طالب باستخدام نمط البيانات <code>varchar</code> بحد أقصى 30 محرفًا.
	</li>
	<li>
		<strong><code>participation_grade</code></strong>: يعرض درجة النشاط الصفّي الإجمالية لكل طالب باستخدام نمط البيانات <code>int</code>.
	</li>
	<li>
		<strong><code>attendance_grade</code></strong>: يستخدم نمط بيانات <code>int</code> لعرض درجات حضور كل طالب.
	</li>
	<li>
		<strong><code>midterm_deadline</code></strong>: يستخدم نمط بيانات <code>TIMESTAMP</code> من أجل عرض الموعد النهائي الذي يجب على كل طالب تقديم الامتحان النصفي بحلوله. يجمع هذا النمط بين التاريخ والوقت في سلسلة واحدة ويستخدم الصيغة<br>
		التالية: (<code>YYYY-MM-DD HH:MM:SS</code>).
	</li>
	<li>
		<strong><code>midterm_submitted</code></strong>: يسجّل اليوم والوقت الدقيق اللذين قدّم فيهما الطلاب الامتحان النصفي باستخدام نمط البيانات <code>TIMESTAMP</code>.
	</li>
	<li>
		<strong><code>midterm_grade</code></strong>: يستخدم نمط بيانات <code>decimal</code> لتخزين درجة كل طالب في الامتحان النصفي. إذ سنحدد لدى تعرف هذا العمود الدقة (Precision) لتساوي أربعة أرقام، بواقع رقم واحد إلى يمين الفاصلة العشرية (Scale). ما يعني أن القيم في هذا العمود يمكن أن تتضمن أربعة أرقام، واحد منها على يمين الفاصلة العشرية.
	</li>
	<li>
		<strong><code>essay_deadline</code></strong>: يعرض الوقت والتاريخ اللذين يجب على الطلاب تقديم مقالاتهم بحلولهما، باستخدام نمط البيانات <code>TIMESTAMP</code>.
	</li>
	<li>
		<strong><code>essay_submitted</code></strong>: يستخدم نمط بيانات <code>TIMESTAMP</code> لتتبع وقت وتاريخ تقديم الطلاب لواجب كتابة المقال.
	</li>
	<li>
		<strong><code>essay_grade</code></strong>: يحتوي على درجات المقال لكل طالب باستخدام نمط البيانات <code>decimal</code>، بدقة أربعة أرقام واحد منها إلى يمين الفاصلة العشرية.
	</li>
	<li>
		<strong><code>finalexam_deadline</code></strong>: يخزن معلومات موعد الامتحان النهائي باستخدام نمط البيانات <code>TIMESTAMP</code>.
	</li>
	<li>
		<strong><code>finalexam_submitted</code></strong>: يستخدم نمط بيانات <code>TIMESTAMP</code> لتسجيل الوقت والتاريخ الفعليين لتقديم الطلاب لامتحانهم النهائي.
	</li>
	<li>
		<strong><code>finalexam_grade</code></strong>: يحتوي على درجة الامتحان النهائي لكل طالب باستخدام نمط البيانات <code>decimal</code> بدقة أربعة أرقام، واحد منها إلى يمين الفاصلة العشرية.
	</li>
</ul>

<p>
	لننشئ الآن جدولاً باسم <code>fall_grades</code> يتضمّن هذه الأعمدة من خلال تنفيذ أمر <code>CREATE TABLE</code> التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5845_15" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE fall_grades </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> student_id </span><span class="kwd">int</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> first_name varchar</span><span class="pun">(</span><span class="lit">20</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> last_name varchar</span><span class="pun">(</span><span class="lit">20</span><span class="pun">),</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> email_address varchar</span><span class="pun">(</span><span class="lit">30</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> participation_grade </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> attendance_grade </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> midterm_deadline TIMESTAMP</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> midterm_submitted TIMESTAMP</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> midterm_grade </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="lit">1</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> essay_deadline TIMESTAMP</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> essay_submitted TIMESTAMP</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> essay_grade </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="lit">1</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> finalexam_deadline TIMESTAMP</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> finalexam_submitted TIMESTAMP</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> finalexam_grade </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="lit">1</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> PRIMARY KEY </span><span class="pun">(</span><span class="pln">student_id</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span></pre>

<p>
	والآن لنملأ هذا الجدول الفارغ ببعض البيانات التجريبية النموذجية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5845_17" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO fall_grades
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">student_id</span><span class="pun">,</span><span class="pln"> first_name</span><span class="pun">,</span><span class="pln"> last_name</span><span class="pun">,</span><span class="pln"> email_address</span><span class="pun">,</span><span class="pln"> participation_grade</span><span class="pun">,</span><span class="pln"> attendance_grade</span><span class="pun">,</span><span class="pln"> midterm_deadline</span><span class="pun">,</span><span class="pln"> midterm_submitted</span><span class="pun">,</span><span class="pln"> midterm_grade</span><span class="pun">,</span><span class="pln"> essay_deadline</span><span class="pun">,</span><span class="pln"> essay_submitted</span><span class="pun">,</span><span class="pln"> essay_grade</span><span class="pun">,</span><span class="pln"> finalexam_deadline</span><span class="pun">,</span><span class="pln"> finalexam_submitted</span><span class="pun">,</span><span class="pln"> finalexam_grade</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Arnold'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Shortman'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ashortman@ps118.com'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">98</span><span class="pun">,</span><span class="pln"> </span><span class="lit">90</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-16 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-16 06:30:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">85.8</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20 03:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">90.1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-11 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-11 03:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">82.5</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Helga'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Pataki'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hpataki@ps118.com'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">85</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-16 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-16 10:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">88.4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-21 03:15:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">72.5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-11 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-11 05:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">90.0</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Gerald'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Johanssen'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'gjohanssen@ps118.com'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">95</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-16 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-16 02:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">94.2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20 02:45:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">95.8</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-11 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-11 11:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">88.1</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Phoebe'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Heyerdahl'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'pheyerdahl@ps118.com'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-16 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-16 11:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">98.8</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20 11:15:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">90.4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-11 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-11 11:40:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100.0</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Harold'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Berman'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hberman@ps118.com'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">75</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-16 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-16 08:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">75.7</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-22 09:15:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">67.5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-11 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-11 09:15:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">90.9</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Eugene'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Horowitz'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ehorowitz@ps118.com'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-16 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-16 01:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100.0</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20 01:22:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">89.9</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-11 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-11 07:55:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">98.2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Rhonda'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Lloyd'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'rlloyd@ps118.com'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">80</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-16 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-16 06:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">90.4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20 06:09:00'</span><span class="pun">,</span><span class="lit">81.3</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-11 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-11 06:45:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">95.5</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Stinky'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Peterson'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'speterson@ps118.com'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">85</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-16 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-16 03:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">70.6</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20 05:55:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">93.1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-11 12:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-11 10:11:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">73.2</span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
Query OK, 8 rows affected (0.01 sec)
Records: 8  Duplicates: 0  Warnings: 0
</pre>

<p>
	وبمجرّد إدخالك للبيانات تغدو مستعدًا لبدء تعلّم كيفية استخدام الدوال <code>CAST</code> وتعابير الضم في SQL.
</p>

<h2 id="cast">
	استخدام دوال <code>CAST</code>
</h2>

<p>
	تتيح لك الدالة <code>CAST</code> إمكانية تحويل قيمة حرفية أو القيم المُخزنة ضمن عمودٍ ما إلى نمط بياناتٍ محدد. ويُعدّ استخدامها مفيدًا لضمان توافق أنماط بيانات القيم ضمن تعبيرٍ مُعيّن.
</p>

<p>
	ولاستخدام الأمر <code>CAST</code>، لابدّ من تحديد العمود أو الأعمدة المُتضمّنة لقيم البيانات الحالية المرغوب بتحويلها، ومن ثم كتابة قيم البيانات المُفضلة و/أو طولها ضمن التعبير. وفيما يلي مثال على الصيغة العامّة:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5845_19" style=""><span class="pln">CAST </span><span class="kwd">function</span><span class="pln"> syntax
</span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln">CAST</span><span class="pun">(قيم</span><span class="pln"> </span><span class="pun">البيانات</span><span class="pln"> </span><span class="pun">الحالية</span><span class="pln"> AS </span><span class="pun">قيم</span><span class="pln"> </span><span class="pun">البيانات</span><span class="pln"> </span><span class="pun">المرغوبة)</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span></pre>

<p>
	وتجدر الملاحظة إلى أنّ دوال <code>CAST</code> تخضع لقواعد معيّنة كي تعمل على نحو صحيح. فعلى سبيل المثال، من المهم التأكد من كون نمط البيانات الذي نرغب في تحويله متوافق مع النمط الذي سنحوّل إليه. فبالعودة إلى البيانات في مثالنا، لن نتمكن باستخدام دالة <code>CAST</code> من تحويل القيم في عمود درجة الامتحان النهائي <code>finalexam_grade</code> من قيم بيانات رقمية (وهو نمط البيانات <code>decimal</code> في حالتنا) مباشرةً إلى قيم سلاسل محرفية، كأن نعبّر عن الدرجات بأحرف. وبالمثل، لا يمكننا تحويل أنماط بيانات مُحدّدة بطول أقصى مثل الأعمدة التي تحمل قيم <code>varchar(30)‎</code> في مثالنا إلى مقدار أطول مثل <code>‎varchar(35</code>).
</p>

<p>
	كما تجدر الملاحظة إلى أنّ تقديمات <a href="https://academy.hsoub.com/programming/sql/" rel="">SQL</a> المختلفة ستتصرف على نحوٍ مختلف لدى تنفيذ استعلامات باستخدام دوال <code>CAST</code> لتحويل أنماط البيانات. فقد ينتج مثلًا عن تنفيذ استعلام باستخدام دالة <code>CAST</code> في MySQL نتائج مختلفة عن تشغيل نفس الاستعلام في PostgreSQL.
</p>

<p>
	الآن ولتحقيق فهم أعمق حول كيفية استخدام دوال <code>CAST</code>، لنتخيل السيناريو التالي بناءً على البيانات في المثال السابق والتي أدخلناها في الخطوة السابقة. إذ يستعد الأستاذ فريد، معلم الصف السادس في مدرسة PS 118، لتقديم درجاته للفصل الدراسي الخريفي. إذ كان يتابع عن كثب تقدم كل طالب، وهو مهتم على وجه الخصوص بدرجاتهم في كل من الامتحان النصفي وواجب كتابة المقال والامتحان النهائي. وبفرض أنّك تعمل مع الأستاذ فريد كمدرّس مساعد، وقد طلب منك تزويده بالمعلومات المُتعلّقة بالمهام الدراسية آنفة الذكر.
</p>

<p>
	ستستخرج في هذه الحالة البيانات المطلوبة من خلال إنشاء استعلام عن جدول <code>fall_grades</code> لاسترجاع المعلومات اللازمة، مثل أسماء الطلاب الأولى <code>first_name</code> وأسماءهم الأخيرة <code>last_name</code>، بالإضافة إلى درجاتهم لكل مهمة دراسية، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5845_22" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT first_name</span><span class="pun">,</span><span class="pln"> last_name</span><span class="pun">,</span><span class="pln"> midterm_grade</span><span class="pun">,</span><span class="pln"> essay_grade</span><span class="pun">,</span><span class="pln"> finalexam_grade FROM fall_grades</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+------------+-----------+---------------+-------------+-----------------+
| first_name | last_name | midterm_grade | essay_grade | finalexam_grade |
+------------+-----------+---------------+-------------+-----------------+
| Arnold     | Shortman  |          85.8 |        90.1 |            82.5 |
| Helga      | Pataki    |          88.4 |        72.5 |            90.0 |
| Gerald     | Johanssen |          94.2 |        95.8 |            88.1 |
| Phoebe     | Heyerdahl |          98.8 |        90.4 |           100.0 |
| Harold     | Berman    |          75.7 |        67.5 |            90.9 |
| Eugene     | Horowitz  |         100.0 |        89.9 |            98.2 |
| Rhonda     | Lloyd     |          90.4 |        81.3 |            95.5 |
| Stinky     | Peterson  |          70.6 |        93.1 |            73.2 |
+------------+-----------+---------------+-------------+-----------------+
8 rows in set (0.00 sec)
</pre>

<p>
	وبعد تقديم هذه النتائج إلى الأستاذ، بيّن لك أنّ النظام الذي يستخدمه يسمح له بإدخال الدرجات كأعداد صحيحة حصرًا، وبالتالي لابدّ من تحويل هذه القيم العشرية إلى أعدادٍ صحيحة. فتقرر استخدام دالة <code>CAST</code> لتحويل عدد القيم المحرفية المحدد (وهو في هذه الحالة أربعة محارف من نمط البيانات <code>decimal</code>) إلى قيمتين محرفيتين.
</p>

<p>
	وسنستخدم لهذا الاستعلام نفس صيغة الاستعلام من المثال أعلاه ولكن مع تضمين دالة <code>CAST</code> لتحويل نمط البيانات <code>decimal</code> إلى قيمتين محرفيتين فقط لكل من المهام الدراسية. إذ ستُطبّق الدالة <code>CAST</code> على ثلاث تعابير مختلفة (لكل من <code>midterm_grade</code> و <code>essay_grade </code>و <code>final_exam_grade</code>) وذلك لتحويلها إلى قيم بيانات مكونة من قيمتين محرفيتين فقط:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9912_7" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT first_name</span><span class="pun">,</span><span class="pln"> last_name</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> CAST</span><span class="pun">(</span><span class="pln">midterm_grade AS </span><span class="kwd">char</span><span class="pun">(</span><span class="lit">2</span><span class="pun">))</span><span class="pln"> AS midterm</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> CAST</span><span class="pun">(</span><span class="pln">essay_grade AS </span><span class="kwd">char</span><span class="pun">(</span><span class="lit">2</span><span class="pun">))</span><span class="pln"> AS essay</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> CAST</span><span class="pun">(</span><span class="pln">finalexam_grade AS </span><span class="kwd">char</span><span class="pun">(</span><span class="lit">2</span><span class="pun">))</span><span class="pln"> AS finalexam 
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM fall_grades</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+------------+-----------+---------+-------+-----------+
| first_name | last_name | midterm | essay | finalexam |
+------------+-----------+---------+-------+-----------+
| Arnold     | Shortman  | 85      | 90    | 82        |
| Helga      | Pataki    | 88      | 72    | 90        |
| Gerald     | Johanssen | 94      | 95    | 88        |
| Phoebe     | Heyerdahl | 98      | 90    | 10        |
| Harold     | Berman    | 75      | 67    | 90        |
| Eugene     | Horowitz  | 10      | 89    | 98        |
| Rhonda     | Lloyd     | 90      | 81    | 95        |
| Stinky     | Peterson  | 70      | 93    | 73        |
+------------+-----------+---------+-------+-----------+
8 rows in set, 24 warnings (0.00 sec)
</pre>

<p>
	الآن وبعد مراجعة درجات كل طالب، يسأل السيد فريد إذا كان بإمكانك جلب معلومات حول التواريخ والأوقات الدقيقة التي قدم فيها كل طالب مهامه الدراسية، ولجلب هذه البيانات، سننفّذ تعليمة <code>SELECT</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9912_9" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT first_name</span><span class="pun">,</span><span class="pln"> last_name</span><span class="pun">,</span><span class="pln"> midterm_deadline</span><span class="pun">,</span><span class="pln"> essay_deadline</span><span class="pun">,</span><span class="pln"> finalexam_deadline FROM fall_grades</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+------------+-----------+---------------------+---------------------+---------------------+
| first_name | last_name | midterm_deadline    | essay_deadline      | finalexam_deadline  |
+------------+-----------+---------------------+---------------------+---------------------+
| Arnold     | Shortman  | 2022-10-16 12:00:00 | 2022-11-20 12:00:00 | 2022-12-11 12:00:00 |
| Helga      | Pataki    | 2022-10-16 12:00:00 | 2022-11-20 12:00:00 | 2022-12-11 12:00:00 |
| Gerald     | Johanssen | 2022-10-16 12:00:00 | 2022-11-20 12:00:00 | 2022-12-11 12:00:00 |
| Phoebe     | Heyerdahl | 2022-10-16 12:00:00 | 2022-11-20 12:00:00 | 2022-12-11 12:00:00 |
| Harold     | Berman    | 2022-10-16 12:00:00 | 2022-11-20 12:00:00 | 2022-12-11 12:00:00 |
| Eugene     | Horowitz  | 2022-10-16 12:00:00 | 2022-11-20 12:00:00 | 2022-12-11 12:00:00 |
| Rhonda     | Lloyd     | 2022-10-16 12:00:00 | 2022-11-20 12:00:00 | 2022-12-11 12:00:00 |
| Stinky     | Peterson  | 2022-10-16 12:00:00 | 2022-11-20 12:00:00 | 2022-12-11 12:00:00 |
+------------+-----------+---------------------+---------------------+---------------------+
8 rows in set (0.00 sec)
</pre>

<p>
	يتنهد الأستاذ فريد بعد مراجعة هذا الخرج مُخبرًا إياك بأنّ هذه المعلومات صعبة التحليل للغاية. إذ تم تعيين كل هذه الأعمدة لتخزين قيم من نمط البيانات <code>TIMESTAMP</code>، وهو سبب طولها البالغ. فتقرر استخدام دالة <code>CAST</code> لتحويل الخرج إلى شكل أسهل للقراءة والفهم، وأيضًا لتقسيم الاستعلام إلى قسمين واحد للتواريخ وآخر للأوقات.
</p>

<p>
	للاستعلام فقط عن أوقات تقديم الطلاب لواجباتهم، لننفّذ دالة <code>CAST</code> محولين قيم البيانات من تلك الأعمدة المحددة إلى قيم وقت من النمط <code>time</code>:
</p>

<pre class="ipsCode">mysql&gt; SELECT first_name, last_name, 
mysql&gt; CAST(midterm_submitted AS time) AS midterm, 
mysql&gt; CAST(essay_submitted AS time) AS essay, 
mysql&gt; CAST(finalexam_submitted AS time) AS finalexam 
mysql&gt; FROM fall_grades;
</pre>

<pre class="ipsCode">الخرج
+------------+-----------+----------+----------+-----------+
| first_name | last_name | midterm  | essay    | finalexam |
+------------+-----------+----------+----------+-----------+
| Arnold     | Shortman  | 06:30:00 | 03:00:00 | 03:00:00  |
| Helga      | Pataki    | 10:00:00 | 03:15:00 | 05:00:00  |
| Gerald     | Johanssen | 02:00:00 | 02:45:00 | 11:00:00  |
| Phoebe     | Heyerdahl | 11:00:00 | 11:15:00 | 11:40:00  |
| Harold     | Berman    | 08:00:00 | 09:15:00 | 09:15:00  |
| Eugene     | Horowitz  | 01:00:00 | 01:22:00 | 07:55:00  |
| Rhonda     | Lloyd     | 06:00:00 | 06:09:00 | 06:45:00  |
| Stinky     | Peterson  | 03:00:00 | 05:55:00 | 10:11:00  |
+------------+-----------+----------+----------+-----------+
8 rows in set (0.00 sec)
</pre>

<p>
	يُقدّم هذا الخرج لمحة عامة حول الإطار الزمني الذي أنهى فيه كل طالب واجباته. ومع ملاحظة أنّ موعد تسليم كل من الواجبات يحين عند منتصف ليل يوم الأحد، نجد أنّ العديد من الطلاب كانوا متسقين مع توقيتهم، في حين أنهى آخرون واجباتهم مبكرًا، أو قبل الموعد المحدد عند منتصف الليل بقليل.
</p>

<p>
	ولكن تُمثّل هذه المعلومات نصف ما طلبه الأستاذ فريد فقط، لذا دعونا نعمل على الاستعلام التالي الذي سيستخدم دالة <code>CAST</code> لتحويل تلك القيم ذاتها من نمط البيانات <code>TIMESTAMP</code> إلى قيم تواريخ. سننفّذ نفس الاستعلام كما في المرة السابقة، ولكن مستبدلين نمط بيانات التاريخ <code>time</code> بنمط بيانات الوقت <code>time</code> هذه المرة:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9912_11" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT first_name</span><span class="pun">,</span><span class="pln"> last_name</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> CAST</span><span class="pun">(</span><span class="pln">midterm_submitted AS date</span><span class="pun">)</span><span class="pln"> AS midterm</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> CAST</span><span class="pun">(</span><span class="pln">essay_submitted AS date</span><span class="pun">)</span><span class="pln"> AS essay</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> CAST</span><span class="pun">(</span><span class="pln">finalexam_submitted AS date</span><span class="pun">)</span><span class="pln"> AS finalexam 
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM fall_grades</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+------------+-----------+------------+------------+------------+
| first_name | last_name | midterm    | essay      | finalexam  |
+------------+-----------+------------+------------+------------+
| Arnold     | Shortman  | 2022-10-16 | 2022-11-20 | 2022-12-11 |
| Helga      | Pataki    | 2022-10-16 | 2022-11-21 | 2022-12-11 |
| Gerald     | Johanssen | 2022-10-16 | 2022-11-20 | 2022-12-11 |
| Phoebe     | Heyerdahl | 2022-10-16 | 2022-11-20 | 2022-12-11 |
| Harold     | Berman    | 2022-10-16 | 2022-11-22 | 2022-12-11 |
| Eugene     | Horowitz  | 2022-10-16 | 2022-11-20 | 2022-12-11 |
| Rhonda     | Lloyd     | 2022-10-16 | 2022-11-20 | 2022-12-11 |
| Stinky     | Peterson  | 2022-10-16 | 2022-11-20 | 2022-12-11 |
+------------+-----------+------------+------------+------------+
8 rows in set (0.00 sec)
</pre>

<p>
	يُمكننا استنادًا إلى هذا الخرج تحديد الطلاب الذين قدموا واجباتهم بعد الموعد النهائي، ما يوضّح سبب تأثر درجاتهم الناتج عن خصم نقاط بسبب التأخر. فعلى سبيل المثال، قدّمت الطالبة هيلجا واجبها بعد يوم واحد من الموعد (<code>2022-11-21</code>)، وقدّم الطالب هارولد واجبه بعد يومين (<code>2022-11-22</code>) من الموعد النهائي للمقال والمُحدد في (<code>2022-11-20</code>).
</p>

<p>
	على الرغم من رضا الأستاذ فريد عن هذه النتائج المُنقحة، إلا أنه يحتاج إلى مزيد من المساعدة في صياغة البيانات على نحوٍ أوضح لتقديم تقرير درجاته. ستتدرب في القسم التالي من المقال على كيفية استخدام تعابير الضم القادرة على دمج قيم حرفية متعددة أو القيم من أعمدة مختلفة ضمن قيمة سلسلة نصية واحدة، مما يساعد في جعل المعلومات أوضح للتفسير كعبارة أو جملة كاملة.
</p>

<h2 id="-1">
	استخدام تعابير الضم
</h2>

<p>
	يُمكنك مع استخدام تعبير الضم <code>CONCAT</code> معالجة البيانات بجمع قيم محرفية أو رقمية من أعمدة مختلفة معًا لتظهر كنتيجة واحدة.
</p>

<p>
	إذ تعيد قواعد بيانات SQL بوجهٍ عام قيم البيانات ضمن مجموعات نتائج منفصلة في أعمدتها المختلفة. فعلى سبيل المثال، لو استعلمنا عن الاسم الأول <code>first_name</code> والأخير <code>last_name</code> لطلاب PS 118، سيظهر الخرج كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9912_13" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT first_name</span><span class="pun">,</span><span class="pln"> last_name FROM fall_grades</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| Arnold     | Shortman  |
| Helga      | Pataki    |
| Gerald     | Johanssen |
| Phoebe     | Heyerdahl |
| Harold     | Berman    |
| Eugene     | Horowitz  |
| Rhonda     | Lloyd     |
| Stinky     | Peterson  |
+------------+-----------+
8 rows in set (0.00 sec)
</pre>

<p>
	لكن هذه المعلومات غير مُقدّمة بالشكل الذي يفضله الأستاذ فريد لتقريره. لذا، لننفّذ استعلامًا آخر باستخدام الضم لدمج الأسماء الأولى والأخيرة للطلاب ضمن سلسلة نصية واحدة. يؤدي الاستعلام التالي هذه المهمة باستخدام الكلمة المفتاحية <code>CONCAT</code> موفرًا للعمود الناتج الاسم بديل <code>full_names</code>، على النحو التالي:
</p>

<pre class="ipsCode">mysql&gt; SELECT CONCAT(first_name, last_name) AS full_names FROM fall_grades;
</pre>

<pre class="ipsCode">الخرج
+-----------------+
| full_names      |
+-----------------+
| ArnoldShortman  |
| HelgaPataki     |
| GeraldJohanssen |
| PhoebeHeyerdahl |
| HaroldBerman    |
| EugeneHorowitz  |
| RhondaLloyd     |
| StinkyPeterson  |
+-----------------+
8 rows in set (0.00 sec)
</pre>

<p>
	تعمل تعابير الضم مع كافة أنماط البيانات إجمالًا، ولكن في حال عدم تحديد تفاصيل من قبيل التباعد بين قيم البيانات، سيظهر الخرج على هيئة سلسلة نصية متصلة دون فواصل كما يتضح من الخرج أعلاه. ولتصحيح ذلك، يمكن إضافة زوج من علامات الاقتباس الفردية مع فراغ بينهما (<code>' '</code>) بين عمودي <code>first_name</code> و<code>last_name</code> بحيث تظهر القيم على هيئة سلسلة واحدة، ولكن هذه المرة مع فراغ بينهما لجعلها أسهل للقراءة:
</p>

<pre class="ipsCode">mysql&gt; SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM fall_grades;
</pre>

<pre class="ipsCode">الخرج
+------------------+
| full_name        |
+------------------+
| Arnold Shortman  |
| Helga Pataki     |
| Gerald Johanssen |
| Phoebe Heyerdahl |
| Harold Berman    |
| Eugene Horowitz  |
| Rhonda Lloyd     |
| Stinky Peterson  |
+------------------+
8 rows in set (0.00 sec)
</pre>

<p>
	بإدراج فراغ بين علامات الاقتباس الفردية في الاستعلام، يُظهر الخرج الآن أسماء الطلاب بوضوح ككلمتين منفصلتين، بدلًا من كلمة مركبة واحدة.
</p>

<p>
	<strong>ملاحظة</strong>: معظم أنظمة إدارة قواعد البيانات العلاقية الحديثة تستخدم الصياغة الموضحة في هذا القسم لضم القيم. إلّا لأنّ هذه الصياغة (استخدام كلمة <code>CONCAT</code> المفتاحية) ليست الصياغة التقليدية المحددة بمعيار SQL. الطريقة التقليدية لضم القيم في SQL تتمثّل في وضع زوج من الخطوط العمودية بين قيم البيانات التي نريد ضمها. تجدر الملاحظة بأنّ MySQL لا تسمح باستخدام هذه الصياغة على الإطلاق، في حين تسمح بعض أنظمة إدارة قواعد البيانات مثل PostgreSQL باستخدام إحدى الطريقتين. الاستعلام التالي (المُنفذ على قاعدة بيانات PostgreSQL) يُعطي نفس الخرج كالاستعلام السابق، ولكن هذه المرة باستخدام الخطوط العمودية:
</p>

<pre class="ipsCode">SELECT first_name || ' ' || last_name AS full_name 
FROM fall_grades;
</pre>

<pre class="ipsCode">الخرج
    full_name
------------------
 Arnold Shortman
 Helga Pataki
 Gerald Johanssen
 Phoebe Heyerdahl
 Harold Berman
 Eugene Horowitz
 Rhonda Lloyd
 Stinky Peterson
(8 rows)
</pre>

<p>
	الآن، لنجرب مثالاً آخر حيث سنسترجع مزيدًا من المعلومات حول كل طالب. إذ نريد في هذه المرة ضم قيم البيانات من أعمدة الاسم الأول <code>first_name</code> والاسم الأخير <code>last_name</code> وعنوان البريد الإلكتروني <code>email_address</code> ودرجة الامتحان النهائي <code>finalexam_grade</code> ووقت وتاريخ تقديم الامتحان النهائي <code>finalexam_submitted</code> ضمن عمود واحد باستخدام <code>CONCAT</code>. ومن المهم هنا عدم نسيان إضافة علامات اقتباس فردية بين كل عمود وآخر نرغب بإضافة فراغ بينهما كما في المثال التالي:
</p>

<pre class="ipsCode">mysql&gt; SELECT CONCAT(first_name, ' ', last_name, ' ', 
mysql&gt; email_address, ' ', finalexam_grade, ' ', finalexam_submitted) 
mysql&gt; AS student_info FROM fall_grades;
</pre>

<pre class="ipsCode">الخرج
+-----------------------------------------------------------------+
| student_info                                                    |
+-----------------------------------------------------------------+
| Arnold Shortman ashortman@ps118.com 82.5 2022-12-11 03:00:00    |
| Helga Pataki hpataki@ps118.com 90.0 2022-12-11 05:00:00         |
| Gerald Johanssen gjohanssen@ps118.com 88.1 2022-12-11 11:00:00  |
| Phoebe Heyerdahl pheyerdahl@ps118.com 100.0 2022-12-11 11:40:00 |
| Harold Berman hberman@ps118.com 90.9 2022-12-11 09:15:00        |
| Eugene Horowitz ehorowitz@ps118.com 98.2 2022-12-11 07:55:00    |
| Rhonda Lloyd rlloyd@ps118.com 95.5 2022-12-11 06:45:00          |
| Stinky Peterson speterson@ps118.com 73.2 2022-12-11 10:11:00    |
+-----------------------------------------------------------------+
8 rows in set (0.00 sec)
</pre>

<p>
	الأستاذ فريد راضٍ عن هذه النتائج ولكنه يودّ جعلها أكثر إيجازًا ضمن تقرير درجاته وذلك من خلال تحويل بعض قيم البيانات. سنستخدم في هذا السيناريو دالة <code>CAST</code> لتحويل نمط بيانات عمود <code>finalexam_grade</code> إلى رقم صحيح، ونمط بيانات عمود <code>finalexam_submitted</code> من <code>TIMESTAMP</code> إلى نمط بيانات التاريخ <code>date</code> كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5845_24" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT CONCAT</span><span class="pun">(</span><span class="pln">first_name</span><span class="pun">,</span><span class="pln"> </span><span class="str">' '</span><span class="pun">,</span><span class="pln"> last_name</span><span class="pun">,</span><span class="pln"> </span><span class="str">' '</span><span class="pun">,</span><span class="pln"> email_address</span><span class="pun">,</span><span class="pln"> </span><span class="str">' '</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> CAST</span><span class="pun">(</span><span class="pln">finalexam_grade AS </span><span class="kwd">char</span><span class="pun">(</span><span class="lit">2</span><span class="pun">)),</span><span class="pln"> </span><span class="str">' '</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> CAST</span><span class="pun">(</span><span class="pln">finalexam_submitted AS date</span><span class="pun">))</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> AS student_info FROM fall_grades</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-----------------------------------------------------+
| student_info                                        |
+-----------------------------------------------------+
| Arnold Shortman ashortman@ps118.com 82 2022-12-11   |
| Helga Pataki hpataki@ps118.com 90 2022-12-11        |
| Gerald Johanssen gjohanssen@ps118.com 88 2022-12-11 |
| Phoebe Heyerdahl pheyerdahl@ps118.com 10 2022-12-11 |
| Harold Berman hberman@ps118.com 90 2022-12-11       |
| Eugene Horowitz ehorowitz@ps118.com 98 2022-12-11   |
| Rhonda Lloyd rlloyd@ps118.com 95 2022-12-11         |
| Stinky Peterson speterson@ps118.com 73 2022-12-11   |
+-----------------------------------------------------+
8 rows in set, 8 warnings (0.00 sec)
</pre>

<p>
	لنحسّن الخرج أكثر، ونكتب استعلامًا يستخدم دالة <code>CAST</code> وتعبير الضم للحصول على جملة كاملة في الخرج. يمكننا القيام بذلك بكتابة عبارة قصيرة بين علامات الاقتباس الفردية. ولا بدّ من الحفاظ على مسافة بين كل عمود وآخر عن طريق إضافة مسافة واحدة قبل وبعد نهاية العبارة أو العبارات المكتوبة:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5845_26" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT CONCAT</span><span class="pun">(</span><span class="pln">first_name</span><span class="pun">,</span><span class="pln"> </span><span class="str">' '</span><span class="pun">,</span><span class="pln"> last_name</span><span class="pun">,</span><span class="pln"> </span><span class="str">' can be contacted at '</span><span class="pun">,</span><span class="pln"> email_address</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="str">' and received a grade of '</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> CAST</span><span class="pun">(</span><span class="pln">finalexam_grade AS </span><span class="kwd">char</span><span class="pun">(</span><span class="lit">2</span><span class="pun">)),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="str">' after submitting the final exam on '</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> CAST</span><span class="pun">(</span><span class="pln">finalexam_submitted AS date</span><span class="pun">))</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> AS student_info FROM fall_grades</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+------------------------------------------------------------------------------------------------------------------------------------+
| student_info                                                                                                                       |
+------------------------------------------------------------------------------------------------------------------------------------+
| Arnold Shortman can be contacted at ashortman@ps118.com and received a grade of 82 after submitting the final exam on 2022-12-11   |
| Helga Pataki can be contacted at hpataki@ps118.com and received a grade of 90 after submitting the final exam on 2022-12-11        |
| Gerald Johanssen can be contacted at gjohanssen@ps118.com and received a grade of 88 after submitting the final exam on 2022-12-11 |
| Phoebe Heyerdahl can be contacted at pheyerdahl@ps118.com and received a grade of 10 after submitting the final exam on 2022-12-11 |
| Harold Berman can be contacted at hberman@ps118.com and received a grade of 90 after submitting the final exam on 2022-12-11       |
| Eugene Horowitz can be contacted at ehorowitz@ps118.com and received a grade of 98 after submitting the final exam on 2022-12-11   |
| Rhonda Lloyd can be contacted at rlloyd@ps118.com and received a grade of 95 after submitting the final exam on 2022-12-11         |
| Stinky Peterson can be contacted at speterson@ps118.com and received a grade of 73 after submitting the final exam on 2022-12-11   |
+------------------------------------------------------------------------------------------------------------------------------------+
8 rows in set, 8 warnings (0.00 sec)
</pre>

<p>
	يُنتج هذا الخرج جملًا كاملة عن كل طالب في فصل الأستاذ فريد. إذ أحدثت هذه الإضافات الطفيفة بين علامات الاقتباس الفردية فارقًا كبيرًا في جعل المعلومات واضحة من حيث هوية صاحب كل معلومة والبيانات المتعلقة به. وذلك بفضل الأعمدة المحددة التي استخرجنا منها البيانات في استعلامنا. الأستاذ فريد ممتن جدًا لعملك الرائع، وهو راضٍ تمامًا لأنك وفرت عليه الوقت أيضًا بتحويلك البيانات إلى جمل مفهومة يمكنه إضافتها بسهولة إلى تقريره.
</p>

<h2 id="-2">
	الخلاصة
</h2>

<p>
	قدّمنا في هذا المقال شرحًا حول استخدامات متنوعة لمعالجة البيانات باستخدام دالة <code>CAST</code> وتعابير الضم. إذ تدربت على كيفية تحويل قيم عمود من نمط بيانات إلى آخر بفضل الدالة <code>CAST</code>. كما تعلمت كيفية استخدام تعابير الضم لجمع قيم بيانات مختلفة سواء كانت محرفية أو رقمية في سلسلة نصية واحدة. كما نفذت استعلامًا يتضمّن كل من دالة <code>CAST</code> وتعبير الضم معًا، مما أتاح لك إنشاء خرج بجمل كاملة توفر تفسيرًا أشمل للبيانات، الأمر الذي يجعل من الأسهل كتابة الجمل على نحوٍ مستقل عن بعضها، إذ يمكنك تنظيم المعلومات بكفاءة ونسخها ولصقها بالصيغة التي هي عليها مباشرةً.
</p>

<p>
	وللمزيد حول SQL، نشجعك على متابعة <a href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20%D8%AA%D8%B9%D9%84%D9%85%20sql/" rel="">سلسلة تعلم SQL</a> في أكاديمية حسوب.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-manipulate-data-with-cast-functions-and-concatenation-expressions-in-sql" rel="external nofollow">How To Manipulate Data with CAST Functions and Concatenation Expressions in SQL</a> لصاحبه Jeanelle Horcasitas.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%B1%D9%8A%D8%AE-%D9%88%D8%A7%D9%84%D8%A3%D9%88%D9%82%D8%A7%D8%AA-%D9%81%D9%8A-sql-r2411/" rel="">كيفية التعامل مع التواريخ والأوقات في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-sql-r844/" rel="">مدخل إلى SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r855/" rel="">دوال التعامل مع البيانات في SQL</a>
	</li>
	<li>
		 <a href="https://academy.hsoub.com/programming/sql/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%81%D9%8A-sql-r854/" rel="">دوال التعامل مع النصوص في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%AC%D8%AF%D9%88%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-common-table-expressions-%D9%81%D9%8A-sql-r856/" rel="">التعابير الجدولية الشائعة Common Table Expressions في SQL</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2418</guid><pubDate>Thu, 03 Oct 2024 15:03:01 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x639;&#x631;&#x648;&#x636; Views &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x627;&#x644;&#x627;&#x633;&#x62A;&#x639;&#x644;&#x627;&#x645; &#x627;&#x644;&#x628;&#x646;&#x64A;&#x648;&#x64A;&#x629; SQL</title><link>https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%B9%D8%B1%D9%88%D8%B6-views-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2415/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_09/SQL.png.b50719bedfd0b0c6fe491f0df9ca5fa1.png" /></p>
<p>
	تستخدم لغة الاستعلام البنيوية Structured Query Language -أو SQL اختصارًا- مجموعةً متنوعة من هياكل البيانات، وتُعدّ الجداول من أكثرها استخدامًا، ولكن يكون لهذه الجداول قيود أو محدوديات معينة، فمثلًا لا يمكنك تقييد المستخدمين للوصول إلى جزء من الجدول فقط، إذ يجب أن يتمكّن المستخدم من الوصول إلى الجدول بأكمله، وليس إلى بعض الأعمدة ضمنه فقط.
</p>

<p>
	لنفترض مثلًا أنك تريد دمج البيانات من عدة جداول في هيكل بيانات جديد، ولكنك لا تريد حذف الجداول الأصلية، حيث يمكنك إنشاء جدول آخر فقط، ولكن سيكون لديك بيانات زائدة مخزَّنة في أماكن متعددة لاحقًا. قد يسبّب ذلك الكثير من الإزعاج، حيث إذا تغيرت بعض بياناتك، فيجب عليك أن تحدّثها في أماكن متعددة، لذا يمكن أن تكون العروض Views مفيدة في مثل هذه الحالات.
</p>

<p>
	يُعَد العرض view في لغة SQL جدولًا افتراضيًا، وتكون محتوياته نتيجة لاستعلام محدّد لجدول واحد أو أكثر، حيث تُعرَف هذه الجداول باسم الجداول الأساسية Base Tables. يقدّم هذا المقال نظرة عامة حول عروض SQL وفوائدها، ويوضّح كيفية إنشاء العروض والاستعلام عنها وتعديلها وتدميرها باستخدام صيغة SQL المعيارية.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	يجب أن يكون لديك حاسوب يشغّل أحد أنواع أنظمة إدارة قواعد البيانات العلاقية Relational Database Management System -أو RDBMS اختصارًا- التي تستخدم <a href="https://academy.hsoub.com/programming/sql/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-sql-r844/" rel="">لغة SQL</a>. اختبرنا التعليمات والأمثلة الواردة في هذا المقال باستخدام البيئة التالية:
</p>

<ul>
	<li>
		خادم عامل على توزيعة أوبنتو Ubuntu مع مستخدم ذو صلاحيات مسؤول مختلف عن المستخدم الجذر وجدار حماية مضبوط باستخدام أداة UFW كما هو موضح في <a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04" rel="external nofollow">دليل الإعداد الأولي للخادم مع الإصدار 20.04 من أوبنتو</a>، كما يمكنك الاطلاع على مقال <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A9-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-%D9%85%D9%86-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A8%D8%A3%D8%A8%D8%B3%D8%B7-%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-r575/" rel="">كيفية تثبيت توزيعة أوبنتو من لينكس بأبسط طريقة</a>.
	</li>
	<li>
		نظام MySQL مُثبَّت ومؤمَّن على الخادم كما هو موضح في مقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبنتو</a>، وقد نفذنا الخطوات باستخدام مستخدم مُنشَأ وفق الطريقة الموضحة في الخطوة 3 من المقال.
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: تجدر الإشارة إلى أنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية RDBMS لها تقديماتها الفريدة من لغة SQL، إذ ستعمل الأوامر المُقدمة في هذا المقال بنجاح مع معظم هذه الأنظمة، ولكن قد تجد بعض الاختلافات في الصيغة أو الناتج عند اختبارها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<p>
	ستحتاج أيضًا إلى قاعدة بيانات تحتوي على بعض الجداول المُحمَّلة ببيانات تجريبية نموذجية لتتمكن من التدرب على إنشاء العروض والتعامل معها، لذا اطّلع على القسم التالي للحصول على تفاصيل حول كيفية الاتصال بخادم MySQL وإنشاء قاعدة بيانات الاختبار المُستخدَمة في أمثلة هذا المقال.
</p>

<h2 id="mysql">
	الاتصال بخادم MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	إذا كان نظام قاعدة بيانات SQL الخاص بك يعمل على خادم بعيد، فاتصل بالخادم مُستخدمًا بروتوكول <a href="https://academy.hsoub.com/devops/security/ssh/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr></a> من جهازك المحلي كما يلي:
</p>

<pre class="ipsCode">$ ssh user@your_server_ip
</pre>

<p>
	ثم افتح واجهة سطر أوامر خادم MySQL مع وضع اسم حساب مستخدم MySQL الخاص بك مكان <code>user</code>:
</p>

<pre class="ipsCode">$ mysql -u user -p
</pre>

<p>
	أنشِئ قاعدة بيانات باسم <code>views_db</code> في موجّه الأوامر:
</p>

<pre class="ipsCode">mysql&gt; CREATE DATABASE views_db;
</pre>

<p>
	إذا أُنشئِت قاعدة البيانات بنجاح، فسيظهر خرج كما يلي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	يمكنك اختيار قاعدة البيانات <code>views_db</code> من خلال تنفيذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1469_9" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> USE views_db</span><span class="pun">;</span></pre>

<p>
	ويكون الخرج كما يلي:
</p>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	اخترنا قاعدة بيانات <code>views_db</code>، وسننشئ جدولين ضمنها. لنفترض مثلًا أنك تدير خدمة رعاية للكلاب، فقررت استخدام قاعدة بيانات SQL لتخزين معلومات حول كل كلب مُسجَّل في الخدمة، بالإضافة إلى معلومات كل متخصص في رعاية الكلاب توظفه خدمتك، حيث نظّمت هذه المعلومات من خلال إنشاء جدولين: أحدهما يمثل الموظفين والآخر يمثل الكلاب التي تعتني بها خدمتك. سيحتوي الجدول الذي يمثل موظفيك على الأعمدة التالية:
</p>

<ul>
	<li>
		<strong><code>emp_id</code></strong>: رقم تعريف لكل موظف يقدّم رعايةً للكلاب، ونعبّر عنه باستخدام نوع البيانات <code>int</code>، وسيكون هذا العمود هو المفتاح الرئيسي Primary Key للجدول، أي أن كل قيمة ستمثّل معرّفًا فريدًا للصف الخاص بها، وسيكون لهذا العمود أيضًا قيد <code>UNIQUE</code> مطبَّق عليه، إذ يجب أن تكون كل قيمة في المفتاح الرئيسي فريدة.
	</li>
	<li>
		<strong><code>emp_name</code></strong>: اسم الموظف، ونعبّر عنه باستخدام نوع البيانات <code>varchar</code> بحد أقصى 20 محرفًا.
	</li>
</ul>

<p>
	نفّذ تعليمة <code>CREATE TABLE</code> التالية لإنشاء جدول بالاسم <code>employees</code> ويحتوي على العمودين التاليين:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1469_11" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE employees </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> emp_id </span><span class="kwd">int</span><span class="pln"> UNIQUE</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> emp_name varchar</span><span class="pun">(</span><span class="lit">20</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> PRIMARY KEY </span><span class="pun">(</span><span class="pln">emp_id</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	سيحتوي الجدول الآخر الذي يمثل الكلاب على الأعمدة الستة التالية:
</p>

<ul>
	<li>
		<strong><code>dog_id</code></strong>: رقم تعريف لكل كلب ويُعبَّر عنه بنوع البيانات <code>int</code>، وسيكون هذا العمود هو المفتاح الرئيسي للجدول <code>dogs</code> مثل العمود <code>emp_id</code> في الجدول <code>employees</code>.
	</li>
	<li>
		<strong><code>dog_name</code></strong>: اسم الكلب ويُعبَّر عنه باستخدام نوع البيانات <code>varchar</code> بحد أقصى 20 محرفًا.
	</li>
	<li>
		<code>walker</code>: يخزّن هذا العمود رقم معرّف الموظف الذي يرعى كل كلب.
	</li>
	<li>
		<strong><code>walk_distance</code></strong>: المسافة التي يجب أن يمشيها كل كلب عند اصطحابه للتمرين، ويُعبَّر عنه باستخدام نوع البيانات <code>decimal</code>، ويمكن أن تحتوي القيم في هذا العمود على ثلاثة أرقام على الأكثر مع وجود رقمين من هذه الأرقام على يمين الفاصلة العشرية.
	</li>
	<li>
		<strong><code>meals_perday</code></strong>: توفر هذه الخدمة لكل كلب عددًا معينًا من الوجبات كل يوم، حيث يحتوي هذا العمود على عدد الوجبات التي يجب أن يحصل عليها كل كلب يوميًا حسب طلب مالكه، ويستخدم نوع البيانات <code>int</code> فهو عدد صحيح.
	</li>
	<li>
		<strong><code>cups_permeal</code></strong>: يمثل هذا العمود عدد أكواب الطعام التي يجب أن يحصل عليها كل كلب في كل وجبة، ويُعبَّر عنه بنوع البيانات <code>decimal</code> مثل العمود <code>walk_distance</code>، ويمكن أن تحتوي القيم في هذا العمود على ما يصل إلى ثلاثة أرقام مع وجود رقمين من هذه الأرقام على يمين الفاصلة العشرية.
	</li>
</ul>

<p>
	نتأكد من أن العمود <code>walker</code> يحتوي على القيم التي تمثل أرقام معرّف الموظف الصالحة فقط من خلال تطبيق <a href="https://academy.hsoub.com/programming/sql/%d9%81%d9%87%d9%85-%d9%82%d9%8a%d9%88%d8%af-sql-r2217/" rel="">قيد مفتاح خارجي Foreign Key</a> على العمود <code>walker</code> الذي يشير إلى العمود <code>emp_ID</code> الخاص بالجدول <code>employees</code>. يُعَد قيد المفتاح الخارجي طريقة للتعبير عن العلاقة بين جدولين من خلال اشتراط أن تكون القيم الموجودة في العمود الذي طُبِّق المفتاح الخارجي عليه موجودة في العمود الذي يشير إليه، حيث يشترط قيد <code>FOREIGN KEY</code> في المثال التالي أن تكون أيّ قيمة مضافة إلى العمود <code>walker</code> في الجدول <code>dogs</code> موجودةً في العمود <code>emp_ID</code> الخاص بالجدول <code>employees</code>. لننشئ جدولًا بالاسم <code>dogs</code> يحتوي على هذه الأعمدة باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1469_13" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE dogs </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> dog_id </span><span class="kwd">int</span><span class="pln"> UNIQUE</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> dog_name varchar</span><span class="pun">(</span><span class="lit">20</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> walker </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> walk_distance </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="lit">2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> meals_perday </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> cups_permeal </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="lit">2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> PRIMARY KEY </span><span class="pun">(</span><span class="pln">dog_id</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> FOREIGN KEY </span><span class="pun">(</span><span class="pln">walker</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> REFERENCES employees</span><span class="pun">(</span><span class="pln">emp_ID</span><span class="pun">)</span><span class="pln">
</span><span class="pun">);</span></pre>

<p>
	يمكنك الآن تحميل الجدولين ببعض البيانات التجريبية النموذجية. نفّذ عملية الإدخال <code><a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D8%AF%D8%B1%D8%A7%D8%AC-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r2226/" rel="">INSERT INTO</a></code> التالية لإضافة ثلاثة صفوف من البيانات تمثّل ثلاثة من موظفي الخدمة إلى الجدول <code>employees</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1469_15" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO employees
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Peter'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Paul'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Mary'</span><span class="pun">);</span></pre>

<p>
	ثم نفّذ العملية التالية لإدخال سبعة صفوف من البيانات في الجدول <code>dogs</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1469_17" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO dogs
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Dottie'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Bronx'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6.5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1.25</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Harlem'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1.25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.25</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Link'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2.75</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.75</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Otto'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4.5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Juno'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4.5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Zephyr'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1.5</span><span class="pun">);</span></pre>

<p>
	وأصبحتَ الآن جاهزًا لمتابعة بقية هذا المقال والبدء في تعلّم كيفية استخدام العروض في لغة SQL.
</p>

<h2 id="views">
	فهم وإنشاء العروض Views
</h2>

<p>
	يمكن أن تصبح استعلامات SQL معقدة، ولكن إحدى الفوائد الرئيسية للغة SQL هي أنها تتضمن العديد من الخيارات والتعليمات التي تسمح لك بترشيح بياناتك بمستوى عالٍ من الدقة والتحديد. إذا كانت لديك استعلامات معقدة تريد تشغيلها بصورة متكررة، فقد يصبح الاضطرار إلى كتابتها باستمرار أمرًا مملًا، وإحدى الطرق لحل هذه المشكلات هي استخدام العروض.
</p>

<p>
	تُعَد العروض views جداولًا افتراضية كما ذكرنا سابقًا، وهذا يعني أنها تشبه الجداول وظيفيًا، ولكنها تمثّل نوعًا مختلفًا من هياكل البيانات لأن العرض لا يحتوي على أيّ بيانات خاصة به، فهو يسحب البيانات من جدول أساسي واحد أو أكثر، وتكون المعلومات الوحيدة حول العرض التي سيخزنها نظام <a href="https://academy.hsoub.com/devops/servers/databases/%D9%85%D8%AD%D8%B1%D9%83%D8%A7%D8%AA-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/" rel="">إدارة قواعد البيانات </a>DBMS هي هيكل العرض. تُسمَّى العروض أحيانًا بالاستعلامات المحفوظة Saved Queries، لأنها تمثّل الاستعلامات المحفوظة باسم مُحدَّد لتسهيل الوصول إليها.
</p>

<p>
	ليكن لدينا المثال التالي، تخيل أن أعمال رعاية الكلاب الخاصة بك ناجحة وتريد طباعة جدول يومي لجميع موظفيك، إذ يجب أن يحتوي هذا الجدول على كل كلب ترعاه الخدمة، والموظف المكلف برعايته، والمسافة التي يجب أن يمشيها كل كلب يوميًا، وعدد الوجبات التي يجب إطعامها لكل كلب يوميًا، وكمية الطعام التي ينبغي أن يحصل كل كلب في كل وجبة.
</p>

<p>
	استخدم مهاراتك في لغة SQL لإنشاء <a href="https://wiki.hsoub.com/SQL/select" rel="external">استعلام</a> مع بياناتٍ تجريبية نموذجية من الخطوة السابقة لاسترجاع جميع هذه المعلومات للجدول، ولاحظ أن هذا الاستعلام يتضمن صيغة <code><a href="https://wiki.hsoub.com/SQL/join" rel="external">JOIN</a></code> لسحب البيانات من الجدولين <code>employees</code> و <code>dogs</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1469_19" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT emp_name</span><span class="pun">,</span><span class="pln"> dog_name</span><span class="pun">,</span><span class="pln"> walk_distance</span><span class="pun">,</span><span class="pln"> meals_perday</span><span class="pun">,</span><span class="pln"> cups_permeal
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM employees JOIN dogs ON emp_ID </span><span class="pun">=</span><span class="pln"> walker</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+----------+----------+---------------+--------------+--------------+
| emp_name | dog_name | walk_distance | meals_perday | cups_permeal |
+----------+----------+---------------+--------------+--------------+
| Peter    | Dottie   |          5.00 |            3 |         1.00 |
| Peter    | Otto     |          4.50 |            3 |         2.00 |
| Peter    | Juno     |          4.50 |            3 |         2.00 |
| Paul     | Link     |          2.75 |            2 |         0.75 |
| Mary     | Bronx    |          6.50 |            3 |         1.25 |
| Mary     | Harlem   |          1.25 |            2 |         0.25 |
| Mary     | Zephyr   |          3.00 |            2 |         1.50 |
+----------+----------+---------------+--------------+--------------+
7 rows in set (0.00 sec)
</pre>

<p>
	لنفترض أنه يجب تنفيذ هذا الاستعلام بشكل متكرر، إذ قد يصبح أمرًا مملًا إذا اضطررت إلى كتابة الاستعلام مرارًا وتكرارًا، وخاصةً عند إجراء تعليمات استعلام أطول وأكثر تعقيدًا، وإذا أردتَ إجراء تعديلات طفيفة على الاستعلام أو التوسّع فيه، فقد يكون الأمر مملًا عند استكشاف الأخطاء وإصلاحها مع وجود العديد من الاحتمالات للأخطاء الصياغية. يمكن أن يكون العرض مفيدًا في مثل هذه الحالات، لأنه يُعَد جدولًا مشتقًا من نتائج الاستعلام.
</p>

<p>
	تستخدم معظم أنظمة RDBMS الصيغة التالية لإنشاء العروض:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1469_21" style=""><span class="pln">CREATE VIEW view_name
AS
SELECT statement</span><span class="pun">;</span></pre>

<p>
	يمكنك بعد تعليمة <code><a href="https://wiki.hsoub.com/SQL/create_view" rel="external">CREATE VIEW</a></code> اختيار اسمٍ للعرض الذي ستستخدمه للإشارة إليه لاحقًا، ثم تدخِل الكلمة المفتاحية <code>AS</code>، ثم تضع استعلام <code>SELECT</code> الذي تريد حفظ خرجه. يمكن أن يكون الاستعلام الذي تستخدمه لإنشاء عرضك أيّ تعليمة <code>SELECT</code> صالحة، ويمكن أن تستعلم التعليمة التي تضمّنها عن جدول أساسي واحد أو أكثر طالما أنك تستخدم الصيغة الصحيحة.
</p>

<p>
	جرّب إنشاء عرض باستخدام استعلام المثال السابق، حيث تسمّي عملية <code>CREATE VIEW</code> العرض بالاسم <code>walking_schedule</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1469_23" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE VIEW walking_schedule
mysql</span><span class="pun">&gt;</span><span class="pln"> AS
mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT emp_name</span><span class="pun">,</span><span class="pln"> dog_name</span><span class="pun">,</span><span class="pln"> walk_distance</span><span class="pun">,</span><span class="pln"> meals_perday</span><span class="pun">,</span><span class="pln"> cups_permeal
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM employees JOIN dogs
mysql</span><span class="pun">&gt;</span><span class="pln"> ON emp_ID </span><span class="pun">=</span><span class="pln"> walker</span><span class="pun">;</span></pre>

<p>
	ستتمكّن بعد ذلك من استخدام هذا العرض والتفاعل معه كما تفعل مع أيّ جدول آخر، فمثلًا يمكنك تنفيذ الاستعلام التالي لإعادة جميع البيانات الموجودة في العرض:
</p>

<pre class="ipsCode">mysql&gt; SELECT * FROM walking_schedule;
</pre>

<pre class="ipsCode">الخرج
+----------+----------+---------------+--------------+--------------+
| emp_name | dog_name | walk_distance | meals_perday | cups_permeal |
+----------+----------+---------------+--------------+--------------+
| Peter    | Dottie   |          5.00 |            3 |         1.00 |
| Peter    | Otto     |          4.50 |            3 |         2.00 |
| Peter    | Juno     |          4.50 |            3 |         2.00 |
| Paul     | Link     |          2.75 |            2 |         0.75 |
| Mary     | Bronx    |          6.50 |            3 |         1.25 |
| Mary     | Harlem   |          1.25 |            2 |         0.25 |
| Mary     | Zephyr   |          3.00 |            2 |         1.50 |
+----------+----------+---------------+--------------+--------------+
7 rows in set (0.00 sec)
</pre>

<p>
	يُعَد هذا العرض مشتقًا من جدولين آخرين، ولكنك لن تتمكّن من الاستعلام عن العرض لأيّ بيانات من هذين الجدولين إن لم تكن موجودة مسبقًا في هذا العرض. يحاول الاستعلام التالي استرجاع العمود <code>walker</code> من العرض <code>walking_schedule</code>، ولكن سيفشل هذا الاستعلام لأن العرض لا يحتوي على أيّ أعمدة بهذا الاسم:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1469_28" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT walker FROM walking_schedule</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
ERROR 1054 (42S22): Unknown column 'walker' in 'field list'
</pre>

<p>
	يعيد هذا الخرج رسالة خطأ لأن العمود <code>walker</code> هو جزء من الجدول <code>dogs</code>، ولكنه غير مُضمَّنٍ في العرض الذي أنشأناه.
</p>

<p>
	يمكنك أيضًا تنفيذ الاستعلامات التي تتضمّن دوالًا تجميعية <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%AC%D9%85%D9%8A%D8%B9%D9%8A%D8%A9-%D9%81%D9%8A-sql-r2410/" rel="">Aggregate Functions</a> تعالج البيانات ضمن العرض، يستخدم المثال التالي الدالة التجميعية <code>MAX</code> مع عبارة <code><a href="https://wiki.hsoub.com/SQL/group_by" rel="external">GROUP BY</a></code> للعثور على أطول مسافة يجب على الموظف أن يمشيها في يوم محدّد:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1469_30" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT emp_name</span><span class="pun">,</span><span class="pln"> MAX</span><span class="pun">(</span><span class="pln">walk_distance</span><span class="pun">)</span><span class="pln"> AS longest_walks
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM walking_schedule GROUP BY emp_name</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+----------+---------------+
| emp_name | longest_walks |
+----------+---------------+
| Peter    |          5.00 |
| Paul     |          2.75 |
| Mary     |          6.50 |
+----------+---------------+
3 rows in set (0.00 sec)
</pre>

<p>
	توجد فائدة أخرى للعروض كما ذكرنا سابقًا، وهي أنه يمكنك استخدامها لتقييد وصول مستخدم قاعدة البيانات إلى العرض فقط بدلًا من الوصول إلى الجدول أو قاعدة البيانات بأكملها. لنفترض مثلًا أنك وظّفتَ مدير مكتب لمساعدتك في إدارة الجدول الزمني، وتريد أن يصل إلى معلومات الجدول دون الوصول لأي بيانات أخرى في قاعدة البيانات، حيث يمكنك إنشاء حساب مستخدم جديد له في قاعدة بياناتك كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4011_10" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE USER </span><span class="str">'office_mgr'</span><span class="pun">@</span><span class="str">'localhost'</span><span class="pln"> IDENTIFIED BY </span><span class="str">'password'</span><span class="pun">;</span></pre>

<p>
	يمكنك بعد ذلك منح هذا المستخدم الجديد صلاحية وصول للقراءة إلى العرض <code>walking_schedule</code> فقط باستخدام <a href="https://wiki.hsoub.com/SQL/grant" rel="external">التعليمة </a><code>GRANT</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4458_12" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> GRANT SELECT ON views_db</span><span class="pun">.</span><span class="pln">walking_schedule to </span><span class="str">'office_mgr'</span><span class="pun">@</span><span class="str">'localhost'</span><span class="pun">;</span></pre>

<p>
	وبالتالي سيتمكّن الشخص الذي لديه صلاحية الوصول إلى حساب مستخدم MySQL الذي هو <code>office_mgr</code> من تنفيذ استعلامات <code>SELECT</code> في العرض <code>walking_schedule</code> فقط.
</p>

<h2 id="views-1">
	تغيير وحذف العروض Views
</h2>

<p>
	إذا أضفتَ أو غيّرتَ بياناتٍ في أحد الجداول التي نشتق العرض منها، فستُضاف أو تُحدَّث البيانات ذات الصلة في العرض تلقائيًا. نفّذ الأمر <code><a href="https://wiki.hsoub.com/SQL/insert" rel="external">INSERT INTO</a></code> التالي لإضافة صف آخر إلى الجدول <code>dogs</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4011_14" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO dogs VALUES </span><span class="pun">(</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Charlie'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3.5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span></pre>

<p>
	يمكنك بعد ذلك استرجاع جميع البيانات من العرض <code>walking_schedule</code> مرة أخرى كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4011_12" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM walking_schedule</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+----------+----------+---------------+--------------+--------------+
| emp_name | dog_name | walk_distance | meals_perday | cups_permeal |
+----------+----------+---------------+--------------+--------------+
| Peter    | Dottie   |          5.00 |            3 |         1.00 |
| Peter    | Otto     |          4.50 |            3 |         2.00 |
| Peter    | Juno     |          4.50 |            3 |         2.00 |
| Paul     | Link     |          2.75 |            2 |         0.75 |
| Paul     | Charlie  |          3.50 |            3 |         1.00 |
| Mary     | Bronx    |          6.50 |            3 |         1.25 |
| Mary     | Harlem   |          1.25 |            2 |         0.25 |
| Mary     | Zephyr   |          3.00 |            2 |         1.50 |
+----------+----------+---------------+--------------+--------------+
8 rows in set (0.00 sec)
</pre>

<p>
	لاحظ وجود صف آخر في مجموعة نتائج الاستعلام، والذي يمثّل البيانات التي أضفتها إلى الجدول <code>dogs</code>، ولكن لا يزال العرض يسحب البيانات ذاتها من الجداول الأساسية نفسها، لذلك لم تغيّر هذه العملية العرض.
</p>

<p>
	تسمح لك العديد من أنظمة RDBMS بتحديث هيكل العرض بعد إنشائه باستخدام صيغة <code>CREATE OR REPLACE VIEW</code>:
</p>

<pre class="ipsCode">mysql&gt; CREATE OR REPLACE VIEW view_name
mysql&gt; AS
mysql&gt; new SELECT statement
</pre>

<p>
	إذا كان العرض الذي اسمه <code>view_name</code> موجودًا مسبقًا في هذه الصيغة، فسيحدّث نظام قاعدة البيانات هذا العرض بحيث يمثّل البيانات التي تعيدها التعليمة <code>new SELECT statement</code>. إذا لم يكن العرض بهذا الاسم موجودًا، فسينشئ نظام إدارة قواعد البيانات DBMS عرضًا جديدًا.
</p>

<p>
	لنفترض أنك تريد تغيير العرض <code>walking_schedule</code> ليسرد إجمالي كمية الطعام التي تناولها كل كلب على مدار اليوم بدلًا من سرد عدد أكواب الطعام التي يتناولها كل كلب في كل وجبة، ويمكنك تغيير العرض باستخدام الأمر التالي:
</p>

<pre class="ipsCode">mysql&gt; CREATE OR REPLACE VIEW walking_schedule
mysql&gt; AS
mysql&gt; SELECT emp_name, dog_name, walk_distance, meals_perday, (cups_permeal * mysql&gt; meals_perday) AS total_kibble
mysql&gt; FROM employees JOIN dogs ON emp_ID = walker;
</pre>

<p>
	إذا أجربتَ الآن استعلامًا على هذا العرض، فستمثّل مجموعة النتائج بيانات العرض الجديدة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4011_19" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM walking_schedule</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+----------+----------+---------------+--------------+--------------+
| emp_name | dog_name | walk_distance | meals_perday | total_kibble |
+----------+----------+---------------+--------------+--------------+
| Peter    | Dottie   |          5.00 |            3 |         3.00 |
| Peter    | Otto     |          4.50 |            3 |         6.00 |
| Peter    | Juno     |          4.50 |            3 |         6.00 |
| Paul     | Link     |          2.75 |            2 |         1.50 |
| Paul     | Charlie  |          3.50 |            3 |         3.00 |
| Mary     | Bronx    |          6.50 |            3 |         3.75 |
| Mary     | Harlem   |          1.25 |            2 |         0.50 |
| Mary     | Zephyr   |          3.00 |            2 |         3.00 |
+----------+----------+---------------+--------------+--------------+
8 rows in set (0.00 sec)
</pre>

<p>
	يمكنك حذف العروض باستخدام صيغة <a href="https://wiki.hsoub.com/SQL/drop_view" rel="external">DROP </a>مثل معظم هياكل البيانات الأخرى التي يمكنك إنشاؤها في لغة SQL، وإليك مثالًا:
</p>

<pre class="ipsCode">DROP VIEW view_name;
</pre>

<p>
	يمكنك مثلًا حذف العرض <code>walking_schedule</code> باستخدام الأمر التالي:
</p>

<pre class="ipsCode">mysql&gt; DROP VIEW walking_schedule;
</pre>

<p>
	يؤدي الأمر السابق إلى إزالة العرض <code>walking_schedule</code> من قاعدة بياناتك، ولكنه لن يحذف أيًّا من بيانات قاعدة بياناتك المتعلقة بالعرض إلّا إذا أزلتها من الجداول الأساسية.
</p>

<h2 id="-1">
	الخلاصة
</h2>

<p>
	تعلّمنا في هذا المقال ما هي عروض SQL وكيفية إنشائها والاستعلام عنها وتغييرها وحذفها من قاعدة البيانات، وتعرّفنا على فوائد العروض، وأنشأنا مستخدم MySQL الذي يمكنه فقط قراءة البيانات من العرض التجريبي الذي أنشأناه.
</p>

<p>
	يجب أن تعمل الأوامر الواردة في أمثلة هذا المقال على معظم قواعد البيانات العلاقية، ولكن يجب أن تدرك أن كل قاعدة بيانات SQL لها تقديمها الفريد من هذه اللغة، لذا يجب عليك الرجوع إلى التوثيق الرسمي لنظام إدارة قواعد البيانات DBMS الخاص بك للحصول على وصف أشمل لكل أمر ومجموعته الكاملة من الخيارات.
</p>

<p>
	ننصحك بالاطلاع على <a href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20%D8%AA%D8%B9%D9%84%D9%85%20sql/" rel="">سلسلة تعلم SQL</a> في أكاديمية حسوب للمزيد حول كيفية التعامل مع لغة SQL.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-views-in-sql" rel="external nofollow">How To Use Views in SQL</a> لصاحبه Mark Drake.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%B9%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r588/" rel="">الاستعلام عن البيانات في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%AC%D9%84%D8%A8-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85%D8%A7%D8%AA-%D8%B9%D8%A8%D8%B1-select-%D9%81%D9%8A-sql-r845/" rel="">جلب الاستعلامات عبر SELECT في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D9%85%D8%AA%D9%82%D8%AF%D9%85-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-sql-r961/" rel="">المرجع المتقدم إلى لغة SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%84%D8%BA%D8%A9-%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-dml-%D8%A7%D9%84%D8%AE%D8%A7%D8%B5%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-sql-r1369/" rel="">لغة معالجة البيانات DML الخاصة بلغة SQL</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2415</guid><pubDate>Sat, 28 Sep 2024 15:09:01 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x62A;&#x648;&#x627;&#x631;&#x64A;&#x62E; &#x648;&#x627;&#x644;&#x623;&#x648;&#x642;&#x627;&#x62A; &#x641;&#x64A; SQL</title><link>https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%B1%D9%8A%D8%AE-%D9%88%D8%A7%D9%84%D8%A3%D9%88%D9%82%D8%A7%D8%AA-%D9%81%D9%8A-sql-r2411/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_09/------SQL.png.dc9c1ada22a6857a69422090a6fcabf4.png" /></p>
<p>
	قد تضطر في بعض الأحيان إلى التعامل مع قيم تمثل تواريخ أو أوقات محددة لدى العمل مع قواعد البيانات العلاقية ولغة الاستعلام البنيوية SQL. فعلى سبيل المثال، قد ترغب في حساب إجمالي الساعات المُستغرقة في أداء نشاطٍ ما، أو قد تحتاج إلى مُعالجة قيم التواريخ أو الأوقات باستخدام المعاملات الرياضية ودوال التجميع لحساب مجموعها أو متوسطها.
</p>

<p>
	ستتعلم في هذا المقال كيفية استخدام التواريخ والأوقات في SQL. إذ ستبدأ بإجراء العمليات الحسابية واستخدام دوال متنوعة مع التواريخ والأوقات باستخدام تعليمة <code>SELECT</code> وحدها. لتتدرب بعد ذلك على تنفيذ استعلامات على بياناتٍ نموذجية تجريبية، وستتعلم كيفية استخدام دالة <code>CAST</code> لجعل النتائج أيسر للقراءة.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	لمتابعة الخطوات في هذا المقال، ستحتاج إلى:
</p>

<ul>
	<li>
		خادم عامل على توزيعة أوبنتو، مع مستخدم ذو صلاحيات مسؤول من نوع <code>sudo</code> مختلف عن المستخدم <strong>الجذر</strong>، وجدار حماية مُفعّل، كما هو موضح المقال <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A9-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-%D9%85%D9%86-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A8%D8%A3%D8%A8%D8%B3%D8%B7-%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-r575/" rel="">كيفية تثبيت توزيعة أوبنتو من لينكس بأبسط طريقة</a>.
	</li>
	<li>
		MySQL مثبتة ومؤمنة على الخادم، كما هو موضح في المقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبونتو</a>. وقد نفذنا خطوات هذا المقال باستخدام مستخدم MySQL مختلف عن المستخدم الجذر، مُنشأ وفق الطريقة الموضحة في الخطوة 3 من هذا المقال.
	</li>
</ul>

<p>
	<strong>ملاحظة:</strong> تجدر الإشارة إلى أنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية لها تقديماتها الفريدة من لغة <a href="https://academy.hsoub.com/programming/sql/" rel="">SQL</a>. فبالرغم من كون الأوامر المُقدمة في هذا المقال ستعمل مع معظم هذه الأنظمة، ولكن قد تجد بعض الاختلافات في الصيغة أو الناتج عند تنفيذها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<p>
	وبالعودة إلى مستلزمات العمل، ستحتاج أيضًا إلى قاعدة بيانات وجدول مُحمّل ببعض البيانات التجريبية النموذجية لتتمكن من التدرب على الأمثلة حول كيفية استخدام التاريخ والوقت في هذا المقال. لذا ننصحك بمتابعة القسم القادم <strong>الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية</strong> للمزيد من التفاصيل حول كيفية إعداد قاعدة بيانات وجدول لاستخدامهما في الأمثلة خلال هذا المقال.
</p>

<h2 id="mysql">
	الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	إذا كان نظام قاعدة بيانات SQL الخاص بك يعمل على خادم عن بُعد، اتصل بالخادم مُستخدمًا بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> من جهازك المحلي على النحو التالي:
</p>

<pre class="ipsCode">$ ssh ssh user@your_server_ip
</pre>

<p>
	ثم افتح واجهة سطر الأوامر في خادم MySQL، مُستبدلًا <code>user</code> باسم حساب مستخدم MySQL الخاص بك:
</p>

<pre class="ipsCode">$ mysql -u user -p
</pre>

<p>
	أنشئ قاعدة بيانات باسم <code>datetimeDB</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_6" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE DATABASE datetimeDB</span><span class="pun">;</span></pre>

<p>
	وبمجرّد إنشاء قاعدة البيانات بنجاح ستحصل على خرجٍ كالتالي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	ولاختيار قاعدة البيانات <code>datetimeDB</code>، نفّذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_8" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> USE datetimeDB</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	الآن وبعد اختيار قاعدة البيانات <code>datetimeDB</code>، لننشئ جدولًا ضمنها.
</p>

<p>
	كمثال في مقالنا هذا، سننشئ جدولًا يُسجّل نتائج اثنين من العدائين في مختلف السباقات التي شاركا بها على مدار عام. سيضم الجدول الأعمدة السبعة التالية:
</p>

<ul>
	<li>
		<code>race_id</code>: يُمثّل قيمًا من نمط بيانات الأعداد الصحيحة <code>int</code> وسيكون المفتاح الأساسي للجدول، ما يعني أن كل قيمة في هذا العمود ستلعب دور المعرّف الفريد لسجلها.
	</li>
	<li>
		<code>runner_name</code>: مُخصص لأسماء هذين العدّائين وهما في مثالنا أحمد ومحمد وذلك باستخدام نمط البيانات <code>varchar</code> بحد أقصى 30 محرفًا.
	</li>
	<li>
		<code>race_name</code>: يُخزّن أسماء السباقات باستخدام نمط البيانات <code>varchar</code> بحد أقصى 20 محرفًا.
	</li>
	<li>
		<code>start_day</code>: لتسجيل تاريخ السباق باليوم والشهر والسنة باستخدام نمط البيانات <code>DATE</code>. إذ يتبع نمط البيانات هذا الصيغة القياسية التالية: أربعة أرقام للسنة، وحد أقصى من رقمين لكل من الشهر واليوم (<code>YYYY-MM-DD</code>).
	</li>
	<li>
		<code>start_time</code>: يُمثل وقت بداية السباق باستخدام نمط بيانات <code>TIME</code> بالساعات والدقائق والثواني (<code>HH:MM:SS</code>). ويستخدم نمط البيانات هذا الوقت بصيغة 24 ساعة، أي <code>15:00</code> على سبيل المثال للتعبير عن الساعة 3:00 مساءً.
	</li>
	<li>
		<code>total_miles</code>: يعرض المسافة الإجمالية لكل سباق باستخدام نمط بيانات <code>decimal</code>، وذلك نظرًا لأن العديد من المسافات الإجمالية للسباقات ليست بأرقامٍ صحيحة. ويُمكّننا نمط البيانات <code>decimal</code> من تمثيل المسافات بدقة تصل إلى ثلاثة أرقام ككل، منها رقم واحد إلى يمين الفاصلة العشرية، أي بدقة تصل إلى عُشر الميل.
	</li>
	<li>
		<code>end_time</code>: يُسجّل وقت انتهاء كل عدّاء من السباق باستخدام نمط بيانات <code>TIMESTAMP</code>، الذي يدمج التاريخ والوقت ضمن تنسيق واحد يجمع بين صيغتي <code>DATE</code> و<code>TIME</code> مُشتملًا على السنة والشهر واليوم متبوعًا بالساعة والدقيقة والثانية، وهو يعتمد في ذلك على الصيغة<br>
		القياسية (YYYY-MM-DD HH:MM:SS) لتقديم توقيت دقيق للحظة انتهاء السباق.
	</li>
</ul>

<p>
	ولإنشاء هذا الجدول، لننفّذ الأمر <code>CREATE TABLE</code> التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_10" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE race_results </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> race_id </span><span class="kwd">int</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> runner_name varchar</span><span class="pun">(</span><span class="lit">30</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> race_name varchar</span><span class="pun">(</span><span class="lit">20</span><span class="pun">),</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> start_day DATE</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> start_time TIME</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> total_miles </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> end_time TIMESTAMP</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> PRIMARY KEY </span><span class="pun">(</span><span class="pln">race_id</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	ثم املأ هذا الجدول الفارغ ببعض البيانات التجريبية النموذجية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_12" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO race_results
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">race_id</span><span class="pun">,</span><span class="pln"> runner_name</span><span class="pun">,</span><span class="pln"> race_name</span><span class="pun">,</span><span class="pln"> start_day</span><span class="pun">,</span><span class="pln"> start_time</span><span class="pun">,</span><span class="pln"> total_miles</span><span class="pun">,</span><span class="pln"> end_time</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Ahmad'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1600_meters'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-09-18'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'7:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1.0</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-09-18 7:06:30'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Ahmad'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'5K'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-19'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'11:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3.1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-19 11:22:31'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Ahmad'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'10K'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'10:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6.2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20 10:38:05'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Ahmad'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'half_marathon'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-21'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'6:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">13.1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-21 07:39:04'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Ahmad'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'full_marathon'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-01-22'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'8:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">26.2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-01-22 11:23:10'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Mohammad'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1600_meters'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-09-18'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'7:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1.0</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-09-18 7:07:15'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Mohammad'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'5K'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-19'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'11:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3.1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-10-19 11:30:50'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Mohammad'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'10K'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'10:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6.2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-11-20 11:10:17'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Mohammad'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'half_marathon'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-21'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'6:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">13.1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2022-12-21 08:11:57'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Mohammad'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'full_marathon'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-01-22'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'8:00:00'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">26.2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2023-01-22 12:02:10'</span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
Query OK, 10 rows affected (0.00 sec)
Records: 10  Duplicates: 0  Warnings: 0
</pre>

<p>
	وبمجرّد إدخالك للبيانات تغدو مستعدًا لبدء التدرّب على استخدام بعض العمليات الحسابية والدوال مع التواريخ في SQL.
</p>

<h2 id="-1">
	استخدام العمليات الحسابية مع التواريخ والأوقات
</h2>

<p>
	من الممكن معالجة قيم التواريخ والأوقات في <a href="https://wiki.hsoub.com/SQL" rel="external">SQL</a> باستخدام التعابير الرياضية، ولن تحتاج سوى إلى العامل الرياضي والقيم المطلوب حسابها.
</p>

<p>
	كمثال، لو أردنا تحديد تاريخ يأتي بعد عدد محدد من الأيام من تاريخٍ آخر. يأخذ الاستعلام التالي قيمة تاريخ معينة (<code>2022-10-05</code>) ويضيف إليها 17، لإعادة قيمة التاريخ الذي يأتي بعد سبعة عشر يومًا من التاريخ المحدد في الاستعلام. وتجدر الملاحظة إلى أننا حددنا <code>2022-10-05</code> هنا كقيمة من نوع <code>DATE</code> لضمان عدم تفسير نظام إدارة قاعدة البيانات لها كنص أو أي نوع بيانات آخر:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_14" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT DATE </span><span class="str">'2022-10-05'</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">17</span><span class="pln"> AS new_date</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+----------+
| new_date |
+----------+
| 20221022 |
+----------+
1 row in set (0.01 sec)
</pre>

<p>
	نلاحظ من هذا الخرج أنّ اليوم الذي يأتي بعد سبعة عشر يومًا من تاريخ <code>2022-10-05</code> هو <code>2022-10-22</code>، أو 22 أكتوبر 2022.
</p>

<p>
	كمثال آخر، بفرض أننا نريد حساب إجمالي الساعات بين وقتين مختلفين من خلال طرحهما من بعضهما البعض. افترضنا في الاستعلام التالي أنّ <code>11:00</code> هو الوقت الأول و<code>3:00</code> هو الوقت الثاني. ولا بدّ في هذه الحالة من تحديد أن كلاهما من نمط البيانات <code>TIME</code> للحصول على الفرق بالساعات:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_16" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT TIME </span><span class="str">'11:00'</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> TIME </span><span class="str">'3:00'</span><span class="pln"> AS time_diff</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-----------+
| time_diff |
+-----------+
|     80000 |
+-----------+
1 row in set (0.00 sec)
</pre>

<p>
	يُظهر هذا الخرج أن الفارق بين الساعة <code>11:00</code> والساعة <code>3:00</code> هو 80000، أو 8 ساعات.
</p>

<p>
	الآن سنطبّق استعلامات على بياناتنا النموذجية بهدف التدرّب على كيفية استخدام العمليات الحسابية على معلومات التاريخ والوقت. بالنسبة للاستعلام الأول، سنحسب الزمن الكلي الذي استغرقه العداءان لإنهاء كل سباق من خلال طرح زمن الانتهاء <code>end_time</code> من زمن البدء <code>start_time</code>، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_20" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT runner_name</span><span class="pun">,</span><span class="pln"> race_name</span><span class="pun">,</span><span class="pln"> end_time </span><span class="pun">-</span><span class="pln"> start_time
mysql</span><span class="pun">&gt;</span><span class="pln"> AS total_time
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM race_results</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------+---------------+----------------+
| runner_name | race_name     | total_time    |
+-------------+---------------+----------------+
| Ahmad        | 1600_meters   | 20220918000630 |
| Ahmad        | 5K            | 20221019002231 |
| Ahmad        | 10K           | 20221120003805 |
| Ahmad        | half_marathon | 20221221013904 |
| Ahmad        | full_marathon | 20230122032310 |
| Mohammad       | 1600_meters   | 20220918000715 |
| Mohammad       | 5K            | 20221019003050 |
| Mohammad       | 10K           | 20221120011017 |
| Mohammad       | half_marathon | 20221221021157 |
| Mohammad       | full_marathon | 20230122040210 |
+-------------+---------------+----------------+
10 rows in set (0.00 sec)
</pre>

<p>
	ستلاحظ أن القيم المُعادة في عمود <code>total_time</code> تظهر على نحوٍ مطوّل وقد تصعب قراءتها. لذا سنشرح في قسمٍ لاحق ضمن هذا المقال كيفية استخدام دالة <code>CAST</code> لتنسيق هذه القيم بطريقة تجعلها أوضح وأسهل للقراءة.
</p>

<p>
	بفرض أنّ تركيزنا منصب على أداء العدّائين في السباقات ذات المسافات الأطول، من قبيل سباقات نصف الماراثون والماراثون الكامل، سنستخدم استعلامًا لاسترجاع هذه البيانات على وجه الخصوص. إذ سنطرح زمن الانتهاء <code>end_time</code> من زمن البدء <code>start_time</code> مُستخدمين بنية <code>WHERE</code> للتحديد الدقيق للنتائج، لتشمل فقط السباقات التي تزيد مسافتها في العمود <code>total_miles</code> عن 12 ميل:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_22" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT runner_name</span><span class="pun">,</span><span class="pln"> race_name</span><span class="pun">,</span><span class="pln"> end_time </span><span class="pun">-</span><span class="pln"> start_time AS half_full_results
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM race_results 
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE total_miles </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">12</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------+---------------+-------------------+
| runner_name | race_name     | half_full_results |
+-------------+---------------+-------------------+
| Ahmad        | half_marathon |    20221221013904 |
| Ahmad        | full_marathon |    20230122032310 |
| Mohammad       | half_marathon |    20221221021157 |
| Mohammad       | full_marathon |    20230122040210 |
+-------------+---------------+-------------------+
4 rows in set (0.00 sec)
</pre>

<p>
	أجرينا في هذا القسم بعض العمليات الحسابية على التواريخ والأوقات باستخدام تعليمة <code>SELECT</code> وذلك لأغراض عملية على البيانات النموذجية التجريبية. فيما يلي، ستتدرب على استخدام استعلامات تشمل دوال متنوعة للتواريخ والأوقات.
</p>

<h2 id="-2">
	استخدام دوال التاريخ والوقت وتعابير الفترات الزمنية
</h2>

<p>
	هناك العديد من الدوال التي يُمكن استخدامها لإيجاد ومعالجة قيم التواريخ والأوقات في SQL. إذ تُستخدم الدوال SQL على نحوٍ أساسي لمعالجة البيانات أو التعامل معها والتحكم بها، وتختلف الدوال المتوفرة باختلاف تقديم SQL المُستخدم. بيد أنّ معظم تقديمات SQL تتيح استرجاع القيم الحالية للتاريخ والوقت من خلال الاستعلام عن قيم دالتيّ <code>current_date</code> و<code>current_time</code>.
</p>

<p>
	على سبيل المثال، لمعرفة تاريخ اليوم، فالصياغة بسيطة وتتألف فقط من تعليمة <code>SELECT</code> ودالة <code>current_date</code> كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_24" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT current_date</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+--------------+
| current_date |
+--------------+
| 2022-02-15   |
+--------------+
1 row in set (0.00 sec)
</pre>

<p>
	كما يمكننا إيجاد الوقت الحالي باستخدام نفس الصياغة اعتمادًا على الدالة <code>current_time</code>، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_26" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT current_time</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+--------------+
| current_time |
+--------------+
| 17:10:20     |
+--------------+
1 row in set (0.00 sec)
</pre>

<p>
	أمّا إذا كنت تفضل الاستعلام عن كل من التاريخ والوقت معًا في خرجٍ واحد، فاستخدم الدالة <code>current_timestamp</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_28" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT current_timestamp</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+---------------------+
| current_timestamp   |
+---------------------+
| 2022-02-15 19:09:58 |
+---------------------+
1 row in set (0.00 sec)
</pre>

<p>
	كما يمكن استخدام دوال التاريخ والوقت كتلك المُستخدمة أعلاه ضمن دوال حسابية على نحوٍ مشابه للقسم السابق من مقالنا. فعلى سبيل المثال، بفرض أنّنا نريد معرفة التاريخ قبل 11 يومًا من تاريخ اليوم الحالي، فيمكننا استخدام نفس الصيغة المُستخدمة سابقًا للاستعلام عن قيمة الدالة <code>current_date</code> مطروحًا منها العدد <code>11</code> لإيجاد التاريخ قبل أحد عشر يومًا:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_30" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT current_date </span><span class="pun">-</span><span class="pln"> </span><span class="lit">11</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------------+
| current_date - 11 |
+-------------------+
|          20220206 |
+-------------------+
1 row in set (0.01 sec)
</pre>

<p>
	يُشير هذا الخرج لكون التاريخ قبل 11 يومًا من <code>current_date</code> (وقت كتابة هذا النص) هو <code>2022-02-06</code>، أو 6 فبراير 2022. لنحاول الآن تنفيذ نفس العملية مستبدلين الدالة <code>current_date</code> بالدالة <code>current_time</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_32" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT current_time </span><span class="pun">-</span><span class="pln"> </span><span class="lit">11</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------------+
| current_time - 11 |
+-------------------+
|            233639 |
+-------------------+
1 row in set (0.00 sec)
</pre>

<p>
	يُظهر هذا الخرج أنه عند طرح <code>11</code> من قيمة <code>current_time</code>، يُطرح فعليًا مقدار 11 ثانية. في حين تُفسّر العملية التي نفذناها سابقًا باستخدام الدالة <code>current_date</code> العدد 11 على أنه أيام وليس ثواني.
</p>

<p>
	هذا التباين في تفسير الأرقام لدى التعامل مع دوال التاريخ والوقت قد يكون مربكًا. فبدلاً من اضطرارك لمعالجة قيم التاريخ والوقت باستخدام الحسابات الرياضية بهذا الشكل، توفّر العديد من أنظمة إدارة قواعد البيانات وضوحًا أكبر من خلال استخدام تعابير الفترات الزمنية <code>INTERVAL</code>.
</p>

<p>
	تسمح تعابير الفترات الزمنية <code>INTERVAL</code> بإيجاد ما سيكون عليه التاريخ أو الوقت قبل أو بعد فترة محددة من تعبير تاريخ أو وقت معين. وتأخذ هذه التعابير الصيغة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_34" style=""><span class="pln">INTERVAL </span><span class="str">`value`</span><span class="pln"> </span><span class="str">`unit`</span></pre>

<p>
	على سبيل المثال، لإيجاد التاريخ بعد خمسة أيام من الآن، يُمكننا تشغيل الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_36" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT current_date </span><span class="pun">+</span><span class="pln"> INTERVAL </span><span class="str">'5'</span><span class="pln"> DAY AS </span><span class="str">"5_days_from_today"</span><span class="pun">;</span></pre>

<p>
	أوجدنا في هذا المثال قيمة <code>current_date</code>، ثم أضفنا إليها تعبير الفترة <code>INTERVAL '5' DAY</code>. ما سيُعيد التاريخ بعد خمسة أيام من الآن:
</p>

<pre class="ipsCode">الخرج
+-------------------+
| 5_days_from_today |
+-------------------+
| 2022-03-06        |
+-------------------+
1 row in set (0.00 sec)
</pre>

<p>
	وهذا أقل غموضًا بكثير من الاستعلام التالي، الذي ينتج عنه خرج مشابه، وحتى إن لم يكن مطابقًا تمامًا:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_38" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT current_date </span><span class="pun">+</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> AS </span><span class="str">"5_days_from_today"</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------------+
| 5_days_from_today |
+-------------------+
|          20220306 |
+-------------------+
1 row in set (0.00 sec)
</pre>

<p>
	يُلاحظ أنه يُمكن أيضًا طرح فترات زمنية من التواريخ أو الأوقات لإيجاد <strong>قيم</strong> قبل التاريخ المحدد:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_40" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT current_date </span><span class="pun">-</span><span class="pln"> INTERVAL </span><span class="str">'7'</span><span class="pln"> MONTH AS </span><span class="str">"7_months_ago"</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+--------------+
| 7_months_ago |
+--------------+
| 2021-08-01   |
+--------------+
1 row in set (0.00 sec)
</pre>

<p>
	تعتمد الوحدات المتاحة لك لاستخدامها في تعابير <code>INTERVAL</code> على نظام <a href="https://academy.hsoub.com/devops/servers/databases/%D9%85%D8%AD%D8%B1%D9%83%D8%A7%D8%AA-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/" rel="">إدارة قواعد البيانات DBMS</a> الذي اخترته، ولكن تتوفّر في معظم الأنظمة خيارات من قبيل <code>HOUR</code> و<code>MINUTE</code> و<code>SECOND</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_42" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT current_time </span><span class="pun">+</span><span class="pln"> INTERVAL </span><span class="str">'6'</span><span class="pln"> HOUR AS </span><span class="str">"6_hours_from_now"</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> current_time </span><span class="pun">-</span><span class="pln"> INTERVAL </span><span class="str">'5'</span><span class="pln"> MINUTE AS </span><span class="str">"5_minutes_ago"</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> current_time </span><span class="pun">+</span><span class="pln"> INTERVAL </span><span class="str">'20'</span><span class="pln"> SECOND AS </span><span class="str">"20_seconds_from_now"</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+------------------+---------------+---------------------+
| 6_hours_from_now | 5_minutes_ago | 20_seconds_from_now |
+------------------+---------------+---------------------+
| 07:51:43         | 01:46:43      | 01:52:03.000000   |
+------------------+---------------+---------------------+
1 row in set (0.00 sec)
</pre>

<p>
	الآن وبعد أن اطلعت على تعابير الفترات الزمنية وتعرفت على بعض دوال التاريخ والوقت، الفرصة أمامك لتتدرّب مستخدمًا البيانات التجريبية النموذجية التي أدرجتها في الخطوة الأولى.
</p>

<h2 id="cast">
	استخدام الدالة <code>CAST</code> ودوال التجميع مع التاريخ والوقت
</h2>

<p>
	بالعودة إلى المثال الثالث من قسم استخدام العمليات الحسابية مع التاريخ والوقت، حين نفّذنا الاستعلام التالي لطرح زمن الانتهاء <code>end_time</code> من زمن البدء <code>start_time</code> لحساب إجمالي الساعات التي أكملها كل عداء لكل سباق. حصلنا على العمود <code>total_time</code> والذي يتضمّن ناتج طويل جدًا يتبع نمط البيانات <code>TIMESTAMP</code> المُحدد لهذا العمود من الجدول:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_44" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT runner_name</span><span class="pun">,</span><span class="pln"> race_name</span><span class="pun">,</span><span class="pln"> end_time </span><span class="pun">-</span><span class="pln"> start_time 
mysql</span><span class="pun">&gt;</span><span class="pln"> AS total_time 
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM race_results</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------+---------------+----------------+
| runner_name | race_name     | total_time     |
+-------------+---------------+----------------+
| Ahmad        | 1600_meters   | 20220918000630 |
| Ahmad        | 5K            | 20221019002231 |
| Ahmad        | 10K           | 20221120003805 |
| Ahmad        | half_marathon | 20221221013904 |
| Ahmad        | full_marathon | 20230122032310 |
| Mohammad       | 1600_meters   | 20220918000715 |
| Mohammad       | 5K            | 20221019003050 |
| Mohammad       | 10K           | 20221120011017 |
| Mohammad       | half_marathon | 20221221021157 |
| Mohammad       | full_marathon | 20230122040210 |
+-------------+---------------+----------------+
10 rows in set (0.00 sec)
</pre>

<p>
	ونظرًا لأنّنا نُجري عملية على عمودين بأنماط بيانات مختلفة (عمود <code>end_time</code> يحمل قيم من نمط <code>TIMESTAMP</code> وعمود <code>start_time</code> يحمل قيم من نمط <code>TIME</code>)، فإنّ قاعدة البيانات لا تعرف أي نمط بيانات يجب أن تستخدم لدى طباعة نتيجة العملية. لذا تحوّل كلا القيمتين إلى أعداد صحيحة لتتمكن من تنفيذ العملية، مما ينتج عنه الأرقام الطويلة في عمود <code>total_time</code>.
</p>

<p>
	وكحل لجعل هذه البيانات أوضح للقراءة والتفسير، يمكنك استخدام دالة <code>CAST</code> لتحويل هذه القيم الطويلة من الأعداد الصحيحة إلى نمط بيانات <code>TIME</code>. وللقيام بذلك، ابدأ بالدالة <code>CAST</code> ثم اتبعها مباشرةً بقوس فتح ثم القيم التي تريد تحويلها ثم الكلمة المفتاحية <code>AS</code> متبوعة باسم نمط البيانات الذي تريد التحويل إليه.
</p>

<p>
	الاستعلام التالي مطابق للمثال السابق، ولكنه يستخدم دالة <code>CAST</code> لتحويل عمود <code>total_time</code> إلى نمط بيانات <code>TIME</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_46" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT runner_name</span><span class="pun">,</span><span class="pln"> race_name</span><span class="pun">,</span><span class="pln"> CAST</span><span class="pun">(</span><span class="pln">end_time </span><span class="pun">-</span><span class="pln"> start_time AS time</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> AS total_time 
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM race_results</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------+---------------+------------+
| runner_name | race_name     | total_time |
+-------------+---------------+------------+
| Ahmad        | 1600_meters   | 00:06:30   |
| Ahmad        | 5K            | 00:22:31   |
| Ahmad        | 10K           | 00:38:05   |
| Ahmad        | half_marathon | 01:39:04   |
| Ahmad        | full_marathon | 03:23:10   |
| Mohammad       | 1600_meters   | 00:07:15   |
| Mohammad       | 5K            | 00:30:50   |
| Mohammad       | 10K           | 01:10:17   |
| Mohammad       | half_marathon | 02:11:57   |
| Mohammad       | full_marathon | 04:02:10   |
+-------------+---------------+------------+
10 rows in set (0.00 sec)
</pre>

<p>
	حوّلت الدالة <code>CAST</code> قيم البيانات في الخرج أعلاه إلى نمط البيانات <code>TIME</code>، ما جعلها أسهل للقراءة والفهم.
</p>

<p>
	لنستخدم الآن بعضًا من دوال التجميع مع دالة <code>CAST</code> بهدف إيجاد أقصر وأطول توقيت وإجمالي الوقت لكل عدّاء. لنستعلم بدايةً عن أصغر (أو أقصر) مدة زمنية مستغرقة باستخدام دالة التجميع <code>MIN</code>. ومجددًا لا بدّ من استخدام الدالة <code>CAST</code> هنا لتحويل قيم البيانات من النمط <code>TIMESTAMP</code> إلى النمط <code>TIME</code> مما يجعلها أوضح. وتجدر الملاحظة إلى أنّه عند استخدام دالتين كما في هذا المثال، تتطلب العملية استخدام زوجين من الأقواس الهلالية، إذ يجب أن تكون عملية حساب إجمالي الساعات المُتمثلة في طرح زمن البدء من زمن الانتهاء (<code>end_time - start_time</code>) متداخلةً ضمن إحداها. وأخيرًا، سنضيف بنية <code>GROUP BY</code> لتنظيم هذه القيم بناءً على عمود <code>runner_name</code> بحيث يعرض الخرج نتائج سباقات كلا العدّائين:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_48" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT runner_name</span><span class="pun">,</span><span class="pln"> MIN</span><span class="pun">(</span><span class="pln">CAST</span><span class="pun">(</span><span class="pln">end_time </span><span class="pun">-</span><span class="pln"> start_time AS time</span><span class="pun">))</span><span class="pln"> AS min_time
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM race_results GROUP BY runner_name</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------+----------+
| runner_name | min_time |
+-------------+----------+
| Ahmad        | 00:06:30 |
| Mohammad       | 00:07:15 |
+-------------+----------+
2 rows in set (0.00 sec)
</pre>

<p>
	يُظهر هذا الخرج أقصر زمن لكل عدّاء، في هذه الحالة الحد الأدنى هو ست دقائق وثلاثون ثانية لأحمد، وسبع دقائق وخمس عشرة ثانية لمحمد.
</p>

<p>
	الآن ولإيجاد أطول زمن لكل عدّاء، يمكننا استخدام نفس الصيغة كما في الاستعلام السابق مستبدلين الدالة <code>MIN</code> بالدالة <code>MAX</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_50" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT runner_name</span><span class="pun">,</span><span class="pln"> MAX</span><span class="pun">(</span><span class="pln">CAST</span><span class="pun">(</span><span class="pln">end_time </span><span class="pun">-</span><span class="pln"> start_time AS time</span><span class="pun">))</span><span class="pln"> AS max_time
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM race_results GROUP BY runner_name</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------+----------+
| runner_name | max_time |
+-------------+----------+
| Ahmad        | 03:23:10 |
| Mohammad       | 04:02:10 |
+-------------+----------+
2 rows in set (0.00 sec)
</pre>

<p>
	يُشير هذا الخرج أن أطول زمن جري لأحمد بلغ كإجمالي ثلاث ساعات وثلاث وعشرين دقيقة وعشر ثوانٍ؛ ولمحمد كان أربع ساعات ودقيقتين وعشر ثوانٍ.
</p>

<p>
	أمّا الآن، لنستعلم عن بعض المعلومات العامة (الخلاصة) حول إجمالي الساعات التي قضاها كل عدّاء في الجري. سندمج في هذا الاستعلام دالة التجميع <code>SUM</code> لإيجاد المجموع الإجمالي للساعات بناءً على الفرق بين <code>end_time</code> و<code>start_time</code>، كما سنستخدم الدالة <code>CAST</code> لتحويل هذه قيم البيانات هذه إلى نمط البيانات <code>TIME</code>. كما لم ننسَ تضمين <code>GROUP BY</code> لتنظيم قيم نتائج كلا العدّاءين:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_52" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT runner_name</span><span class="pun">,</span><span class="pln"> SUM</span><span class="pun">(</span><span class="pln">CAST</span><span class="pun">(</span><span class="pln">end_time </span><span class="pun">-</span><span class="pln"> start_time AS time</span><span class="pun">))</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> AS total_hours FROM race_results GROUP BY runner_name</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------+-------------+
| runner_name | total_hours |
+-------------+-------------+
| Ahmad        |       52880 |
| Mohammad       |       76149 |
+-------------+-------------+
2 rows in set (0.00 sec)
</pre>

<p>
	يُبيّن هذا الخرج على نحوٍ مثير للاهتمام كيفية تفسير MySQL للنتائج، والتي في الواقع تحسب الزمن الإجمالي في هيئة أعداد صحيحة. فلو قرأنا هذه النتائج على أنها أزمنة، فإنّ الرقم المُعبّر عن إجمالي زمن أحمد يُفصّل إلى خمس ساعات وثمان وعشرين دقيقة وثمانين ثانية؛ وزمن محمد يُفصّل إلى سبع ساعات وواحد وستين دقيقة وتسع وأربعين ثانية. ومن الواضح أنّ هذا التفصيل للأزمنة لا يبدو منطقيًا وفقًا للقيم الفعلية، مما يشير إلى أنّ MySQL تعاملت مع الأزمنة كأعداد صحيحة. أمّا إذا جربنا الأمر عينه في نظام إدارة قواعد بيانات مختلف، من قبيل PostgreSQL على سبيل المثال، فسيبدو الاستعلام مختلفًا قليلًا:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8603_54" style=""><span class="pln">postgres</span><span class="pun">=#</span><span class="pln"> SELECT runner_name</span><span class="pun">,</span><span class="pln"> SUM</span><span class="pun">(</span><span class="pln">CAST</span><span class="pun">(</span><span class="pln">end_time </span><span class="pun">-</span><span class="pln"> start_time AS time</span><span class="pun">))</span><span class="pln">
postgres</span><span class="pun">=#</span><span class="pln"> AS total_hours FROM race_results GROUP BY runner_name</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
runner_name | total_hours
-------------+-------------
 Mohammad       | 10:01:44
 Ahmad        | 06:09:20
(2 rows)
</pre>

<p>
	يُفسّر الاستعلام في PostgreSQL القيم كأزمنة في هذه الحالة، وتُحسب على هذا الأساس، إذ يمكن تفصيل نتائج محمد إلى إجمالي عشر ساعات ودقيقة واحدة وأربع وأربعين ثانية؛ وزمن أحمد إلى ست ساعات وتسع دقائق وعشرين ثانية. وما هذا سوى مثال حول كيفية تفسير تنفيذات أنظمة إدارة قواعد البيانات المختلفة للقيم البيانات على نحوٍ مختلف حتى ولو كانت تستخدم نفس الاستعلام ونفس مجموعة البيانات.
</p>

<h2 id="-3">
	الخلاصة
</h2>

<p>
	لعلّ فهم كيفية استخدام التاريخ والوقت في SQL مفيد لدى الاستعلام عن نتائج محددة مثل الدقائق أو الثواني أو الساعات أو الأيام أو الأشهر أو السنوات، أو مزيج من كل هذه الوحدات. ناهيك عن وجود العديد من الدوال المتاحة للتواريخ والأوقات والتي تسهّل العثور على قيم معينة، من قبيل التاريخ أو الوقت الحالي. ورغم استخدامنا في هذا المقال لعمليات حسابية بسيطة مثل الجمع والطرح على التواريخ والأوقات في SQL، ولكن يمكنك استخدام قيم التاريخ والوقت مع أي تعبير رياضي.
</p>

<p>
	وللمزيد حول SQL، نشجعك على متابعة <a href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20%D8%AA%D8%B9%D9%84%D9%85%20sql/" rel="">سلسلة تعلم SQL</a> في أكاديمية حسوب.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-work-with-dates-and-times-in-sql" rel="external nofollow">How To Work with Dates and Times in SQL</a> لصاحبه Jeanelle Horcasitas.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%AC%D9%85%D9%8A%D8%B9%D9%8A%D8%A9-%D9%81%D9%8A-sql-r2410/" rel="">كيفية استخدام التعابير الرياضية والدوال التجميعية في SQL</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/SQL/datatype" rel="external">أنواع البيانات في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r855/" rel="">دوال التعامل مع البيانات في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%86%D8%B8%D8%B1%D8%A9-%D8%B3%D8%B1%D9%8A%D8%B9%D8%A9-%D8%B9%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D9%87%D9%8A%D9%83%D9%84%D9%8A%D8%A9-sql-r1368/" rel="">نظرة سريعة على لغة الاستعلامات الهيكلية SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/php/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%88%D9%82%D8%AA-%D9%88%D8%A7%D9%84%D8%AA%D8%A7%D8%B1%D9%8A%D8%AE-%D9%81%D9%8A-php-r1047/" rel="">التعامل مع الوقت والتاريخ في PHP</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D9%8A%D9%84-%D8%B9%D9%84%D9%89-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r851/" rel="">معالجة الأخطاء والتعديل على قواعد البيانات في SQL</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2411</guid><pubDate>Thu, 26 Sep 2024 15:06:02 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x62A;&#x639;&#x627;&#x628;&#x64A;&#x631; &#x627;&#x644;&#x631;&#x64A;&#x627;&#x636;&#x64A;&#x629; &#x648;&#x627;&#x644;&#x62F;&#x648;&#x627;&#x644; &#x627;&#x644;&#x62A;&#x62C;&#x645;&#x64A;&#x639;&#x64A;&#x629; &#x641;&#x64A; SQL</title><link>https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D8%AC%D9%85%D9%8A%D8%B9%D9%8A%D8%A9-%D9%81%D9%8A-sql-r2410/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_09/-------SQL.png.ce2f9bea86f4160770276e84e2ce317a.png" /></p>
<p>
	تُستخدم لغة الاستعلام البنيوية SQL لتخزين وإدارة وتنظيم المعلومات في نظام إدارة قواعد البيانات العلاقية RDBMS. كما يمكن لـلغة SQL إجراء الحسابات ومعالجة البيانات باستخدام التعابير Expressions فالتعابير تجمع ما بين معاملات SQL المختلفة مع الدوال والقيم لحساب قيمةٍ ما. وتُستخدم التعابير الرياضية عادةً لجمع وطرح وقسمة وضرب القيم العددية. في حين تُستخدم الدوال التجميعية aggregate functions لتقييم وتجميع القيم ضمن مجموعات بهدف إنشاء ملخص إحصائي حولها، من قبيل المتوسط الحسابي لها أو مجموعها وإظهاره في عمود معين. وبالتالي يمكن أن توفّر التعابير الرياضية والتجميعية رؤًى قيّمة من خلال تحليل البيانات مما يُسهم في اتخاذ قرارات مستقبلية مستنيرة.
</p>

<p>
	ستتدرب من خلال هذا المقال على كيفية استخدام التعابير الرياضية. إذ ستتعامل بدايةً مع العمليات الحسابية كما لو كنت تستخدم آلةً حاسبة، لتستخدم بعدها هذه المعاملات مع بيانات نموذجية لتنفيذ استعلامات باستخدام الدوال التجميعية، انتهاءً بسيناريو عملي يتضمّن الاستعلام عن بيانات نموذجية للحصول على معلوماتٍ وتحليلاتٍ أعقد.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	لمتابعة الخطوات في هذا المقال، ستحتاج إلى:
</p>

<ul>
	<li>
		خادم عامل على توزيعة أوبنتو، مع مستخدم ذو صلاحيات مسؤول من نوع <code>sudo</code> مختلف عن المستخدم الجذر، وجدار حماية مُفعّل، كما هو موضح في <a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04" rel="external nofollow">دليل الإعداد الأولي للخادم مع الإصدار 20.04 من أوبنتو</a>، كما يمكنك الاطلاع على المقال <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A9-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-%D9%85%D9%86-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A8%D8%A3%D8%A8%D8%B3%D8%B7-%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-r575/" rel="">كيفية تثبيت توزيعة أوبنتو من لينكس بأبسط طريقة</a>.
	</li>
	<li>
		MySQL مثبتة ومؤمنة على الخادم، كما هو موضح في المقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبونتو</a>. وقد نفذنا خطوات هذا المقال باستخدام مستخدم MySQL مختلف عن المستخدم الجذر، مُنشأ وفق الطريقة الموضحة في الخطوة 3 من هذا المقال.
	</li>
</ul>

<p>
	<strong>ملاحظة:</strong> تجدر الإشارة إلى أنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية لها تقديماتها الفريدة من لغة <a href="https://academy.hsoub.com/programming/sql/" rel="">SQL</a>. فبالرغم من كون الأوامر المُقدمة في هذا المقال ستعمل مع معظم هذه الأنظمة، ولكن قد تجد بعض الاختلافات في الصيغة أو الناتج عند تنفيذها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<p>
	وبالعودة إلى مستلزمات العمل، ستحتاج أيضًا إلى قاعدة بيانات وجدول مُحمّل ببعض البيانات التجريبية النموذجية لتتمكن من التدرب على الأمثلة العديدة حول التعابير الرياضية في هذا المقال. لذا ننصحك بمتابعة القسم القادم <strong>الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية</strong> للمزيد من التفاصيل حول كيفية إعداد قاعدة بيانات وجدول لاستخدامهما في الأمثلة خلال هذا المقال.
</p>

<h2 id="mysql">
	الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	إذا كان نظام قاعدة بيانات <a href="https://wiki.hsoub.com/SQL" rel="external">SQL</a> الخاص بك يعمل على خادم عن بُعد، اتصل بالخادم مُستخدمًا بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><a href="https://academy.hsoub.com/devops/security/ssh/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></a></abbr> من جهازك المحلي على النحو التالي:
</p>

<pre class="ipsCode">$ ssh ssh user@your_server_ip
</pre>

<p>
	ثم افتح واجهة سطر الأوامر في خادم MySQL، مُستبدلًا <code>user</code> باسم حساب مستخدم MySQL الخاص بك:
</p>

<pre class="ipsCode">$ mysql -u user -p
</pre>

<p>
	أنشئ قاعدة بيانات باسم <code>mathDB</code>:
</p>

<pre class="ipsCode">mysql&gt; CREATE DATABASE mathDB;
</pre>

<p>
	وبمجرّد إنشاء قاعدة البيانات بنجاح ستحصل على خرجٍ كالتالي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	ولاختيار قاعدة البيانات <code>mathDB</code>، نفّذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode">mysql&gt; USE mathDB;
</pre>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	الآن وبعد اختيار قاعدة البيانات <code>mathDB</code>، <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r2224/" rel="">لننشئ جدولًا</a> ضمنها باستخدام الأمر <code><a href="https://wiki.hsoub.com/SQL/create_table" rel="external">CREATE TABLE</a></code>.
</p>

<p>
	بعد تحديد قاعدة البيانات، سننشئ جدولًا ضمنها باستخدام تعليمة <code>CREATE TABLE</code>. وكمثال في مقالنا هذا، سننشئ جدولاً باسم <code>product_information</code> لتسجيل معلومات المخزون والمبيعات لمحل شاي صغير. سيشتمل هذا الجدول على الأعمدة الثمانية التالية:
</p>

<ul>
	<li>
		<strong>product_id</strong>: يُمثّل قيم من نمط بيانات الأعداد الصحيحة int وسيكون المفتاح الأساسي للجدول، ما يعني أن كل قيمة في هذا العمود ستلعب دور المعرّف الفريد لسجلها.
	</li>
	<li>
		<strong>product_name</strong>: يُوضّح أسماء المنتجات باستخدام نمط البيانات varchar بحد أقصى 30 محرفًا.
	</li>
	<li>
		<strong>product_type</strong>: يُخزّن أنواع المنتجات باستخدام نمط البيانات varchar بحد أقصى 30 محرفًا.
	</li>
	<li>
		<strong>total_inventory</strong>: يُمثّل عدد الوحدات المتبقية في المخزن من كل منتج، باستخدام نمط البيانات int بحد أقصى 200 وحدة.
	</li>
	<li>
		<strong>product_cost</strong>: يُظهر سعر شراء كل منتج بالتكلفة الأصلية باستخدام نمط البيانات decimal بحد أقصى 3 أرقام إلى يسار الفاصلة العشرية و2 رقم إلى يمينها.
	</li>
	<li>
		<strong>product_retail</strong>: يُسجّل أسعار كل منتج يُباع بالتجزئة، باستخدام نمط البيانات decimal بحد أقصى 3 أرقام إلى يسار الفاصلة العشرية و2 رقم إلى يمينها.
	</li>
	<li>
		<strong>store_units</strong>: يعرض عدد وحدات المنتج المحدد المتاحة في المخزون للمبيعات في المتجر الفعلي باستخدام قيم من نمط البيانات int.
	</li>
	<li>
		<strong>online_units</strong>: يُمثل عدد وحدات المنتج المحدد المتاحة في المخزون للمبيعات عبر الإنترنت، باستخدام قيم من نمط البيانات int.
	</li>
</ul>

<p>
	ولإنشاء هذا الجدول النموذجي، نفّذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_12" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE product_information </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> product_id </span><span class="kwd">int</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> product_name varchar</span><span class="pun">(</span><span class="lit">30</span><span class="pun">),</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> product_type varchar</span><span class="pun">(</span><span class="lit">30</span><span class="pun">),</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> total_inventory </span><span class="kwd">int</span><span class="pun">(</span><span class="lit">200</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> product_cost </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> product_retail </span><span class="kwd">decimal</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> store_units </span><span class="kwd">int</span><span class="pun">(</span><span class="lit">100</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> online_units </span><span class="kwd">int</span><span class="pun">(</span><span class="lit">100</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> PRIMARY KEY </span><span class="pun">(</span><span class="pln">product_id</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected, 0 warnings (0.01 sec)
</pre>

<p>
	ثم املأ هذا الجدول الفارغ ببعض البيانات التجريبية النموذجية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_14" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO product_information
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">product_id</span><span class="pun">,</span><span class="pln"> product_name</span><span class="pun">,</span><span class="pln"> product_type</span><span class="pun">,</span><span class="pln"> total_inventory</span><span class="pun">,</span><span class="pln"> product_cost</span><span class="pun">,</span><span class="pln"> product_retail</span><span class="pun">,</span><span class="pln"> store_units</span><span class="pun">,</span><span class="pln"> online_units</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'chamomile'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tea'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">200</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5.12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7.50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">38</span><span class="pun">,</span><span class="pln"> </span><span class="lit">52</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'chai'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tea'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7.40</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9.00</span><span class="pun">,</span><span class="pln"> </span><span class="lit">17</span><span class="pun">,</span><span class="pln"> </span><span class="lit">27</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="str">'lavender'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tea'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">200</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5.12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7.50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">112</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'english_breakfast'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tea'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">150</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5.12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7.50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">22</span><span class="pun">,</span><span class="pln"> </span><span class="lit">74</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'jasmine'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tea'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">150</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6.17</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7.50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">33</span><span class="pun">,</span><span class="pln"> </span><span class="lit">92</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="str">'matcha'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tea'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6.17</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7.50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">41</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="str">'oolong'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tea'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">75</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7.40</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9.00</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">29</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tea sampler'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tea'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6.00</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8.50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">18</span><span class="pun">,</span><span class="pln"> </span><span class="lit">25</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ceramic teapot'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tea item'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">30</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7.00</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9.75</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">15</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="str">'golden teaspoon'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tea item'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2.00</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5.00</span><span class="pun">,</span><span class="pln"> </span><span class="lit">18</span><span class="pun">,</span><span class="pln"> </span><span class="lit">67</span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
Query OK, 10 rows affected (0.01 sec)
Records: 10  Duplicates: 0  Warnings: 0
</pre>

<p>
	وبمجرّد إدخالك للبيانات تغدو مستعدًا لبدء استخدام التعابير الرياضية.
</p>

<h2 id="sql-1">
	إجراء العمليات الحسابية باستخدام التعابير الرياضية في SQL
</h2>

<p>
	تُجرى الاستعلامات في SQL عادةً باستخدام الكلمة المفتاحية <code><a href="https://academy.hsoub.com/programming/sql/%D8%AC%D9%84%D8%A8-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85%D8%A7%D8%AA-%D8%B9%D8%A8%D8%B1-select-%D9%81%D9%8A-sql-r845/" rel="">SELECT</a></code> لاسترجاع البيانات المطلوبة من قاعدة البيانات، كما يمكن استخدامها لتنفيذ العديد من العمليات الرياضية.
</p>

<p>
	من المهم التذكير بأن SQL تُستخدم على نحوٍ رئيسي في الواقع العملي بهدف تنفيذ الاستعلامات وإجراء الحسابات على القيم الموجودة ضمن قاعدة البيانات الفعلية. ولكننا سنستخدم <code>SELECT</code> في هذا القسم للتعامل مع قيم عددية مباشرة (وليس مع قيم مخزنة في قاعدة البيانات) بهدف التعرف على صيغة التعابير والعمليات الرياضية.
</p>

<p>
	ولكن وقبل الشروع، دعونا نلقي نظرة عامّة على المعاملات الرياضية المُستخدمة لإجراء ست عمليات حسابية في SQL. مع الانتباه إلى أنّ هذه القائمة ليست شاملة بالكامل، وأنّ لكل نظام من أنظمة إدارة قواعد البيانات العلاقية مجموعته الخاصة من المعاملات الرياضية:
</p>

<ul>
	<li>
		عملية الجمع تستخدم الرمز <code>+</code>
	</li>
	<li>
		عملية الطرح تستخدم الرمز <code>-</code>
	</li>
	<li>
		عملية الضرب تستخدم الرمز <code>*</code>
	</li>
	<li>
		عملية القسمة تستخدم الرمز <code>/</code>
	</li>
	<li>
		عملية باقي القسمة تستخدم الرمز <code>%</code>
	</li>
	<li>
		عملية الرفع إلى قوة تستخدم <a href="https://wiki.hsoub.com/SQL/functions" rel="external">الدالة</a><code>POW(x,y‎)‎</code>
	</li>
</ul>

<p>
	كما يُمكنك التدرب على إجراء أنماط متنوعة من العمليات الحسابية مُستخدمًا تراكيب قيم من اختيارك. أمّا عنا، فسنُقدم الشرح من خلال الأمثلة التالية، بدءًا بمعادلة للجمع:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_16" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="lit">893</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">579</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-----------+
| 893 + 579 |
+-----------+
|      1472 |
+-----------+
1 row in set (0.00 sec)
</pre>

<p>
	ومن الجدير بالملاحظة أنه ما من حاجة لتضمين بنية <code>FROM</code> في الاستعلام ضمن مثالنا هذا أو في الأمثلة التالية، وذلك نظرًا لكوننا لا نسترجع أي بيانات من قاعدة البيانات، وإنما نُجري فقط عملياتٍ حسابية على أرقامٍ مباشرة.
</p>

<p>
	أمّا الآن، لنجري عملية حسابية باستخدام معامل الطرح. ونلاحظ أنّه من الممكن إجراء العمليات الحسابية على القيم ذات الخانات العشرية كالتالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_18" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="lit">437.82</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">66.34</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+----------------+
| 437.82 - 66.34 |
+----------------+
|         371.48 |
+----------------+
1 row in set (0.00 sec)
</pre>

<p>
	كما من الممكن تضمين قيم ومعاملات متعددة ضمن تعبير حسابي واحد في SQL. فمثلًا نستخدم في التعبير الحسابي التالي ثلاث معاملات ضرب لإيجاد حاصل ضرب أربعة أرقام:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_20" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="lit">60</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">1234</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">117</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+---------------------+
| 60 * 1234 * 2 * 117 |
+---------------------+
|            17325360 |
+---------------------+
1 row in set (0.00 sec)
</pre>

<p>
	لنُجري الآن عملية قسمة تتضمن قيمة عشرية وأخرى من نمط الأعداد الصحيحة، كالتالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_22" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="lit">2604.56</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">41</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+--------------+
| 2604.56 / 41 |
+--------------+
|    63.525854 |
+--------------+
1 row in set (0.00 sec)
</pre>

<p>
	كما يعدّ <code>%</code> كمعامل آخر لعملية القسمة والمُسمّى بمعامل باقي القسمة، إذ يحسب قيمة الباقي من قسمة المقسوم على المقسوم عليه:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_24" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="lit">38</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+--------+
| 38 % 5 |
+--------+
|      3 |
+--------+
1 row in set (0.00 sec)
</pre>

<p>
	كما يعدّ المعامل <code>POW(x,y)‎</code> من المعاملات المفيدة بدوره، إذ يحسب قيمة القوة لأساس <code>x</code> وأس <code>y</code>، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_26" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT POW</span><span class="pun">(</span><span class="lit">99</span><span class="pun">,</span><span class="lit">9</span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
+---------------------+
| POW(99,9)           |
+---------------------+
| 9.13517247483641e17 |
+---------------------+
1 row in set (0.01 sec)
</pre>

<p>
	والآن وبعدما تدربت على إجراء العمليات الحسابية مستخدمًا كل عملية على حدة، أصبح بإمكانك تجربة دمج معاملات رياضية مختلفة للتدرب على التعامل مع معادلات رياضية أعقد.
</p>

<h2 id="sql-2">
	فهم ترتيب العمليات في SQL
</h2>

<p>
	ربما سمعت مسبقًا عن مصطلح PEMDAS، والذي يُمثّل اختصارًا يدل على أولوية ترتيب تنفيذ العمليات الحسابية بدءًا بالأقواس، ثم الرفع إلى قوة، ثم الضرب والقسمة، ثم الجمع والطرح. وهو المصطلح المُستخدم في الولايات المتحدة، وقد تستخدم دول أخرى اختصارات مختلفة لتمثيل قاعدة ترتيب العمليات.
</p>

<p>
	ولدى دمج عمليات رياضية مختلفة متداخلة ضمن أقواس، تقرأ SQL العمليات من اليسار إلى اليمين، ومن ثم تقرأ القيم بدءًا من الأقواس الأكثر تداخلًا. لذا، من المهم التأكد بأن القيم داخل الأقواس تعكس بوضوح المعادلة التي نسعى إلى حلها.
</p>

<p>
	لنجرّب إجراء العملية الحسابية التالية باستخدام أقواس وبعض المعاملات المختلفة:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_28" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">(</span><span class="lit">2</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">4</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">8</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-----------+
| (2+4) * 8 |
+-----------+
|        48 |
+-----------+
1 row in set (0.00 sec)
</pre>

<p>
	تذكّر أنّ مكان وضع الأقواس مهم جدًا، فإذا لم تكن حذرًا قد يتغير الناتج كليًا. على سبيل المثال، سنستخدم فيما يلي نفس القيم الثلاث ونفس المعاملات ولكن بتغيير مكان الأقواس، ما سينتج عنه نتائج مختلفة:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_30" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="lit">2</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pln">  </span><span class="pun">*</span><span class="pln"> </span><span class="lit">8</span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
+-------------+
| 2 + (4 * 8) |
+-------------+
|          34 |
+-------------+
1 row in set (0.00 sec)
</pre>

<p>
	وإذا كنت ممن يفضلون إجراء العمليات الحسابية دون استخدام أقواس، فالأمر ممكن أيضًا، مع ملاحظة أنّ قاعدة ترتيب العمليات تبقى سارية في هذه الحالة؛ وبالتالي وكما في حالة استخدام الأقواس، تأكد من أن المعادلة تعكس بدقة النتيجة المرجوة استنادًا إلى ترتيب العمليات التي ستُقيّم بناءً عليها. في المثال التالي، نلاحظ أنّ عملية القسمة تأخذ الأولوية على معامل الطرح لتنتج قيمة سالبة:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_32" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="lit">100</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">300</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+---------------+
| 100 / 5 - 300 |
+---------------+
|     -280.0000 |
+---------------+
1 row in set (0.00 sec)
</pre>

<p>
	لقد نجحت حتى الآن في استخدام التعابير الرياضية لأداء العمليات الحسابية الأساسية منها والمركبة مستخدمًا مجموعة متنوعة من المعاملات. في الخطوة التالية، ستستخدم البيانات النموذجية لتنفيذ عمليات حسابية باستخدام دوال التجميع مُستخلصًا معلومات جديدة من بياناتك.
</p>

<h2 id="-1">
	تحليل البيانات باستخدام دوال التجميع
</h2>

<p>
	لنفترض بأنك تملك محلًّا صغير لبيع الشاي، وأنّك ترغب في إجراء حسابات تتعلق بالمعلومات المخزنة في قاعدة البيانات لديك. فيمكن لـ SQL استخدام التعابير الرياضية للاستعلام عن البيانات ومعالجتها من خلال استرجاعها من الجداول في قاعدة البيانات بأعمدتها المختلفة، ما يساعد في توليد معلومات جديدة حول البيانات التي تهتم بتحليلها. ستتدرب في هذا القسم على كيفية الاستعلام عن البيانات ومعالجتها باستخدام دوال التجميع وصولًا إلى معلومات ذات طابع تجاري تخص أعمال محل الشاي.
</p>

<p>
	تشمل الدوال التجميعية الرئيسية في SQL كل من الدوال <code>SUM</code> و <code>MAX</code> و <code>MIN</code> و <code>AVG</code> و <code>COUNT</code>. تحسب دالة <code>SUM</code> حاصل جمع كافّة القيم في عمودٍ ما. فعلى سبيل المثال، لنستخدم الدالة <code>SUM</code> لحساب حاصل جمع إجمالي الكميات في عمود <code>total_inventory</code> من مجموعة بياناتنا النموذجية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_34" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT SUM</span><span class="pun">(</span><span class="pln">total_inventory</span><span class="pun">)</span><span class="pln"> FROM product_information</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+----------------------+
| SUM(total_inventory) |
+----------------------+
|                 1155 |
+----------------------+
1 row in set (0.00 sec)
</pre>

<p>
	في حين تحسب الدالة <code>MAX</code> القيمة العظمى في العمود المحدد. لنستخدم الآن هذه الدالة للاستعلام عن القيمة العظمى للتكاليف الأصلية المدفوعة للمنتجات والمُدرجة في عمود <code>product_cost</code>، مع استخدام تعليمة <code>AS</code> لإعادة تسمية ترويسة العمود لتغدو <code>cost_max</code> ما يجعلها أوضح:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_36" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT MAX</span><span class="pun">(</span><span class="pln">product_cost</span><span class="pun">)</span><span class="pln"> AS cost_max 
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM product_information</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+----------+
| cost_max |
+----------+
|     7.40 |
+----------+
1 row in set (0.00 sec)
</pre>

<p>
	تُعد الدالة <code>MIN</code> النقيض للدالة <code>MAX</code>، إذ تحسب القيمة الدنيا للقيم الموجودة في عمود واحد. لنستخدمها الآن للاستعلام عن القيمة الدنيا المدفوعة للمنتجات بسعر التجزئة في عمود <code>product_retail</code>، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_38" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT MIN</span><span class="pun">(</span><span class="pln">product_retail</span><span class="pun">)</span><span class="pln"> AS retail_min 
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM product_information</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+------------+
| retail_min |
+------------+
|       5.00 |
+------------+
1 row in set (0.00 sec)
</pre>

<p>
	أمّا الدالة <code>AVG</code> فتحسب المتوسط الحسابي لجميع القيم في العمود المحدد. وتجدر الملاحظة إلى إمكانية تشغيل أكثر من دالة تجميعية واحدة في نفس الاستعلام. لنجرّب الآن دمج دالة لإيجاد متوسط سعر المنتجات المباعة بالتجزئة <code>product_retail</code> وأخرى لسعر المنتجات المشتراة بالتكلفة <code>product_cost</code> الأصلية في استعلامٍ واحد:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_40" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT AVG</span><span class="pun">(</span><span class="pln">product_retail</span><span class="pun">)</span><span class="pln"> AS retail_average</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> AVG</span><span class="pun">(</span><span class="pln">product_cost</span><span class="pun">)</span><span class="pln"> AS cost_average 
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM product_information</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+----------------+--------------+
| retail_average | cost_average |
+----------------+--------------+
|       7.875000 |     5.750000 |
+----------------+--------------+
1 row in set (0.00 sec)
</pre>

<p>
	في حين تعمل الدالة <code>COUNT</code> على نحوٍ مختلف عن الدوال الأخرى، لأنها تحسب قيمة من الجدول نفسه بعد عد عدد السجلات التي يُعيدها الاستعلام. كمثال، لنستخدم الدالة <code>COUNT</code> بالتزامن مع تعليمة <code>WHERE</code> للاستعلام عن عدد المنتجات التي يزيد سعر بيعها بالتجزئة عن 8 دولارات:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_42" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT COUNT</span><span class="pun">(</span><span class="pln">product_retail</span><span class="pun">)</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM product_information 
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE product_retail </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">8.00</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-----------------------+
| COUNT(product_retail) |
+-----------------------+
|                     4 |
+-----------------------+
1 row in set (0.00 sec)
</pre>

<p>
	الآن لنستعلم عن عدد المنتجات من عمود <code>product_cost</code> المُشتراة من المتجر بسعر يزيد عن 8 دولارات:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_44" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT COUNT</span><span class="pun">(</span><span class="pln">product_cost</span><span class="pun">)</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM product_information 
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE product_cost </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">8.00</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+---------------------+
| COUNT(product_cost) |
+---------------------+
|                   0 |
+---------------------+
1 row in set (0.00 sec)
</pre>

<p>
	وبذلك تكون قد استخدمت دوال التجميع بنجاح لتوفير ملخص إحصائي للقيم من قبيل القيمة العظمى والقيمة الدنيا والمتوسط الحسابي والعدد، إذ استرجعت هذه المعلومات من بياناتنا النموذجية لمحاكاة سيناريو واقعي. أمّا في القسم الأخير من هذا المقال، فستطبق كل ما تعلمته حول التعابير الرياضية ودوال التجميع لإجراء استعلامات وتحليلات أكثر تفصيلاً على البيانات النموذجية لمحل الشاي الصغير.
</p>

<h2 id="-2">
	تطبيق التعابير الرياضية في سيناريو عملي لأغراض تجارية
</h2>

<p>
	سنعرض في هذا القسم عدة أمثلة لسيناريوهات مختلفة حول تحليل البيانات لمساعدة مالكي محل الشاي في اتخاذ القرارات المتعلقة بأعمالهم.
</p>

<p>
	كسيناريو أول، لنحسب العدد الإجمالي المتاح حاليًا في المخزون من الوحدات بغية فهم كمية المنتجات المتبقية والمتاحة للبيع سواءً في المتجر الفعلي أو عبر الإنترنت. كما سيتضمن هذا الاستعلام تعليمة <code>DESC</code> المُستخدمة لفرز أو <a href="https://wiki.hsoub.com/SQL/order_by" rel="external">ترتيب البيانات</a> من الأكبر إلى الأصغر. فعادةً ما تستخدم قواعد إدارة قواعد البيانات العلاقية الترتيب التصاعدي افتراضيًا، ولكننا في هذا المثال ضمّنا خيار <code>DESC</code> الذي يسمح لنا بعرض البيانات بترتيبٍ تنازلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_46" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT product_name</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> total_inventory </span><span class="pun">-</span><span class="pln"> </span><span class="pun">(</span><span class="pln">store_units </span><span class="pun">+</span><span class="pln"> online_units</span><span class="pun">)</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> AS remaining_inventory 
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM product_information 
mysql</span><span class="pun">&gt;</span><span class="pln"> ORDER BY</span><span class="pun">(</span><span class="pln">remaining_inventory</span><span class="pun">)</span><span class="pln"> DESC</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------------+---------------------+
| product_name      | remaining_inventory |
+-------------------+---------------------+
| chamomile         |                 110 |
| chai              |                  56 |
| english_breakfast |                  54 |
| matcha            |                  47 |
| lavender          |                  38 |
| oolong            |                  36 |
| jasmine           |                  25 |
| golden teaspoon   |                  15 |
| tea sampler       |                   7 |
| ceramic teapot    |                   7 |
+-------------------+---------------------+
10 rows in set (0.00 sec)
</pre>

<p>
	هذا الاستعلام مفيد عمليًا لأنه يحسب المخزون المتبقي، الأمر الذي يمكن أن يساعد مالكي محل الشاي في التخطيط لشراء طلبيات جديدة في حال وجود نقص في منتج ما.
</p>

<p>
	أمّا للسيناريو التالي، فسنحلل ونقارن مقدار الإيرادات من المبيعات في كل من المتجر الفعلي وعبر الإنترنت:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_48" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT product_name</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">online_units </span><span class="pun">*</span><span class="pln"> product_retail</span><span class="pun">)</span><span class="pln"> AS o</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">store_units </span><span class="pun">*</span><span class="pln"> product_retail</span><span class="pun">)</span><span class="pln"> AS s 
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM product_information</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج

+-------------------+--------+--------+
| product_name      | o      | s      |
+-------------------+--------+--------+
| chamomile         | 390.00 | 285.00 |
| chai              | 243.00 | 153.00 |
| lavender          | 840.00 | 375.00 |
| english_breakfast | 555.00 | 165.00 |
| jasmine           | 690.00 | 247.50 |
| matcha            | 307.50 |  90.00 |
| oolong            | 261.00 |  90.00 |
| tea sampler       | 212.50 | 153.00 |
| ceramic teapot    | 146.25 |  78.00 |
| golden teaspoon   | 335.00 |  90.00 |
+-------------------+--------+--------+
10 rows in set (0.00 sec)
</pre>

<p>
	الآن، لنحسب الإيرادات الإجمالية من المبيعات في كل من المتجر الفعلي وعبر الإنترنت باستخدام الدالة <code>SUM</code> مع عدّة معاملات رياضية، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_50" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT SUM</span><span class="pun">(</span><span class="pln">online_units </span><span class="pun">*</span><span class="pln"> product_retail</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> SUM</span><span class="pun">(</span><span class="pln">store_units </span><span class="pun">*</span><span class="pln"> product_retail</span><span class="pun">)</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> AS total_sales 
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM product_information</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------+
| total_sales |
+-------------+
|     5706.75 |
+-------------+
1 row in set (0.00 sec)
</pre>

<p>
	ولعلّ إجراء هذه الاستعلامات مهم لسببين. الأول هو أنه يسمح لمالكي محل الشاي بتقييم أي المنتجات الأكثر مبيعًا وإعطاء الأولوية لتلك المنتجات عند شراء المزيد في المستقبل. والثاني، أنّه يمكّنهم من تحليل مدى نجاح محل الشاي إجمالًا من خلال مبيعات المنتجات في كل من المتجر الفعلي وعبر الإنترنت.
</p>

<p>
	سنحسب الآن هامش الربح لكل منتج. وهامش الربح لمنتج ما هو مقدار الإيراد الذي يحققه العمل التجاري من كل وحدة مباعة من هذا المنتج. وبالتالي لفهم مقدار الإيراد الإجمالي الذي حققته، يمكنك ضرب عدد الوحدات المُباعة بهامش الربح.
</p>

<p>
	لحساب هامش الربح للمنتجات الفردية في مثالنا، سنطرح سعر التكلفة <code>product_cost</code> من سعر المبيع بالتجزئة <code>product_retail</code> لكل سجل. ثم سنقسّم هذه القيمة على سعر المبيع بالتجزئة للمنتج لحساب نسبة هامش الربح، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_52" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT product_name</span><span class="pun">,</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">product_retail </span><span class="pun">-</span><span class="pln"> product_cost</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> product_retail 
mysql</span><span class="pun">&gt;</span><span class="pln"> AS profit_margin
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM product_information</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------------+-------------+
| product_name      | profit_margin |
+-------------------+-------------+
| chamomile         |    0.317333 |
| chai              |    0.177778 |
| lavender          |    0.317333 |
| english_breakfast |    0.317333 |
| jasmine           |    0.177333 |
| matcha            |    0.177333 |
| oolong            |    0.177778 |
| tea sampler       |    0.294118 |
| ceramic teapot    |    0.282051 |
| golden teaspoon   |    0.600000 |
+-------------------+-------------+
10 rows in set (0.00 sec)
</pre>

<p>
	نلاحظ استنادًا إلى هذا الخرج أنّ المنتج الذي يتمتع بأعلى هامش ربح هو golden teaspoon بنسبة 60%، والأدنى هو لكل من Chai و Jasmine و Matcha و Oolong بنسبة 18%. بالنسبة لمنتج golden teaspoon، فهامش الربح هذا يعني أنه ومن أجل سعر مبيع بالتجزئة قدره 5.00 دولار مع هامش الربح البالغ 60%، سنحصل على 3.00 دولار كإيرادات.
</p>

<p>
	كما يمكننا استخدام دالة التجميع <code>AVG</code> لحساب المتوسط الحسابي لهوامش ربح كافّة منتجات محل الشاي. إذ يلعب هذا المتوسط الحسابي دور المعيار لمالكي محل الشاي لتحديد المنتجات الواقعة تحت هذا الرقم ووضع استراتيجيات لتحسينها:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_54" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT AVG</span><span class="pun">((</span><span class="pln">product_retail </span><span class="pun">-</span><span class="pln"> product_cost</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> product_retail</span><span class="pun">)</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> AS avg_profit_margin 
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM product_information</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------------------+
| avg_profit_margin |
+-------------------+
|      0.2838391151 |
+-------------------+
1 row in set (0.00 sec)
</pre>

<p>
	استنادًا إلى نتيجة الحساب أعلاه نستنتج أنّ المتوسط الحسابي لهوامش الربح للمنتجات في محل بيع الشاي هذا يبلغ 28%.
</p>

<p>
	وبفرض أنّ مالكي محل الشاي قد قرروا بناءً على هذه المعلومات الجديدة زيادة هامش الربح إلى 31% في الربع القادم لأي منتج بهامش ربح أقل من 27%. ولإنجاز الأمر، سنطرح هامش الربح المُستهدف من 1 أي (<code>1-0.31</code>) ومن ثم سنقسّم سعر التكلفة الأصلي لكل من المنتجات المُعادة (ذات هامش الربح الأقل من 27%) على هذه القيمة. فتكون النتيجة النهائية هي السعر الجديد الذي ينبغي بيع المنتج بالتجزئة وفقًا له لتحقيق هامش ربح 31%:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_9843_56" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT product_name</span><span class="pun">,</span><span class="pln"> product_cost </span><span class="pun">/</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">0.31</span><span class="pun">)</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> AS new_retail 
FROM product_information 
WHERE </span><span class="pun">(</span><span class="pln">product_retail </span><span class="pun">-</span><span class="pln"> product_cost</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> product_retail </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0.27</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+--------------+------------+
| product_name | new_retail |
+--------------+------------+
| chai         |  10.724638 |
| jasmine      |   8.942029 |
| matcha       |   8.942029 |
| oolong       |  10.724638 |
+--------------+------------+
4 rows in set (0.00 sec)
</pre>

<p>
	تُظهر هذه النتائج أسعار المبيع بالتجزئة الجديدة اللازمة للمنتجات ذات الأداء الضعيف لتحقيق هامش ربح قدره 31%. يزوّد هذا النوع من تحليل البيانات مالكي محل الشاي بالقدرة على اتخاذ قرارات تجارية حاسمة حول كيفية تحسين إيراداتهم للربع القادم وفهم ما يجب التطلع إليه.
</p>

<h2 id="-3">
	الخلاصة
</h2>

<p>
	يُمكن لاستخدام التعابير الرياضية في SQL أن يتراوح من حل المسائل الحسابية ببساطة كما تفعل باستخدام الآلة الحاسبة، إلى تنفيذ تحليلاتٍ معقدة تستند إلى بيانات من العالم الواقعي والتي يمكن أن تلعب دورًا في صياغة قرارات الأعمال. وبمجرد اتقانك لكيفية التعامل مع المعاملات الرياضية الرئيسية وقواعد ترتيب العمليات، ستجد أمامك عالمًا واسعًا من الإمكانيات الحسابية. وحين ترنو إلى تحليلٍ أعمق لبياناتك، يتيح لك دمج هذه المعاملات مع دوال التجميع الغوص في الأسئلة الافتراضية من قبيل "ماذا لو"، مما يوفر لك رؤًى قيمة قد تكون بمثابة الأساس للتخطيط الاستراتيجي لمستقبل أعمالك.
</p>

<p>
	وللمزيد حول SQL، نشجعك على متابعة <a href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20%D8%AA%D8%B9%D9%84%D9%85%20sql/" rel="">سلسلة تعلم SQL</a> في أكاديمية حسوب.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-mathematical-expressions-in-sql" rel="external nofollow">How To Use Mathematical Expressions and Aggregate Functions in SQL</a> لصاحبه Mark Drake.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق:  <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D9%86%D9%89-%D8%A7%D9%84%D8%AF%D9%85%D8%AC-joins-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2403/" rel="">كيفية استخدام بنى الدمج Joins في لغة الاستعلام البنيوية SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-mysql-r299/" rel="">استخدام الدوال في قواعد بيانات MySQL</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/SQL/functions" rel="external">بعض الدوال المساعدة في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%84%D8%BA%D8%A9-%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-dml-%D8%A7%D9%84%D8%AE%D8%A7%D8%B5%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-sql-r1369/" rel="">لغة معالجة البيانات DML الخاصة بلغة SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%85%D9%88%D8%A7%D8%B6%D9%8A%D8%B9-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-sql-r853/" rel="">مواضيع متقدمة في SQL</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2410</guid><pubDate>Thu, 19 Sep 2024 15:02:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x628;&#x646;&#x649; &#x627;&#x644;&#x62F;&#x645;&#x62C; Joins &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x627;&#x644;&#x627;&#x633;&#x62A;&#x639;&#x644;&#x627;&#x645; &#x627;&#x644;&#x628;&#x646;&#x64A;&#x648;&#x64A;&#x629; SQL</title><link>https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D9%86%D9%89-%D8%A7%D9%84%D8%AF%D9%85%D8%AC-joins-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2403/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_09/----Joins-----SQL.png.0d6751eb5cf3c51cf132530e89270fe6.png" /></p>
<p>
	غالبًا ما تفصل تصاميم قواعد البيانات المعلومات إلى جداول مختلفة بناءً على العلاقات بين بعض نقاط البيانات. ولكن حتى في مثل هذه الحالات، من المحتمل أن نرغب أحيانًا باسترجاع المعلومات من أكثر من جدول في وقتٍ واحد.
</p>

<p>
	إحدى الطرق الشائعة للوصول إلى البيانات من جداول متعددة في عملية واحدة باستخدام لغة الاستعلام البنيوية SQL هي تجميع الجداول باستخدام بنى الدمج <code>JOIN</code>. إذ تُجمّع بنية الدمج <code>JOIN</code> الجداول المنفصلة عن طريق مطابقة السجلات المرتبطة ببعضها البعض من كل جدول مستندةً إلى عمليات الدمج في الجبر العلاقيّ -وهو نظرية تستخدم الهياكل الجبرية لنمذجة البيانات وتحديد الاستعلامات عليها، إذ يُعد إطارًا نظريًا يُستخدم لوصف العمليات على البيانات في قواعد البيانات العلاقية، مثل الدمج والاختيار، ويساعد في تشكيل الأساس الرياضي للغات الاستعلام مثل SQL) -. وعادةً ما تُبنى العلاقة بين الجداول المطلوب دمجها على زوجٍ من الأعمدة - عمود من كل جدول - والتي تتشارك قيمًا مشتركة، كأن نختار <strong>مفتاح خارجي</strong> لجدول مع <strong>مفتاح أساسي</strong> لجدول آخر يُشير إليه المفتاح الخارجي آنف الذكر.
</p>

<p>
	يوضّح هذا المقال كيفية بناء مجموعة متنوعة من استعلامات SQL التي تتضمن بنية الدمج <code>JOIN</code>. كما يُسلط الضوء على أنماط مختلفة من بنى الدمج وكيفية تجميع البيانات من جداول متعددة وكيفية استخدام الأسماء البديلة alias للأعمدة لجعل كتابة عمليات الدمج <code>JOIN</code> أقل تعقيدًا.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	لمتابعة الخطوات في هذا المقال، ستحتاج إلى جهاز كمبيوتر يُشغّل أحد أنواع أنظمة إدارة قواعد البيانات العلاقية RDBMS التي تستخدم <a href="https://academy.hsoub.com/programming/sql/" rel="">SQL</a>. وقد اختبرنا الأوامر البرمجية والأمثلة في هذا المقال مستخدمين البيئة التالية:
</p>

<ul>
	<li>
		خادم عامل على توزيعة أوبنتو، مع مستخدم ذو صلاحيات مسؤول مختلف عن المستخدم الجذر، وجدار حماية مكوّن باستخدام UFW، كما هو موضح في <a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04" rel="external nofollow">دليل الإعداد الأولي للخادم مع الإصدار 20.04 من أوبنتو</a>، كما يمكنك الاطلاع على المقال <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A9-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-%D9%85%D9%86-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A8%D8%A3%D8%A8%D8%B3%D8%B7-%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-r575/" rel="">كيفية تثبيت توزيعة أوبنتو من لينكس بأبسط طريقة</a>.
	</li>
	<li>
		MySQL مثبتة ومؤمنة على الخادم، كما هو موضح في المقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبونتو</a>. وقد نفذنا خطوات هذا المقال باستخدام مستخدم MySQL مختلف عن المستخدم الجذر، مُنشأ وفق الطريقة الموضحة في الخطوة 3 من هذا المقال.
	</li>
</ul>

<p>
	<strong>ملاحظة:</strong> تجدر الإشارة إلى أنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية لها تقديماتها الفريدة من لغة SQL. فبالرغم من كون الأوامر المُقدمة في هذا المقال ستعمل مع معظم هذه الأنظمة، ولكن قد تجد بعض الاختلافات في الصيغة أو الناتج عند تنفيذها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<p>
	وبالعودة إلى مستلزمات العمل، ستحتاج أيضًا إلى قاعدة بيانات وجدول مُحمّل ببعض البيانات التجريبية النموذجية لتتمكن من التدرب على استخدام <a href="https://wiki.hsoub.com/SQL/join" rel="external">عمليات الدمج JOIN.</a> لذا ننصحك بمتابعة القسم القادم الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية للمزيد من التفاصيل حول كيفية إعداد قاعدة بيانات وجدول لاستخدامهما في الأمثلة خلال هذا المقال.
</p>

<h2 id="mysql">
	الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	إذا كان نظام قاعدة بيانات <a href="https://wiki.hsoub.com/SQL" rel="external">SQL</a> الخاص بك يعمل على خادم عن بُعد، اتصل بالخادم مُستخدمًا بروتوكول <a href="https://academy.hsoub.com/devops/security/ssh/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr> </a>من جهازك المحلي على النحو التالي:
</p>

<pre class="ipsCode">$ ssh ssh user@your_server_ip
</pre>

<p>
	ثم افتح واجهة سطر الأوامر في خادم MySQL، مُستبدلًا <code>user</code> باسم حساب مستخدم MySQL الخاص بك:
</p>

<pre class="ipsCode">$ mysql -u user -p
</pre>

<p>
	أنشئ قاعدة بيانات باسم<code>joinsDB</code>:
</p>

<pre class="ipsCode">mysql&gt; CREATE DATABASE joinsDB;
</pre>

<p>
	وبمجرّد إنشاء قاعدة البيانات بنجاح ستحصل على خرج كالتالي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	ولاختيار قاعدة البيانات <code>joinsDB</code>، نفّذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode">mysql&gt; USE joinsDB;
</pre>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	الآن، بعد اختيار قاعدة البيانات <code>joinsDB</code> لننشئ بعض الجداول ضمنها.
</p>

<p>
	لمتابعة الأمثلة المستخدمة في هذا المقال، تخيل أنك تدير مصنعًا وقررت البدء في تتبع المعلومات حول كل من خط الإنتاج، والموظفين في فريق المبيعات، ومبيعات الشركة، وذلك في قاعدة بيانات SQL. فقررت البدء بثلاثة جداول، أولها سيخزن معلومات حول المنتجات، متضمنًا ثلاثة أعمدة:
</p>

<ul>
	<li>
		<code>productID</code>: رقم تعريف كل منتج، معبرًا عنه بنمط البيانات <code>int</code>. سيعمل هذا العمود كمفتاح أساسي للجدول، مما يعني أن كل قيمة فيه ستلعب دور مُعرّف فريد للسجل الخاص بها. وبما أن كل قيمة في المفتاح الأساسي يجب أن تكون فريدة، ستطبّق على هذا العمود القيد <code>UNIQUE</code>.
	</li>
	<li>
		<code>productName</code>: اسم كل منتج، معبرًا عنه باستخدام نمط البيانات <code>varchar</code> بحد أقصى 20 محرفًا.
	</li>
	<li>
		<code>price</code>: سعر كل منتج، معبرًا عنه باستخدام نمط البيانات <code>decimal</code>. وتُحدد القيم في هذا العمود بحد أقصى قدره أربعة أرقام، بواقع رقمين على يمين الفاصلة العشرية. وبالتالي، تتراوح القيم المسموح بها من <code>-99.99</code> إلى <code>99.99</code>.
	</li>
</ul>

<p>
	إذًا، أنشئ جدولًا باسم <code>products</code> يحتوي على هذه الأعمدة الثلاثة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_8" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE products </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> productID </span><span class="kwd">int</span><span class="pln"> UNIQUE</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> productName varchar</span><span class="pun">(</span><span class="lit">20</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> price </span><span class="kwd">decimal</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="lit">2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> PRIMARY KEY </span><span class="pun">(</span><span class="pln">productID</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;);</span></pre>

<p>
	في حين سيخزّن الجدول الثاني معلومات حول الموظفين في فريق مبيعات الشركة. فارتأيت أن هذا الجدول يحتاج أيضًا إلى ثلاثة أعمدة:
</p>

<ul>
	<li>
		<code>empID</code>: مشابه لعمود <code>productID</code>، إذ سيحتوي على مُعرّف فريد لكل موظف في فريق المبيعات مُعبرًا عنه بنمط البيانات <code>int</code>. وبالمثل، سيُطبق على هذا العمود قيد <code>UNIQUE</code> وسيعمل كمفتاح أساسي لجدول الفريق.
	</li>
	<li>
		<code>empName</code>: اسم كل مندوب مبيعات، مُعبرًا عنه باستخدام نمط البيانات <code>varchar</code> بحد أقصى 20 محرفًا.
	</li>
	<li>
		<code>productSpecialty</code>: بفرض أنك قررت تخصيص منتج لكل عضو في فريق المبيعات؛ إذ يمكنه بيع أي منتج تصنعه الشركة ولكن تركيزه العام سيكون على المنتج المُخصّص له. وللإشارة إلى هذا الأمر في الجدول، أنشأنا هذا العمود الذي يحتوي على قيمة <code>productID</code> للمنتج المُخصّص لكل موظف.
	</li>
</ul>

<p>
	ولضمان أنّ عمود <code>productSpecialty</code> لن يتضمّن سوى قيم تُمثّل مُعرّفات صالحة للمنتجات، يمكنك إنشاء قيد مفتاح خارجي عليه بحيث يُشير إلى عمود <code>productID</code> في جدول المنتجات <code>products</code>. يُعد قيد المفتاح الخارجي طريقة لتحديد علاقة بين جدولين، إذ يفرض أن تكون القيم في العمود المُطبّق عليه موجودة بالفعل في العمود المُشار إليه.
</p>

<p>
	يشترط قيد المفتاح الخارجي في تعليمة <code>CREATE TABLE</code> أدناه أن تكون كل قيمة تُضاف إلى عمود <code>productSpecialty</code> من جدول الفريق <code>team</code> موجودة مسبقًا في عمود <code>productID</code> من جدول المنتجات <code>products</code>.
</p>

<p>
	أنشئ جدولًا باسم <code>team</code> يحتوي على هذه الأعمدة الثلاثة:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_10" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE team </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> empID </span><span class="kwd">int</span><span class="pln"> UNIQUE</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> empName varchar</span><span class="pun">(</span><span class="lit">20</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> productSpecialty </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> PRIMARY KEY </span><span class="pun">(</span><span class="pln">empID</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> FOREIGN KEY </span><span class="pun">(</span><span class="pln">productSpecialty</span><span class="pun">)</span><span class="pln"> REFERENCES products </span><span class="pun">(</span><span class="pln">productID</span><span class="pun">)</span><span class="pln">
</span><span class="pun">);</span></pre>

<p>
	أمّا الجدول الأخير فسيتضمّن سجلات مبيعات الشركة. وسيكون لهذا الجدول أربعة أعمدة:
</p>

<ul>
	<li>
		<code>saleID</code>: مشابه لعمودي <code>productID</code> و<code>empID</code>، إذ سيحتوي هذا العمود على مُعرّف فريد لكل عملية بيع مُعبرًا عنه بنمط البيانات <code>int</code>. سنطبق على هذا العمود أيضًا قيد <code>UNIQUE</code> ليلعب دور المفتاح الأساسي لجدول المبيعات <code>sales</code>.
	</li>
	<li>
		<code>quantity</code>: عدد الوحدات من كل منتج مُباع، مُعبرًا عنه بنمط البيانات <code>int</code>.
	</li>
	<li>
		<code>productID</code>: مُعرّف المنتج المُباع، مُعبرًا عنه بنمط البيانات <code>int</code>.
	</li>
	<li>
		<code>salesperson</code>: مُعرّف الموظف الذي أجرى عملية البيع.
	</li>
</ul>

<p>
	وعلى نحوٍ مشابه لعمود <code>productSpecialty</code> من جدول الفريق <code>team</code>، لنُطبّق قيد <code>FOREIGN KEY</code> على كل من عموديّ <code>productID</code> و<code>salesperson</code>. الأمر الذي سيضمن أنّ هذه الأعمدة لن تتضمّن سوى قيم موجودة بالفعل في عمود <code>productID</code> من جدول المنتجات <code>products</code> وعمود <code>empID</code> من جدول الفريق <code>team</code> على التوالي.
</p>

<p>
	لننشئ إذًا جدولًا باسم <code>sales</code> يحتوي على هذه الأعمدة الأربعة:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_12" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE sales </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> saleID </span><span class="kwd">int</span><span class="pln"> UNIQUE</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> quantity </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> productID </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> salesperson </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> PRIMARY KEY </span><span class="pun">(</span><span class="pln">saleID</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> FOREIGN KEY </span><span class="pun">(</span><span class="pln">productID</span><span class="pun">)</span><span class="pln"> REFERENCES products </span><span class="pun">(</span><span class="pln">productID</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> FOREIGN KEY </span><span class="pun">(</span><span class="pln">salesperson</span><span class="pun">)</span><span class="pln"> REFERENCES team </span><span class="pun">(</span><span class="pln">empID</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	ومن ثم املأ جدول المنتجات <code>products</code> ببعض البيانات التجريبية النموذجية عبر تنفيذ عملية <code>INSERT INTO</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_14" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO products
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'widget'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">18.99</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'gizmo'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">14.49</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="str">'thingamajig'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">39.99</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'doodad'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11.50</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'whatzit'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">29.99</span><span class="pun">);</span></pre>

<p>
	ثم املأ جدول الفريق <code>team</code> ببعض البيانات التجريبية النموذجية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_16" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO team
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Florence'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Mary'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Diana'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Betty'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span></pre>

<p>
	املأ جدول المبيعات <code>sales</code> ببعض البيانات النموذجية أيضًا:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_18" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO sales
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">);</span></pre>

<p>
	ونهايةً، تخيّل أن شركتك حققت بعضًا من المبيعات دون مشاركة أحد من فريق المبيعات. لتسجيل هذه المبيعات، أضف ثلاث سجلات إلى جدول المبيعات <code>sales</code> لا تتضمن قيمة لعمود موظف المبيعات <code>salesperson</code> عبر تنفيذ العملية التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_20" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO sales </span><span class="pun">(</span><span class="pln">saleID</span><span class="pun">,</span><span class="pln"> quantity</span><span class="pun">,</span><span class="pln"> productID</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">);</span></pre>

<p>
	بهذا، تغدو مستعدًا لمتابعة باقي المقال وبدء تعلم كيفية دمج الجداول معًا في SQL.
</p>

<h2 id="join">
	فهم صيغة عمليات بنى الدمج JOIN
</h2>

<p>
	يمكن استخدام بنى الدمج <code>JOIN</code> في مجموعة متنوعة من تعليمات SQL، بما في ذلك عمليات التحديث <code>UPDATE</code> والحذف <code>DELETE</code>. ولكن ولأغراض التوضيح، سنستخدم في الأمثلة في هذا المقال استعلامات <a href="https://wiki.hsoub.com/SQL/select" rel="external">SELECT</a> لإظهار كيفية عمل بنى الدمج <code>JOIN</code>.
</p>

<p>
	يُظهر المثال التالي الصيغة العامة لتعليمة <code>SELECT</code> التي تتضمن بنية الدمج <code>JOIN</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_22" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT table1</span><span class="pun">.</span><span class="pln">column1</span><span class="pun">,</span><span class="pln"> table2</span><span class="pun">.</span><span class="pln">column2
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM table1 JOIN table2
mysql</span><span class="pun">&gt;</span><span class="pln"> ON search_condition</span><span class="pun">;</span></pre>

<p>
	تبدأ هذه الصيغة بتعليمة <code>SELECT</code> التي ستعيد عمودين من جدولين منفصلين. لاحظ أنه نظرًا لقدرة بنى <code>JOIN</code> على مقارنة البيانات من عدة جداول، فإن صيغة هذا المثال تُوضّح الجدول المُستهدف لكل عمود بوضع اسم الجدول متبوعًا بنقطة قبل اسم العمود، وهذا ما يُعرف بالإشارة الكاملة والمؤهلة للعمود fully qualified column reference.
</p>

<p>
	يمكنك استخدام الإشارة الكاملة والمؤهلة للعمود في أي عملية على نحوٍ اختياريّ، في حين يغدو استخدامها ضرورةً فقط في العمليات التي يكون فيها عمودان يشتركان في نفس الاسم من جداول مختلفة. ومن الجيد عمومًا استخدامها لدى التعامل مع جداول متعددة، إذ يمكن أن تساعد في جعل عمليات الدمج <code>JOIN</code> أسهل للقراءة والفهم.
</p>

<p>
	وتأتي بنية <code>FROM</code> بعد بنية <code>SELECT</code>. إذ تُحدد بنية <code>FROM</code> في أي استعلام مجموعة البيانات التي ينبغي البحث فيها لإعادة البيانات المطلوبة. ولعلّ الاختلاف الوحيد هنا هو أن بنية <code>FROM</code> تتضمن جدولين مفصولين بالكلمة المفتاحية <code>JOIN</code>. ومن الطرق المفيدة في فهم الاستعلامات لدى كتابتها هي تذكر أنك تختار (<code>SELECT</code>) الأعمدة التي تريد إعادتها من (<code>FROM</code>) الجدول الذي ترغب في الاستعلام عنه.
</p>

<p>
	يلي ذلك بنية <code>ON</code>، والتي تصف كيفية ربط الاستعلام للجدولين معًا عن طريق تحديد شرط بحث. وما شرط البحث سوى مجموعة من التوابع الشرطية أو التعابير القادرة على تقييم فيما إذا كان شرط معيّن "محققًا True" أو "غير محقق False" أو "غير محدد Unknown". وبالتالي، من المفيد فهم عملية الدمج <code>JOIN</code> على أنها عملية تجميع كافة السجلات من جدولين، لتعيد بعد ذلك أي سجلات يُقيم شرط البحث في بنية <code>ON</code> من أجلها على أنه "محقق True".
</p>

<p>
	ولعلّه من المنطقي في بنية <code>ON</code> أن نُضمّن شرط بحث يختبر ما إذا كان عمودين مرتبطين – من قبيل مفتاح خارجي لجدول ما ومفتاح أساسي لجدول آخر يُشير إليه ذلك المفتاح الخارجي - يتضمنان قيمًا متساوية. وهذا ما يُشار إليه أحيانًا بالدمج عند التساوي equi join.
</p>

<p>
	وكمثال على كيفية مطابقة "الدمج عند التساوي" للبيانات من جداول متعددة، لنُنفّذ الاستعلام التالي باستخدام البيانات التجريبية النموذجية المُضافة سابقًا. إذ ستعمل هذه التعليمة على دمج جدولي <code>products</code> و<code>team</code> باستخدام شرط بحث يختبر تطابق القيم في عمودي <code>productID</code> و<code>productSpecialty</code> من الجدولين آنفي الذكر، معيدًا أسم كل عضو من فريق المبيعات واسم المنتج المُخصص له وسعر هذا المنتج:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_24" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT team</span><span class="pun">.</span><span class="pln">empName</span><span class="pun">,</span><span class="pln"> products</span><span class="pun">.</span><span class="pln">productName</span><span class="pun">,</span><span class="pln"> products</span><span class="pun">.</span><span class="pln">price
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM products JOIN team
mysql</span><span class="pun">&gt;</span><span class="pln"> ON products</span><span class="pun">.</span><span class="pln">productID </span><span class="pun">=</span><span class="pln"> team</span><span class="pun">.</span><span class="pln">productSpecialty</span><span class="pun">;</span></pre>

<p>
	وتكون مجموعة نتائج هذا الاستعلام على النحو:
</p>

<pre class="ipsCode">الخرج
+----------+-------------+-------+
| empName  | productName | price |
+----------+-------------+-------+
| Florence | widget      | 18.99 |
| Mary     | doodad      | 11.50 |
| Diana    | thingamajig | 39.99 |
| Betty    | gizmo       | 14.49 |
+----------+-------------+-------+
4 rows in set (0.00 sec)
</pre>

<p>
	ولتوضيح كيفية دمج SQL لهذه الجداول وتشكيل مجموعة النتائج، دعونا نلقي نظرة أقرب على هذه العملية. ومن المهم في هذه المرحلة التنويه إلى كون الخطوات التالية لا نُمثّل بالضبط ما يحدث عند دمج جدولين في نظام إدارة قواعد البيانات، ولكن من المفيد تصوّر عمليات الدمج <code>JOIN</code> وكأنها تتبع خطوات مشابهة لها.
</p>

<p>
	أولًا، يعرض الاستعلام كافة سجلات وأعمدة الجدول الأول ضمن بنية <code>FROM</code>، وهو في حالتنا الجدول <code>products</code>:
</p>

<pre class="ipsCode">مثال على عملية الدمج
+-----------+-------------+-------+
| productID | productName | price |
+-----------+-------------+-------+
|         1 | widget      | 18.99 |
|         2 | gizmo       | 14.49 |
|         3 | thingamajig | 39.99 |
|         4 | doodad      | 11.50 |
|         5 | whatzit     | 29.99 |
+-----------+-------------+-------+
</pre>

<p>
	بعد ذلك، يخضع كل سجل من جدول <code>products</code> للتحليل ليُطابق مع أي سجل من جدول الفريق <code>team</code> حيث تكون قيمة العمود <code>productSpecialty</code> مطابقة لقيمة <code>productID</code> في السجل المعني:
</p>

<pre class="ipsCode">مثال على عملية الدمج
+-----------+-------------+-------+-------+----------+------------------+
| productID | productName | price | empID | empName  | productSpecialty |
+-----------+-------------+-------+-------+----------+------------------+
|         1 | widget      | 18.99 |   1 | Florence |              1 |
|         2 | gizmo       | 14.49 |   4 | Betty   |               2 |
|         3 | thingamajig | 39.99 |   3 | Diana   |               3 |
|         4 | doodad      | 11.50 |   2 | Mary    |               4 |
|         5 | whatzit     | 29.99 |       |       |               |
+-----------+-------------+-------+-------+----------+------------------+
</pre>

<p>
	تُستبعد بعد ذلك كافة السجلات التي لا تتطابق فيها قيمة العمود <code>productSpecialty</code> مع قيمة <code>productID</code>، ثم يُعاد تنظيم الأعمدة وفق ترتيب ورودها ضمن بنية <code>SELECT</code>، مع حذف أي أعمدة لم تُحدد في الاستعلام، وأخيرًا يُعاد فرز السجلات المتبقية وتُقدم كمجموعة النتائج النهائية:
</p>

<pre class="ipsCode">مثال على عملية الدمج
+----------+-------------+-------+
| empName  | productName | price |
+----------+-------------+-------+
| Florence | widget      | 18.99 |
| Mary     | doodad    | 11.50 |
| Diana    | thingamajig | 39.99 |
| Betty    | gizmo     | 14.49 |
+----------+-------------+-------+
4 rows in set (0.00 sec)
</pre>

<p>
	ولعلّ استخدام الدمج عند التساوي هو الأسلوب الأكثر شيوعًا لربط الجداول، في حين من الممكن استخدام عوامل SQL أخرى ضمن شروط بحث بنية <code>ON</code>، من قبيل <code>&lt;</code>، <code>&gt;</code>، <code>LIKE</code>، <code>NOT LIKE</code>، أو حتى <code>BETWEEN</code>. ويُنصح بالانتباه إلى أن استخدام شروط بحث أعقد قد يجعل التنبؤ بالبيانات التي ستظهر في مجموعة النتائج أصعب.
</p>

<p>
	يُمكنك دمج الجداول في معظم تقديمات SQL باستخدام أي مجموعة من الأعمدة، شرط امتلاكها لما يُشار إليه في المعيار القياسي لـ SQL باسم "نمط بيانات مؤهل للدمج". ما يعني عمومًا أنّه من الممكن دمج عمود يحتوي على بيانات رقمية مع أي عمود آخر يحمل بيانات رقمية، وذلك بغض النظر عن أنماط بياناتهما الدقيقة. وبالمثل، من الممكن عادةً دمج أي عمود يحتوي على قيم محرفية مع آخر يحمل بيانات محرفية. ولكن وكما ذكرنا سابقًا، عادةً ما تكون الأعمدة التي نختارها لدمج جدولين هي تلك التي تُشير بالفعل إلى علاقة بين الجداول، من قبيل مفتاح خارجي مع المفتاح الأساسي لجدول آخر والذي يُشير إليه ذلك المفتاح الخارجي.
</p>

<p>
	كما تسمح العديد من تقديمات SQL بدمج الأعمدة التي تحمل نفس الاسم باستخدام الكلمة المفتاحية <code>USING</code> بدلًا من <code>ON</code>. وتبدو صيغة مثل هذه العملية على النحو الآتي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_26" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT table1</span><span class="pun">.</span><span class="pln">column1</span><span class="pun">,</span><span class="pln"> table2</span><span class="pun">.</span><span class="pln">column2
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM table1 JOIN table2
mysql</span><span class="pun">&gt;</span><span class="pln"> USING </span><span class="pun">(</span><span class="pln">related_column</span><span class="pun">);</span></pre>

<p>
	في صيغة هذا المثال، تعادل بنية <code>USING</code> استخدام <code>ON table1.related_column = table2.related_column;</code>.
</p>

<p>
	وبما أنّ لكل من جدولي <code>sales</code> و<code>products</code> عمود باسم <code>productID</code>، فمن الممكن دمجهما عبر مطابقة هذه الأعمدة باستخدام الكلمة المفتاحية <code>USING</code>. وهذا ما يُمثّل مهمّة الأمر التالي، والذي يُعيد <code>saleID</code> لكل عملية بيع، وكمية الوحدات المباعة، واسم كل منتج تم بيعه وسعره. كما يفرز مجموعة النتائج تصاعديًا استنادًا إلى قيمة <code>saleID</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_28" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT sales</span><span class="pun">.</span><span class="pln">saleID</span><span class="pun">,</span><span class="pln"> sales</span><span class="pun">.</span><span class="pln">quantity</span><span class="pun">,</span><span class="pln"> products</span><span class="pun">.</span><span class="pln">productName</span><span class="pun">,</span><span class="pln"> products</span><span class="pun">.</span><span class="pln">price
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM sales JOIN products
mysql</span><span class="pun">&gt;</span><span class="pln"> USING </span><span class="pun">(</span><span class="pln">productID</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> ORDER BY saleID</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+--------+----------+-------------+-------+
| saleID | quantity | productName | price |
+--------+----------+-------------+-------+
|      1 |        7 | widget     | 18.99 |
|      2 |       10 | whatzit     | 29.99 |
|      3 |        8 | gizmo       | 14.49 |
|      4 |        1 | thingamajig | 39.99 |
|      5 |        5 | widget      | 18.99 |
|      6 |        1 | whatzit     | 29.99 |
|      7 |        3 | widget      | 18.99 |
|      8 |        4 | whatzit     | 29.99 |
+--------+----------+-------------+-------+
8 rows in set (0.00 sec)
</pre>

<p>
	إذ قد يعيد نظام قاعدة البيانات أحيانًا ترتيب السجلات بطرق لا يسهل التنبؤ بها عند دمج الجداول. ولعلّ الحل يكون بتضمين جملة <code>ORDER BY</code> كما في المثال أعلاه، ما قد يُساعد في جعل مجموعات النتائج أكثر تناسقًا وسهولة في القراءة.
</p>

<h2 id="-1">
	دمج أكثر من جدولين
</h2>

<p>
	نحتاج أحيانًا إلى دمج البيانات من أكثر من جدولين. إذ يُمكننا دمج أي عدد من الجداول معًا عن طريق تضمين بنى دمج <code>JOIN</code> ضمن بنى <code>JOIN</code> أخرى. وتُمثّل الصيغة التالية مثالًا لحالة دمج ثلاثة جداول:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_30" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT table1</span><span class="pun">.</span><span class="pln">column1</span><span class="pun">,</span><span class="pln"> table2</span><span class="pun">.</span><span class="pln">column2</span><span class="pun">,</span><span class="pln"> table3</span><span class="pun">.</span><span class="pln">column3
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM table1 JOIN table2
mysql</span><span class="pun">&gt;</span><span class="pln"> ON table1</span><span class="pun">.</span><span class="pln">related_column </span><span class="pun">=</span><span class="pln"> table2</span><span class="pun">.</span><span class="pln">related_column
mysql</span><span class="pun">&gt;</span><span class="pln"> JOIN table3
mysql</span><span class="pun">&gt;</span><span class="pln"> ON table3</span><span class="pun">.</span><span class="pln">related_column </span><span class="pun">=</span><span class="pln"> table1_or_2</span><span class="pun">.</span><span class="pln">related_column</span><span class="pun">;</span></pre>

<p>
	تبدأ صيغة البنية <code>FROM</code> في هذا المثال بدمج الجدول <code>table1</code> مع <code>table2</code>. لتبدأ عملية دمج <code>JOIN</code> ثانية بعد بنية <code>ON</code> الخاصة بعملية الدمج الأولى، والتي تدمج مجموعة الجداول المدموجة الأولية السابقة مع الجدول <code>table3</code>. ونلاحظ هنا أنه من الممكن دمج الجدول الثالث مع عمود موجود إما في الجدول الأول أو الثاني.
</p>

<p>
	ولتوضيح الأمر، لنفرض أننا نرغب بمعرفة قيمة الإيرادات المُحققة من مبيعات الموظفين، لكن اهتمامنا منصب فقط على سجلات المبيعات التي تشمل موظفًا باع المنتج الذي يتخصص فيه تحديدًا دون مبيعاته لأي منتج آخر.
</p>

<p>
	وللحصول على هذه المعلومات، يمكننا تنفيذ الاستعلام التالي. والذي يبدأ بدمج جدولي <code>products</code> و<code>sales</code> معًا عن طريق مطابقة عمودي <code>productID</code> في كل منهما. ثم يدمج جدول <code>team</code> مع الجدولين السابقين بمطابقة كل سجل من عملية الدمج الأولية بعمود <code>productSpecialty</code> لكل موظف. بعد ذلك، يعمل الاستعلام على تصفية النتائج باستخدام بنية <code>WHERE</code> بهدف إعادة السجلات التي يكون فيها الموظف، الذي تم تخصيصه للمنتج المُباع، هو نفسه من أتم عملية البيع بالفعل. يتضمن هذا الاستعلام أيضًا بنية <code>ORDER BY</code> التي تفرز النتائج النهائية تصاعديًا استنادًا إلى القيم في عمود <code>saleID</code>.
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_32" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT sales</span><span class="pun">.</span><span class="pln">saleID</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> team</span><span class="pun">.</span><span class="pln">empName</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> products</span><span class="pun">.</span><span class="pln">productName</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">sales</span><span class="pun">.</span><span class="pln">quantity </span><span class="pun">*</span><span class="pln"> products</span><span class="pun">.</span><span class="pln">price</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM products JOIN sales
mysql</span><span class="pun">&gt;</span><span class="pln"> USING </span><span class="pun">(</span><span class="pln">productID</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> JOIN team
mysql</span><span class="pun">&gt;</span><span class="pln"> ON team</span><span class="pun">.</span><span class="pln">productSpecialty </span><span class="pun">=</span><span class="pln"> sales</span><span class="pun">.</span><span class="pln">productID
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE team</span><span class="pun">.</span><span class="pln">empID </span><span class="pun">=</span><span class="pln"> sales</span><span class="pun">.</span><span class="pln">salesperson
mysql</span><span class="pun">&gt;</span><span class="pln"> ORDER BY sales</span><span class="pun">.</span><span class="pln">saleID</span><span class="pun">;</span></pre>

<p>
	نلاحظ أنّه من بين الأعمدة المدرجة في بنية <code>SELECT</code> لهذا الاستعلام يوجد تعبير يضرب قيم عمود <code>quantity</code> الموجود في جدول <code>sales</code> بقيم السعر الموجودة في عمود <code>price</code> بجدول <code>products</code>. والذي يُعيد حاصل ضرب هذه القيم في السجلات المتطابقة.
</p>

<pre class="ipsCode">الخرج
+--------+----------+-------------+-----------------------------------+
| saleID | empName  | productName | (sales.quantity * products.price) |
+--------+----------+-------------+-----------------------------------+
|      1 | Florence | widget      |                            132.93 |
|      3 | Betty    | gizmo       |                            115.92 |
|      4 | Diana    | thingamajig |                             39.99 |
+--------+----------+-------------+-----------------------------------+
3 rows in set (0.00 sec)
</pre>

<p>
	استعرضت كافّة الأمثلة حتى الآن نفس نوع بنية الدمج، ألا وهي: الدمج الداخلي <code>INNER JOIN</code>. وللحصول على نظرة عامّة حول كل من الدمج الداخلي والدمج الخارجي <code>OUTER JOIN</code> وأوجه اختلافهما، تابع قراءة القسم التالي.
</p>

<h2 id="-2">
	عمليات الدمج الداخلي مقابل الدمج الخارجي
</h2>

<p>
	يوجد نوعان رئيسيان من بنى الدمج: الدمج الداخلي <code>INNER</code> والدمج الخارجي <code>OUTER</code>. ويكمن الفارق بين هذين النوعين من الدمج بالبيانات التي يعيدها كل منهما. إذ تُعيد عمليات الدمج الداخلي <code>INNER</code> فقط السجلات التي تمتلك تطابقات من كل جدول مدمج، في حين تُعيد عمليات الدمج الخارجي <code>OUTER</code> السجلات مع أو بدون تطابقات.
</p>

<p>
	استخدمنا في صيغ الأمثلة والاستعلامات من الأقسام السابقة بنى الدمج الداخلي رغم عدم تضمين الكلمة المفتاحية <code>INNER</code> صراحةً في أي منها. فمعظم تطبيقات SQL تعامل أي بنية دمج على أنها <code>INNER</code> ما لم يُذكر خلاف ذلك صراحةً.
</p>

<p>
	تجمع الاستعلامات التي تستخدم الدمج الخارجي <code>OUTER JOIN</code> بين عدة جداول لتعيد السجلات مع أو بدون تطابقات. وهذا الأمر مفيد في الكشف عن السجلات التي تنقصها بعض القيم، أو في الحالات التي تكون فيها التطابقات الجزئية مقبولة.
</p>

<p>
	يمكن تقسيم عمليات الدمج الخارجي <code>OUTER JOIN</code> إلى ثلاثة أنواع فرعية، وهي: الدمج الخارجي الأيسر <code>LEFT OUTER</code>، والدمج الخارجي الأيمن <code>RIGHT OUTER</code>، والدمج الخارجي الكامل <code>FULL OUTER</code>.
</p>

<p>
	يعيد الدمج الخارجي الأيسر، أو ببساطة <a href="https://wiki.hsoub.com/SQL/left_join" rel="external">الدمج الأيسر LEFT JOIN</a> جميع السجلات التي تمتلك تطابقات من كلا الجدولين المدمجين بالإضافة إلى السجلات دون تطابقات من الجدول الموجود على الجانب الأيسر. إذ يعدّ الجدول "الأيسر" في سياق عمليات الدمج ذلك الجدول الأوّل المُحدّد مباشرةً بعد الكلمة المفتاحية <code>FROM</code> وقبل كلمة <code>JOIN</code>. وبالمثل، يعدّ الجدول "الأيمن" هو الجدول الثاني، أو الجدول الذي يلي كلمة <code>JOIN</code> مباشرةً، ويُعيد<a href="https://wiki.hsoub.com/SQL/right_join" rel="external"> الدمج الخارجي الأيمن RIGHT OUTER</a> كافة السجلات ذات التطابقات من الجداول المدمجة بالإضافة إلى كل سجل دون تطابقات من الجدول "الأيمن". في حين يعيد <a href="https://wiki.hsoub.com/SQL/full_join" rel="external">الدمج الخارجي الكامل FULL OUTER JOIN</a> كافة السجلات من كلا الجدولين، بما في ذلك تلك التي لا تمتلك أي مطابقات.
</p>

<p>
	لتوضيح كيف تعيد هذه الأنواع المختلفة من بنى الدمج البيانات، لننفذ الاستعلامات التالية على الجداول المُنشأة في الفقرة السابقة "الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية". مع ملاحظة أن هذه الاستعلامات متطابقة باستثناء أن كل واحد منها يستخدم نوعًا مختلفًا من بنى الدمج <code>JOIN</code>.
</p>

<p>
	يستخدم هذا المثال الأول الدمج الداخلي <code>INNER JOIN</code> لدمج جدولي <code>sales</code> و<code>team</code> معًا بمطابقة عمودي <code>salesperson</code> و<code>empID</code> من كل منهما على التوالي. ونؤكّد مجددًا أنّ الاستعلام يعدّ داخليًا حتى وإن لم نُضمّن الكلمة المفتاحية <code>INNER</code> صراحةً ما لم نشير إلى خلاف ذلك ضمن بنية الاستعلام.
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_34" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT sales</span><span class="pun">.</span><span class="pln">saleID</span><span class="pun">,</span><span class="pln"> sales</span><span class="pun">.</span><span class="pln">quantity</span><span class="pun">,</span><span class="pln"> sales</span><span class="pun">.</span><span class="pln">salesperson</span><span class="pun">,</span><span class="pln"> team</span><span class="pun">.</span><span class="pln">empName 
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM sales JOIN team
mysql</span><span class="pun">&gt;</span><span class="pln"> ON sales</span><span class="pun">.</span><span class="pln">salesperson </span><span class="pun">=</span><span class="pln"> team</span><span class="pun">.</span><span class="pln">empID</span><span class="pun">;</span></pre>

<p>
	نظرًا لأن هذا الاستعلام يستخدم بنية الدمج الداخلي <code>INNER JOIN</code>، فإنه يُعيد فقط السجلات ذات التطابقات من كلا الجدولين:
</p>

<pre class="ipsCode">الخرج
+--------+----------+-------------+----------+
| saleID | quantity | salesperson | empName  |
+--------+----------+-------------+----------+
|      1 |        7 |           1 | Florence |
|      4 |        1 |           3 | Diana    |
|      5 |        5 |           3 | Diana    |
|      2 |       10 |           4 | Betty    |
|      3 |        8 |           4 | Betty    |
+--------+----------+-------------+----------+
5 rows in set (0.00 sec)
</pre>

<p>
	سنستخدم الآن في هذه النسخة من الاستعلام بنية الدمج الخارجي الأيسر <code>LEFT OUTER JOIN</code>، على النحو:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_36" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT sales</span><span class="pun">.</span><span class="pln">saleID</span><span class="pun">,</span><span class="pln"> sales</span><span class="pun">.</span><span class="pln">quantity</span><span class="pun">,</span><span class="pln"> sales</span><span class="pun">.</span><span class="pln">salesperson</span><span class="pun">,</span><span class="pln"> team</span><span class="pun">.</span><span class="pln">empName
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM sales LEFT OUTER JOIN team
mysql</span><span class="pun">&gt;</span><span class="pln"> ON sales</span><span class="pun">.</span><span class="pln">salesperson </span><span class="pun">=</span><span class="pln"> team</span><span class="pun">.</span><span class="pln">empID</span><span class="pun">;</span></pre>

<p>
	وكما هو الحال مع الاستعلام السابق، يعيد هذا الاستعلام أيضًا كافّة القيم ذات التطابقات من كلا الجدولين. لكنه يعيد أيضًا أي قيم من الجدول "الأيسر" (وهو الجدول <code>sales</code> في هذه الحالة) التي لا تمتلك أي تطابقات مع الجدول "الأيمن" (<code>team</code>). ولكن وبما أنّ هذه السجلات في الجدول الأيسر لا تمتلك تطابقات في الجدول الأيمن، فتُعاد القيمة الفارغة <code>NULL</code> بدلًا من القيم غير المتطابقة من الجدول الأيمن.
</p>

<pre class="ipsCode">الخرج
+--------+----------+-------------+----------+
| saleID | quantity | salesperson | empName  |
+--------+----------+-------------+----------+
|      1 |        7 |           1 | Florence |
|      2 |       10 |           4 | Betty    |
|      3 |        8 |           4 | Betty    |
|      4 |        1 |           3 | Diana    |
|      5 |        5 |           3 | Diana    |
|      6 |        1 |        NULL | NULL     |
|      7 |        3 |        NULL | NULL     |
|      8 |        4 |        NULL | NULL     |
+--------+----------+-------------+----------+
8 rows in set (0.00 sec)
</pre>

<p>
	أمّا الآن فسنستخدم بنية الدمج الأيمن <code>RIGHT JOIN</code>، على النحو:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_38" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT sales</span><span class="pun">.</span><span class="pln">saleID</span><span class="pun">,</span><span class="pln"> sales</span><span class="pun">.</span><span class="pln">quantity</span><span class="pun">,</span><span class="pln"> sales</span><span class="pun">.</span><span class="pln">salesperson</span><span class="pun">,</span><span class="pln"> team</span><span class="pun">.</span><span class="pln">empName
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM sales RIGHT JOIN team
mysql</span><span class="pun">&gt;</span><span class="pln"> ON sales</span><span class="pun">.</span><span class="pln">salesperson </span><span class="pun">=</span><span class="pln"> team</span><span class="pun">.</span><span class="pln">empID</span><span class="pun">;</span></pre>

<p>
	لاحظ أننا اكتفينا باستخدام التعليمة <code>RIGHT JOIN</code> بدلًا من <code>RIGHT OUTER JOIN</code> في بنية الدمج من هذا الاستعلام. فكما هو الحال من حيث عدم اشتراط استخدام الكلمة المفتاحية <code>INNER</code> لتحديد بنية دمج داخلي <code>INNER JOIN</code>، فإن كلمة <code>OUTER</code> تُطبّق تلقائيًا بدورها بمجرد كتابة <code>LEFT JOIN</code> أو <code>RIGHT JOIN</code>.
</p>

<p>
	نتيجة هذا الاستعلام هي العكس تمامًا من الاستعلام السابق، بمعنى أنها تعيد كافّة السجلات ذات التطابقات من كلا الجدولين، والسجلات دون تطابقات من الجدول "الأيمن" فقط:
</p>

<pre class="ipsCode">الخرج:
+--------+----------+-------------+----------+
| saleID | quantity | salesperson | empName  |
+--------+----------+-------------+----------+
|      1 |        7 |           1 | Florence |
|   NULL |     NULL |        NULL | Mary     |
|      4 |        1 |           3 | Diana    |
|      5 |        5 |           3 | Diana    |
|      2 |       10 |           4 | Betty    |
|      3 |        8 |           4 | Betty    |
+--------+----------+-------------+----------+
6 rows in set (0.00 sec)
</pre>

<p>
	<strong>ملاحظة</strong>: انتبه لكون MySQL لا تدعم بنى الدمج الخارجي الكامل <code>FULL OUTER JOIN</code>. ولتوضيح البيانات التي كان من الممكن أن يُعيدها هذا الاستعلام في حال استخدام بنية <code>FULL OUTER JOIN</code>، إليك كيف ستبدو مجموعة النتائج في قاعدة بيانات PostgreSQL:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_40" style=""><span class="typ">Joinsdb</span><span class="pun">=#</span><span class="pln"> SELECT sales</span><span class="pun">.</span><span class="pln">saleID</span><span class="pun">,</span><span class="pln"> sales</span><span class="pun">.</span><span class="pln">quantity</span><span class="pun">,</span><span class="pln"> sales</span><span class="pun">.</span><span class="pln">salesperson</span><span class="pun">,</span><span class="pln"> team</span><span class="pun">.</span><span class="pln">empName
</span><span class="typ">Joinsdb</span><span class="pun">=#</span><span class="pln"> FROM sales FULL OUTER JOIN team
</span><span class="typ">Joinsdb</span><span class="pun">=#</span><span class="pln"> ON sales</span><span class="pun">.</span><span class="pln">salesperson </span><span class="pun">=</span><span class="pln"> team</span><span class="pun">.</span><span class="pln">empID</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
saleid | quantity | salesperson | empname  
--------+----------+-------------+----------
      1 |        7 |           1 | Florence
      2 |       10 |           4 | Betty
      3 |        8 |           4 | Betty
      4 |        1 |           3 | Diana
      5 |        5 |           3 | Diana
      6 |        1 |             | 
      7 |        3 |             | 
      8 |        4 |             | 
        |          |             | Mary
(9 rows)
</pre>

<p>
	نلاحظ من النتائج أعلاه أنّ الدمج الكامل <code>FULL JOIN</code> يُعيد كافّة السجلات من كلا الجدولين بما في ذلك تلك التي لا تمتلك تطابقات.
</p>

<h2 id="-3">
	تسمية الجداول والأعمدة بأسماء بديلة في بنى الدمج
</h2>

<p>
	لدى دمج جداول ذات أسماء طويلة أو وصفية للغاية، قد تصبح مهمة كتابة الإشارة الكاملة والمؤهلة للعمود مرهقة. ولتجنب ذلك، يجد المستخدمون أحيانًا أنّه من المفيد تزويد أسماء الجداول أو الأعمدة بأسماء بديلة أقصر.
</p>

<p>
	الأمر الذي يمكننا تنفيذه في SQL بإلحاق تعريف الجدول في بنية <code>FROM</code> بكلمة <code>AS</code> المفتاحية، ليأتي بعدها الاسم البديل الذي اخترناه، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_42" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT t1</span><span class="pun">.</span><span class="pln">column1</span><span class="pun">,</span><span class="pln"> t2</span><span class="pun">.</span><span class="pln">column2
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM table1 AS t1 JOIN table2 AS t2
mysql</span><span class="pun">&gt;</span><span class="pln"> ON t1</span><span class="pun">.</span><span class="pln">related_column </span><span class="pun">=</span><span class="pln"> t2</span><span class="pun">.</span><span class="pln">related_column</span><span class="pun">;</span></pre>

<p>
	استخدمنا في صيغة هذا المثال<a href="https://wiki.hsoub.com/SQL/alias" rel="external"> الأسماء البديلة</a> في بنية <code>SELECT</code> رغم أننا لم نُعرف هذه الأسماء إلّا في بنية <code>FROM</code>. وهذا الأمر ممكن لأنّ ترتيب تنفيذ الاستعلامات في SQL يبدأ ببنية <code>FROM</code> أولًا. قد تكون هذه الطريقة مربكة، ولكن من المفيد تذكرها والتفكير في الأسماء البديلة قبل البدء في كتابة الاستعلام.
</p>

<p>
	كمثال، لننفذ الاستعلام التالي الذي يدمج جدولي <code>sales</code> و<code>products</code> ويزودهما بالأسماء البديلة <code>S</code> و<code>P</code> على التوالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_44" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT S</span><span class="pun">.</span><span class="pln">saleID</span><span class="pun">,</span><span class="pln"> S</span><span class="pun">.</span><span class="pln">quantity</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> P</span><span class="pun">.</span><span class="pln">productName</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">P</span><span class="pun">.</span><span class="pln">price </span><span class="pun">*</span><span class="pln"> S</span><span class="pun">.</span><span class="pln">quantity</span><span class="pun">)</span><span class="pln"> AS revenue 
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM sales AS S JOIN products AS P
mysql</span><span class="pun">&gt;</span><span class="pln"> USING </span><span class="pun">(</span><span class="pln">productID</span><span class="pun">);</span></pre>

<p>
	نلاحظ أن هذا المثال يُنشئ أيضًا اسمًا بديلًا ثالثًا وهو <code>revenue</code> لحاصل ضرب قيم العمود <code>quantity</code> من الجدول <code>sales</code> بقيمها المقابلة في عمود <code>price</code> من الجدول <code>products</code>. ولن يظهر هذا الاسم سوى ضمن مجموعة النتائج كاسم عمود، لكنه مهم لتوضيح المعنى أو الغرض من نتائج الاستعلام:
</p>

<pre class="ipsCode">الخرج
+--------+----------+-------------+---------+
| saleID | quantity | productName | revenue |
+--------+----------+-------------+---------+
|      1 |        7 | widget      |  132.93 |
|      2 |       10 | whatzit     |  299.90 |
|      3 |        8 | gizmo       |  115.92 |
|      4 |        1 | thingamajig |   39.99 |
|      5 |        5 | widget      |   94.95 |
|      6 |        1 | whatzit     |   29.99 |
|      7 |        3 | widget      |   56.97 |
|      8 |        4 | whatzit     |  119.96 |
+--------+----------+-------------+---------+
8 rows in set (0.00 sec)
</pre>

<p>
	وتجدر الملاحظة إلى أنّ استخدام كلمة <code>AS</code> عند تحديد الأسماء البديلة اختياري من الناحية التقنية، إذ من الممكن أيضًا كتابة المثال السابق على النحو:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_8935_46" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT S</span><span class="pun">.</span><span class="pln">saleID</span><span class="pun">,</span><span class="pln"> S</span><span class="pun">.</span><span class="pln">quantity</span><span class="pun">,</span><span class="pln"> P</span><span class="pun">.</span><span class="pln">productName</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">P</span><span class="pun">.</span><span class="pln">price </span><span class="pun">*</span><span class="pln"> S</span><span class="pun">.</span><span class="pln">quantity</span><span class="pun">)</span><span class="pln"> revenue 
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM sales S JOIN products P
mysql</span><span class="pun">&gt;</span><span class="pln"> USING </span><span class="pun">(</span><span class="pln">productID</span><span class="pun">);</span></pre>

<p>
	ورغم كون كلمة <code>AS</code> ليست ضرورية لتحديد اسم بديل، إلّا أنّ تضمينها يعدّ من الممارسات الجيدة. إذ يُمكن أن يساعد في الحفاظ على وضوح غرض الاستعلام وتحسين مقروئيته.
</p>

<h2 id="-4">
	الخلاصة
</h2>

<p>
	باطلاعك على هذا المقال، اكتسبت المعرفة حول كيفية استخدام <a href="https://wiki.hsoub.com/SQL/join" rel="external">عمليات الدمج </a><code>JOIN</code> لدمج جداول متفرقة ضمن مجموعة نتائج استعلام واحدة.
</p>

<p>
	ومن المفترض أن تعمل الأوامر المشروحة في هذا المقال مع أي نظام لإدارة قواعد البيانات يستخدم SQL. لكن تذكر أن لكل قاعدة بيانات SQL تقديمها الخاص للغة، لذا ينبغي مراجعة التوثيق الرسمي لنظام إدارة قواعد البيانات الخاص بك للحصول على وصف أكثر تفصيلاً لكل أمر فيها ومجموعة خياراته الكاملة. وللمزيد حول SQL، نشجعك على متابعة <a href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20%D8%AA%D8%B9%D9%84%D9%85%20sql/" rel="">سلسلة تعلم SQL</a> في أكاديمية حسوب.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-joins-in-sql" rel="external nofollow">How To Use Joins in SQL</a> لصاحبه Mark Drake.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D8%A7%D9%84%D8%A8%D8%AF%D9%84-wildcards-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2401/" rel="">كيفية استخدام محارف البدل في لغة الاستعلام البنيوية SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%AF%D9%85%D8%AC-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r849/" rel="">الدمج بين الجداول في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%B9%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r588/" rel="">الاستعلام عن البيانات في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A3%D9%87%D9%85-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85%D8%A7%D8%AA-queries-%D9%81%D9%8A-mysql-r295/" rel="">مدخل إلى أهم الاستعلامات (queries) في MySQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%85%D9%88%D8%A7%D8%B6%D9%8A%D8%B9-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-sql-r853/" rel="">مواضيع متقدمة في SQL</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2403</guid><pubDate>Wed, 11 Sep 2024 15:07:02 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x645;&#x62D;&#x627;&#x631;&#x641; &#x627;&#x644;&#x628;&#x62F;&#x644; Wildcards &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x627;&#x644;&#x627;&#x633;&#x62A;&#x639;&#x644;&#x627;&#x645; &#x627;&#x644;&#x628;&#x646;&#x64A;&#x648;&#x64A;&#x629; SQL</title><link>https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AD%D8%A7%D8%B1%D9%81-%D8%A7%D9%84%D8%A8%D8%AF%D9%84-wildcards-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2401/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_09/--------SQL.png.a2b1a7c77eda12af97c8b19c93dbaee4.png" /></p>
<p>
	تسمح لغة الاستعلام البنيوية SQL باستخدام مجموعة متنوعة من محارف البدل (Wildcards) كما هو الحال في العديد من لغات البرمجة. وتُعرّف محارف البدل بأنها محارف مواضع مؤقتة خاصة قادرة على تمثيل محرف آخر أو قيمة أخرى واحدة أو أكثر، وهي ميزة مفيدة في SQL، إذ تمكننا من البحث في قاعدة البيانات عن البيانات دون الحاجة لمعرفة القيم الدقيقة المخزنة فيها.
</p>

<p>
	سيتناول هذا المقال كيفية الاستعلام عن البيانات باستخدام محارف البدل المحددة في SQL.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	لمتابعة الخطوات في هذا المقال، ستحتاج إلى جهاز كمبيوتر يُشغّل أحد أنواع أنظمة إدارة قواعد البيانات العلاقية RDBMS التي تستخدم <a href="https://academy.hsoub.com/programming/sql/" rel="">SQL</a>. وقد اختبرنا الأوامر البرمجية والأمثلة في هذا المقال مستخدمين البيئة التالية:
</p>

<ul>
	<li>
		خادم عامل على الإصدار 20.04 من توزيعة أوبنتو، مع مستخدم ذو صلاحيات مسؤول مختلف عن المستخدم الجذر، وجدار حماية مكوّن باستخدام UFW، كما هو موضح في <a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04" rel="external nofollow">دليل الإعداد الأولي للخادم مع الإصدار 20.04 من أوبنتو</a>، كما يمكنك الاطلاع على المقال <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A9-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-%D9%85%D9%86-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A8%D8%A3%D8%A8%D8%B3%D8%B7-%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-r575/" rel="">كيفية تثبيت توزيعة أوبنتو من لينكس بأبسط طريقة</a>.
	</li>
	<li>
		MySQL مثبتة ومؤمنة على الخادم، كما هو موضح في المقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبونتو</a>. وقد نفذنا خطوات هذا المقال باستخدام مستخدم MySQL مختلف عن المستخدم الجذر، مُنشأ وفق الطريقة الموضحة في الخطوة 3 من هذا المقال.
	</li>
</ul>

<p>
	<strong>ملاحظة:</strong> تجدر الإشارة إلى أنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية لها تقديماتها الفريدة من لغة SQL. فبالرغم من كون الأوامر المُقدّمة في هذا المقال ستعمل مع معظم هذه الأنظمة، ولكن قد تجد بعض الاختلافات في الصيغة الدقيقة أو الناتج عند تنفيذها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<p>
	وبالعودة إلى مستلزمات العمل، ستحتاج أيضًا إلى قاعدة بيانات وجدول مُحمّل ببعض البيانات التجريبية النموذجية لتتمكن من التدرب على استخدام محارف البدل. لذا ننصحك بقراءة القسم القادم <strong>الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية</strong> للمزيد من التفاصيل حول كيفية إنشاء قاعدة بيانات وجدول لاستخدامهما في الأمثلة خلال هذا المقال.
</p>

<h2 id="mysql">
	الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	إذا كان نظام قاعدة بيانات SQL الخاص بك يعمل على خادم بعيد، اتصل بهذا الخادم مُستخدمًا بروتوكول <a href="https://academy.hsoub.com/devops/security/ssh/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr></a> من جهازك المحلي على النحو:
</p>

<pre class="ipsCode">$ ssh ssh user@your_server_ip
</pre>

<p>
	ثم افتح واجهة سطر الأوامر في خادم MySQL، مُستبدلًا <code>user</code> باسم حساب مستخدم MySQL الخاص بك:
</p>

<pre class="ipsCode">$ mysql -u user -p
</pre>

<p>
	أنشئ قاعدة بيانات باسم<code>wildcardsDB</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_516_7" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE DATABASE wildcardsDB</span><span class="pun">;</span></pre>

<p>
	وبمجرّد إنشاء قاعدة البيانات بنجاح ستحصل على خرجٍ كالتالي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	ولاختيار قاعدة البيانات <code>wildcardsDB</code>، نفّذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_516_9" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> USE wildcardsDB</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	الآن، وبعد اختيار قاعدة البيانات، يمكننا إنشاء جدول ضمنها باستخدام الأمر التالي. وبفرض أننا نرغب في إنشاء جدول باسم <code>user_profiles</code> لتخزين معلومات ملفات المستخدمين لتطبيق ما، سيحتوي هذا الجدول على الأعمدة الخمسة التالية:
</p>

<ul>
	<li>
		<code>user_id</code>: مُعرّف كل مستخدم مُعبّرًا عنه بنمط بيانات الأعداد الصحيحة <code>int</code>. سيكون هذا العمود هو المفتاح الأساسي للجدول، إذ ستلعب كل قيمة فيه دور مُعرّف فريد لسجلها الموافق.
	</li>
	<li>
		<code>name</code>: اسم كل مستخدم، معبرًا عنه باستخدام نمط البيانات <code>varchar</code> بحد أقصى 30 محرفًا.
	</li>
	<li>
		<code>email</code>: سيحتوي هذا العمود على عناوين البريد الإلكتروني للمستخدمين، معبرًا عنها أيضًا باستخدام نمط البيانات <code>varchar</code> ولكن بحد أقصى 40 محرفًا.
	</li>
	<li>
		<code>birthdate</code>: سيحتوي هذا العمود على تاريخ ميلاد كل مستخدم باستخدام نمط البيانات <code>date</code>.
	</li>
	<li>
		<code>quote</code>: الاقتباس المفضل لكل مستخدم. وبهدف توفير عدد كافٍ من المحارف للاقتباسات، سنستخدم في هذا العمود أيضًا نمط البيانات <code>varchar</code>، ولكن بحد أقصى 300 محرف.
	</li>
</ul>

<p>
	سنشغّل الآن الأمر التالي لإنشاء هذا الجدول التجريبي النموذجي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_516_11" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE user_profiles </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> user_id </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> name varchar</span><span class="pun">(</span><span class="lit">30</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> email varchar</span><span class="pun">(</span><span class="lit">40</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> birthdate date</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> quote varchar</span><span class="pun">(</span><span class="lit">300</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> PRIMARY KEY </span><span class="pun">(</span><span class="pln">user_id</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	والآن سنملأ جدولنا الفارغ ببعضٍ من البيانات النموذجية التجريبية، على النحو:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_516_13" style=""><span class="pln">INSERT INTO user_profiles
VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Kim'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bd_eyes@example.com'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1945-07-20'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'"Never let the fear of striking out keep you from playing the game." -Babe Ruth'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Ann'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'cantstandrain@example.com'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1947-04-27'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'"The future belongs to those who believe in the beauty of their dreams." -Eleanor Roosevelt'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Phoebe'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'poetry_man@example.com'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1950-07-17'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'"100% of the people who give 110% do not understand math." -Demitri Martin'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Jim'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'u_f_o@example.com'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1940-08-13'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'"Whoever is happy will make others happy too." -Anne Frank'</span><span class="pun">),</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Timi'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'big_voice@example.com'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1940-08-04'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'"It is better to fail in originality than to succeed in imitation." -Herman Melville'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Taeko'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'sunshower@example.com'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1953-11-28'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'"You miss 100% of the shots you don\'t take." -Wayne Gretzky'</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Irma'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'soulqueen_NOLA@example.com'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1941-02-18'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'"You have brains in your head. You have feet in your shoes. You can steer yourself any direction you choose." -Dr. Seuss'</span><span class="pun">),</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Iris'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'our_town@example.com'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1961-01-05'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'"You will face many defeats in life, but never let yourself be defeated." -Maya Angelou'</span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0
</pre>

<p>
	وبذلك، تغدو جاهزًا لمتابعة باقي المقال وبدء التعلم حول كيفية استخدام محارف البدل للاستعلام عن البيانات في SQL.
</p>

<h2 id="sql">
	الاستعلام عن البيانات باستخدام محارف البدل في SQL
</h2>

<p>
	كما ذكرنا في المقدمة، محارف البدل عبارة عن محارف مواضع مؤقتة خاصة، تتميز بقدرتها على تمثيل محرف مختلف أو قيمة مختلفة واحدة أو أكثر. ولا يوجد سوى نمطين فقط من محارف البدل المُعرّفة في SQL، وهما:
</p>

<ul>
	<li>
		الشرطة السفلية <code>_</code> حيث تمثل عند استخدامها كمحرف بدل محرفًا واحدًا. فيتطابق مثلًا النمط <code>s_mmy</code> مع الأسماء <code>sammy</code> أو <code>sbmmy</code> أو <code>sxmmy</code>.
	</li>
	<li>
		محرف إشارة النسبة المئوية <code>% </code>الذي يمثل صفرًا أو أكثر من المحارف. إذ يتطابق مثلًا النمط <code>s%mmy</code> مع كل من الأسماء التالية <code>sammy أو saaaaaammy</code> أو <code>smmy</code>.
	</li>
</ul>

<p>
	ملاحظة: تُستخدم محارف البدل هذه حصريًا ضمن بنية <a href="https://wiki.hsoub.com/SQL/where" rel="external"><code>WHERE</code></a> من الاستعلام، مقترنةً بأحد عاملي <code><a href="https://wiki.hsoub.com/SQL/like" rel="external">LIKE</a></code> أو <code>NOT LIKE</code>.
</p>

<p>
	لتوضيح كيفية استخدامها مع البيانات النموذجية المذكورة في قسم مستلزمات العمل، لنفترض أننا نعلم بوجود مستخدم في جدول <code>user_profiles</code> يحمل اسمًا مكونًا من ثلاثة أحرف ينتهي بالمقطع "im"، لكننا غير متأكدين من هويته. بما أن الغموض يكمن فقط في المحرف الأول من الاسم، يمكننا تشغيل الاستعلام التالي الذي يستخدم محرف البدل <code>_</code> للكشف عن هوية هذا المستخدم، على النحو:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_516_16" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM user_profiles WHERE name LIKE </span><span class="str">'_im'</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+---------+------+---------------------+------------+---------------------------------------------------------------------------------+
| user_id | name | email               | birthdate  | quote                                                                           |
+---------+------+---------------------+------------+---------------------------------------------------------------------------------+
|       1 | Kim  | bd_eyes@example.com | 1945-07-20 | "Never let the fear of striking out keep you from playing the game." -Babe Ruth |
|       4 | Jim  | u_f_o@example.com   | 1940-08-13 | "Whoever is happy will make others happy too." -Anne Frank                      |
+---------+------+---------------------+------------+---------------------------------------------------------------------------------+
2 rows in set (0.00 sec)
</pre>

<p>
	<strong>ملاحظة</strong>: ألحقنا الكلمة المفتاحية <code>SELECT</code> بعلامة النجمة (<code>*</code>) مباشرةً في هذا المثال، وهي الطريقة المختصرة في SQL للإشارة إلى "جميع الأعمدة".
</p>

<p>
	تُستخدم علامات النجمة <code>*</code>كمحارف بدل في بعض التطبيقات ولغات البرمجة، وحتى بعض تقديمات SQL، إذ تُمثّل صفرًا أو أكثر من المحارف، كما هو الحال تمامًا مع علامة النسبة المئوية المستخدمة في هذا المثال. ولكن النجمة في المثال أعلاه ليست بمحرف بدل، إذ أنّها تُمثّل أمرًا محددًا - ألا وهو: كل الأعمدة في جدول <code>user_profiles</code> - ولا تعبّر عن محرف أو أكثر غير معروف.
</p>

<p>
	وبالعودة إلى موضوعنا، تجدر الإشارة إلى أنّه لعامل <code>NOT LIKE</code> تأثير معاكس لعامل <code>LIKE</code>. فبدلاً من إرجاع كل سجل يطابق نمط محرف البدل، سيعيد كل سجل لا يطابق ذلك النمط. ولتوضيح الأمر، لنعد تنفيذ الاستعلام السابق مجددًا مستبدلين العامل <code>LIKE</code> بالعامل <code>NOT LIKE</code>، على النحو:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_516_18" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM user_profiles WHERE name NOT LIKE </span><span class="str">'_im'</span><span class="pun">;</span></pre>

<p>
	في هذه المرة، تُستثنى من مجموعة النتائج كل السجلات التي لا تتطابق قيمتها في عمود الاسم <code>name</code> مع النمط <code>_im</code>.
</p>

<pre class="ipsCode">الخرج
+---------+--------+----------------------------+------------+--------------------------------------------------------------------------------------------------------------------------+
| user_id | name   | email                      | birthdate  | quote                                                                                                                    |
+---------+--------+----------------------------+------------+--------------------------------------------------------------------------------------------------------------------------+
|       2 | Ann    | cantstandrain@example.com  | 1947-04-27 | "The future belongs to those who believe in the beauty of their dreams." -Eleanor Roosevelt                              |
|       3 | Phoebe | poetry_man@example.com     | 1950-07-17 | "100% of the people who give 110% do not understand math." -Demitri Martin                                               |
|       5 | Timi   | big_voice@example.com      | 1940-08-04 | "It is better to fail in originality than to succeed in imitation." -Herman Melville                                     |
|       6 | Taeko  | sunshower@example.com      | 1953-11-28 | "You miss 100% of the shots you don't take." -Wayne Gretzky                                                              |
|       7 | Irma   | soulqueen_NOLA@example.com | 1941-02-18 | "You have brains in your head. You have feet in your shoes. You can steer yourself any direction you choose." -Dr. Seuss |
|       8 | Iris   | our_town@example.com       | 1961-01-05 | "You will face many defeats in life, but never let yourself be defeated." -Maya Angelou                                  |
+---------+--------+----------------------------+------------+--------------------------------------------------------------------------------------------------------------------------+
6 rows in set (0.00 sec)
</pre>

<p>
	كمثال آخر، بفرض أننا نعلم بأنّ أسماء عدة مستخدمين مُدرجين في قاعدة البيانات لدينا تبدأ بحرف "I"، ولكننا لا نستطيع تذكرهم جميعًا. فمن الممكن في هذه الحالة استخدام محرف البدل <code>%</code> لعرض قائمة بجميع هؤلاء المستخدمين، كما هو موضح بالاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_516_20" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT user_id</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> email FROM user_profiles WHERE name LIKE </span><span class="str">'I%'</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+---------+------+----------------------------+
| user_id | name | email                      |
+---------+------+----------------------------+
|       7 | Irma | soulqueen_NOLA@example.com |
|       8 | Iris | our_town@example.com       |
+---------+------+----------------------------+
2 rows in set (0.00 sec)
</pre>

<p>
	وتجدر الإشارة إلى أنّ عاملي <code>LIKE</code> و<code>NOT LIKE</code> في MySQL لا يتميزان بالحساسية تجاه حالة الأحرف افتراضيًا. ما يعني أنّ الاستعلام السابق سيعيد نفس النتائج حتى لو لم نكتب حرف "I" ضمن نمط محرف البدل في حالته الكبيرة:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_516_22" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT user_id</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> email FROM user_profiles WHERE name LIKE </span><span class="str">'i%'</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+---------+------+----------------------------+
| user_id | name | email                      |
+---------+------+----------------------------+
|       7 | Irma | soulqueen_NOLA@example.com |
|       8 | Iris | our_town@example.com       |
+---------+------+----------------------------+
2 rows in set (0.00 sec)
</pre>

<p>
	ولا بدّ من الانتباه إلى أنّ محارف البدل تختلف عن التعابير النمطية <a href="https://academy.hsoub.com/devops/linux/%d9%85%d9%82%d8%af%d9%85%d8%a9-%d9%81%d9%8a-%d8%a7%d9%84%d8%aa%d8%b9%d8%a7%d8%a8%d9%8a%d8%b1-%d8%a7%d9%84%d9%86%d9%85%d8%b7%d9%8a%d8%a9-regular-expressions-r63/" rel="">regular expressions</a>. فعادةً ما يُشير محرف البدل إلى محرف مُستخدم في مطابقة الأنماط العامّة glob-style pattern matching، وهو أسلوب لمطابقة الأنماط يُستخدم في أنظمة الأوامر وملفات التشغيل لتحديد مجموعات من أسماء الملفات بأنماط معينة. في حين تعتمد التعابير النمطية على لغة نمطية regular language لمطابقة أنماط السلاسل النصية، إذ تُعرّف اللغة النمطية بأنها نوع من اللغات الشكلية التي يمكن وصفها أو التعبير عنها باستخدام التعابير النمطية. تمتاز هذه اللغات ببنية بسيطة نسبيًا يمكن تحليلها باستخدام آلات حالة محدودة Finite State Machines، وهي نماذج رياضية بسيطة تصف كيف يمكن التحول من حالة إلى أخرى استجابةً لبعض المدخلات.
</p>

<h2 id="-1">
	تجاهل محارف البدل
</h2>

<p>
	قد نرغب في بعض الأحيان بالبحث عن مدخلات تحتوي على أحد محارف البدل الخاصة بلغة SQL. وهنا يمكننا استخدام محرف هروب، والذي سيوجّه SQL لتجاهل وظيفة الإبدال لكل من محرفي البدل <code>%</code> أو <code>_</code> واعتبارهما كنص عادي.
</p>

<p>
	على سبيل المثال، بفرض أنّنا نعلم بوجود مستخدمين في قاعدة البيانات لديهم اقتباس مفضل يتضمن علامة النسبة المئوية، ولكننا غير متأكدين من هويتهم على وجه التحديد. لذا سنحاول تنفيذ الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_516_24" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT user_id</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> quote FROM user_profiles WHERE quote LIKE </span><span class="str">'%'</span><span class="pun">;</span></pre>

<p>
	إلّا أنّ هذا الاستعلام لن يكون مفيدًا إلى ذلك الحد، إذ ستلعب علامة النسبة المئوية دور البديل لأي سلسلة من المحارف وبأي طول، وستعيد بالتالي كافّة سجلات الجدول:
</p>

<pre class="ipsCode">الخرج
+---------+--------+--------------------------------------------------------------------------------------------------------------------------+
| user_id | name   | quote                                                                                                                    |
+---------+--------+--------------------------------------------------------------------------------------------------------------------------+
|       1 | Kim    | "Never let the fear of striking out keep you from playing the game." -Babe Ruth                                          |
|       2 | Ann    | "The future belongs to those who believe in the beauty of their dreams." -Eleanor Roosevelt                              |
|       3 | Phoebe | "100% of the people who give 110% do not understand math." -Demitri Martin                                               |
|       4 | Jim    | "Whoever is happy will make others happy too." -Anne Frank                                                               |
|       5 | Timi   | "It is better to fail in originality than to succeed in imitation." -Herman Melville                                     |
|       6 | Taeko  | "You miss 100% of the shots you don't take." -Wayne Gretzky                                                              |
|       7 | Irma   | "You have brains in your head. You have feet in your shoes. You can steer yourself any direction you choose." -Dr. Seuss |
|       8 | Iris   | "You will face many defeats in life, but never let yourself be defeated." -Maya Angelou                                  |
+---------+--------+--------------------------------------------------------------------------------------------------------------------------+
8 rows in set (0.00 sec)
</pre>

<p>
	لتجاهل علامة النسبة المئوية، يمكننا وضع خط مائل خلفي (<code>\</code>) قبلها، وهو محرف الهروب الافتراضي في MySQL، على النحو:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_516_26" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM user_profiles WHERE quote LIKE </span><span class="str">'\%'</span><span class="pun">;</span></pre>

<p>
	إلا أنّ هذا الاستعلام لن يكون مفيدًا بدوره، نظرًا لكونه يحدد أنّ محتويات عمود الاقتباس <code>quote</code> يجب أن تتكون من علامة نسبة مئوية <strong>فقط</strong>. وبالتالي، ستكون مجموعة النتائج فارغة:
</p>

<pre class="ipsCode">الخرج
Empty set (0.00 sec)
</pre>

<p>
	لتصحيح الأمر، لا بدّ من تضمين محارف بدل علامة النسبة المئوية في بداية ونهاية نمط البحث الذي يتبع عامل <code>LIKE</code>، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_516_28" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT user_id</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> quote FROM user_profiles WHERE quote LIKE </span><span class="str">'%\%%'</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+---------+--------+----------------------------------------------------------------------------+
| user_id | name   | quote                                                                      |
+---------+--------+----------------------------------------------------------------------------+
|       3 | Phoebe | "100% of the people who give 110% do not understand math." -Demitri Martin |
|       6 | Taeko  | "You miss 100% of the shots you don't take." -Wayne Gretzky                |
+---------+--------+----------------------------------------------------------------------------+
2 rows in set (0.00 sec)
</pre>

<p>
	يعمل الخط المائل العكسي في هذا الاستعلام على تجاهل علامة النسبة المئوية الثانية فقط، في حين تبقى كل من الأولى والثالثة كمحارف بدل. وبالتالي، سيعيد هذا الاستعلام كل سجل يحتوي عمود الاقتباس <code>quote</code> فيه على علامة نسبة مئوية واحدة على الأقل.
</p>

<p>
	وتجدر الملاحظة إلى أنّه من الممكن تحديد محارف هروب مخصصة باستخدام بنية <code>ESCAPE</code>، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_516_30" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT user_id</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> email FROM user_profiles WHERE email LIKE </span><span class="str">'%@_%'</span><span class="pln"> ESCAPE </span><span class="str">'@'</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+---------+--------+----------------------------+
| user_id | name   | email                      |
+---------+--------+----------------------------+
|       1 | Kim    | bd_eyes@example.com        |
|       3 | Phoebe | poetry_man@example.com     |
|       4 | Jim    | u_f_o@example.com          |
|       5 | Timi   | big_voice@example.com      |
|       7 | Irma   | soulqueen_NOLA@example.com |
+---------+--------+----------------------------+
5 rows in set (0.00 sec)
</pre>

<p>
	يُعرّف هذا الاستعلام علامة <code>@</code> كمحرف هروب، ويعيد كل سجل يحتوي عمود البريد الإلكتروني <code>email</code> فيه على شرطة سفلية واحدة على الأقل. أمّا إذا أزلنا بنية <code>ESCAPE</code>، فإنّ الاستعلام سيعيد كافة سجلات الجدول، نظرًا لأن كل منها يتضمّن علامة <code>@</code> واحدة على الأقل.
</p>

<h2 id="-2">
	الخلاصة
</h2>

<p>
	باطلاعك على هذا المقال، اكتسبت المعرفة حول كيفية استخدام محارف البدل وتجاهلها في قواعد بيانات SQL التي تتعامل مع محارف البدل.
</p>

<p>
	ومن المفترض أن تعمل الأوامر المشروحة في هذا المقال مع أي نظام لإدارة قواعد البيانات يستخدم SQL. لكن تذكر أن لكل قاعدة بيانات SQL تقديمها الخاص للغة، لذا ينبغي مراجعة التوثيق الرسمي لنظام إدارة قواعد البيانات الخاص بك للحصول على وصف أكثر تفصيلاً لكل أمر فيها ومجموعة خياراته الكاملة.
</p>

<p>
	وللمزيد حول SQL، نشجعك على متابعة <a href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20%D8%AA%D8%B9%D9%84%D9%85%20sql/" rel="">سلسلة تعلم SQL</a> في أكاديمية حسوب.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-wildcards-in-sql" rel="external nofollow">How To Use Wildcards in SQL</a> لصاحبه Mark Drake.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D8%A7%D9%84%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D9%88%D8%A7%D9%84%D8%B9%D8%A7%D9%85%D9%84-is-null-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2270/" rel="">كيفية استخدام عوامل المقارنة والعامل IS NULL في لغة الاستعلام البنيوية SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B9%D9%88%D8%A7%D9%85%D9%84-between-%D9%88-in-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2263/" rel="">كيفية استخدام عوامل BETWEEN و IN في لغة الاستعلام البنيوية SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r2224/" rel="">كيفية إنشاء وإدارة الجداول في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%B9%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r588/" rel="">الاستعلام عن البيانات في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D9%88%D8%A7%D9%84%D8%AA%D9%86%D9%82%D9%8A%D8%A8-%D9%88%D8%A7%D9%84%D8%AA%D8%B1%D8%B4%D9%8A%D8%AD-%D9%81%D9%8A-sql-r848/" rel="">البحث والتنقيب والترشيح في SQL</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2401</guid><pubDate>Fri, 06 Sep 2024 15:01:07 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x639;&#x648;&#x627;&#x645;&#x644; &#x627;&#x644;&#x645;&#x642;&#x627;&#x631;&#x646;&#x629; &#x648;&#x627;&#x644;&#x639;&#x627;&#x645;&#x644; IS NULL &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x627;&#x644;&#x627;&#x633;&#x62A;&#x639;&#x644;&#x627;&#x645; &#x627;&#x644;&#x628;&#x646;&#x64A;&#x648;&#x64A;&#x629; SQL</title><link>https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D8%A7%D9%84%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D9%88%D8%A7%D9%84%D8%B9%D8%A7%D9%85%D9%84-is-null-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2270/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_03/-----IS-NULL-----SQL(1).png.67261dedc71ec6a6272e56151500bd9f.png" /></p>
<p>
	 نشرح في هذا المقال المزيد عن طريقة كتابة شروط بنى <code>WHERE</code> التي تتحكم في السجلات التي ستتأثر بعملية معينة. إذ تُحدّد هذه البنى معايير يجب أن تنطبق على كل سجل ليتأثر بالعملية، والتي تُعرف<strong> بشروط البحث</strong>. إذ تتألّف شروط البحث من تابع شرطي واحد أو أكثر، وهي تعابير خاصة تُقييم لتكون "صحيحة True" أو "خاطئة False" أو "غير معروفة Unknown"، ولا تؤثر العمليات إلا على السجلات التي يُقيّم فيها كل تابع شرطي على أنّه "صحيح True" ضمن بنية <code>WHERE</code>.
</p>

<p>
	تمكّن SQL المستخدمين من صياغة شروط بحث تضم أنماط متعددة من التوابع الشرطية، كل منها يستخدم عاملًا خاصًا لتقييم السجلات وسنركز في مقال اليوم على نمطين من التوابع الشرطية والعوامل المستخدمة فيها: عوامل المقارنة وعامل <code>IS NULL</code>.
</p>

<p>
	على الرغم من أن هذا المقال يعتمد تحديدًا على تعليمات <code>SELECT</code> في أمثلته، إلّا أنّ المفاهيم الموضحة هنا قابلة للتطبيق على عدد من العمليات في SQL. إذ تعد بنى <code>WHERE</code> وشروط البحث المرتبطة بها عناصر أساسية في عمليات التحديث <code>UPDATE</code> والحذف <code>DELETE</code> على وجه الخصوص.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	لمتابعة الخطوات في هذا المقال، ستحتاج إلى جهاز حاسوب يُشغّل أحد أنواع أنظمة إدارة قواعد البيانات العلاقية RDBMS التي تستخدم <a href="https://academy.hsoub.com/programming/sql/" rel="">SQL</a>. وقد اختبرنا الأوامر البرمجية والأمثلة في هذا المقال مستخدمين البيئة التالية:
</p>

<ul>
	<li>
		خادم عامل على توزيعة أوبنتو، مع مستخدم ذو صلاحيات مسؤول مختلف عن المستخدم الجذر، وجدار حماية مكوّن باستخدام UFW، كما هو موضح في مقال <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A9-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-%D9%85%D9%86-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A8%D8%A3%D8%A8%D8%B3%D8%B7-%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-r575/" rel="">كيفية تثبيت توزيعة أوبنتو من لينكس بأبسط طريقة</a>.
	</li>
	<li>
		MySQL مثبتة ومؤمنة على الخادم، كما هو موضح في المقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبونتو</a>. وقد نفذنا خطوات هذا المقال باستخدام مستخدم MySQL مختلف عن المستخدم الجذر، مُنشأ وفق الطريقة الموضحة في الخطوة 3 من هذا المقال.
	</li>
	<li>
		ستحتاج أيضًا إلى قاعدة بيانات بجداول مُحمّلة ببعض البيانات التجريبية النموذجية لتتمكن من التدرب على كتابة استعلامات تتضمّن بنى <code>WHERE</code>. لذا ننصحك بقراءة القسم القادم <strong>الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية</strong> للمزيد من التفاصيل حول كيفية الاتصال بخادم MySQL وإنشاء قاعدة البيانات التجريبية المُستخدمة في الأمثلة خلال هذا المقال.
	</li>
</ul>

<p>
	<strong>ملاحظة:</strong> تجدر الإشارة إلى أنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية لها تقديماتها الفريدة من لغة SQL. فبالرغم من كون الأوامر المُقدمة في هذا المقال ستعمل مع معظم هذه الأنظمة، ولكن قد تجد بعض الاختلافات في الصيغة أو الخرج عند تنفيذها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<h2 id="mysql">
	الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	إذا كان نظام قاعدة بيانات SQL الخاص بك يعمل على خادم عن بُعد، اتصل بالخادم مُستخدمًا بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> من جهازك المحلي على النحو:
</p>

<pre class="ipsCode">$ ssh ssh user@your_server_ip
</pre>

<p>
	ثم افتح واجهة سطر الأوامر في خادم MySQL، مُستبدلًا <code>user</code> باسم حساب مستخدم MySQL الخاص بك:
</p>

<pre class="ipsCode">$ mysql -u user -p
</pre>

<p>
	الآن ومن نافذة سطر الأوامر، أنشئ قاعدة بيانات باسم <code>comparison_null_db</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5390_7" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE DATABASE comparison_null_db</span><span class="pun">;</span></pre>

<p>
	وبمجرّد إنشاء قاعدة البيانات بنجاح ستحصل على خرجٍ كالتالي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	ولاختيار قاعدة البيانات <code>comparison_null_db</code>، نفّذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5390_20" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> USE comparison_null_db</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	بعد اختيار قاعدة البيانات <code>comparison_null_db</code>، أنشئ جدولًا ضمنها.
</p>

<p>
	لمتابعة الأمثلة المستخدمة في هذا المقال، تخيل أنك قررت مع مجموعة من أصدقائك أن تصبحوا أنشط بدنيًا وأن تبدأوا في ممارسة الجري. ولتحقيق ذلك، حدّد كل من أصدقائك هدفًا شخصيًا لعدد الأميال التي يرغب في قطعها خلال الشهر المقبل. فقررت تتبع أهداف عدد الأميال التي عيّنها أصدقائك، بالإضافة إلى عدد الأميال التي قطعوها فعليًا ضمن جدول SQL يحتوي على الأعمدة الثلاثة التالية:
</p>

<ul>
	<li>
		<code>name</code>: أسماء كل من أصدقائك، معبرًا عنها باستخدام نمط البيانات <code>varchar</code> بحد أقصى 15 محرفًا.
	</li>
	<li>
		<code>goal</code>: هدف كل صديق لعدد الأميال التي أمل في قطعها خلال الشهر الماضي، معبرًا عنه كعدد صحيح باستخدام نمط بيانات <code>int</code>.
	</li>
	<li>
		<code>result</code>: عدد الأميال التي قطعها كل صديق في نهاية الشهر، معبرًا عنه أيضًا كعدد صحيح باستخدام نمط بيانات <code>int</code>.
	</li>
</ul>

<p>
	نفّذ التعليمة <code>CREATE TABLE</code> التالية لإنشاء جدول باسم <code>running_goals</code> يحتوي على هذه الأعمدة الثلاثة:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5390_11" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE running_goals </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> name varchar</span><span class="pun">(</span><span class="lit">15</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> goal </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> result </span><span class="kwd">int</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج:
Query OK, 0 rows affected (0.012 sec)
</pre>

<p>
	الآن سنملأ جدول <code>running_goals</code> ببعض البيانات التجريبية. نفذ العملية <code>INSERT INTO</code> التالية لإضافة سبع سجلات من البيانات تمثل سبعة من أصدقائك وأهدافهم للجري ونتائجهم:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5390_13" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO running_goals
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Michelle'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">55</span><span class="pun">,</span><span class="pln"> </span><span class="lit">48</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Jerry'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">25</span><span class="pun">,</span><span class="pln"> NULL</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Milton'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">45</span><span class="pun">,</span><span class="pln"> </span><span class="lit">52</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Bridget'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">40</span><span class="pun">,</span><span class="pln"> NULL</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Wanda'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">30</span><span class="pun">,</span><span class="pln"> </span><span class="lit">38</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Stewart'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">35</span><span class="pun">,</span><span class="pln"> NULL</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Leslie'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">40</span><span class="pun">,</span><span class="pln"> </span><span class="lit">44</span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج:
Query OK, 7 rows affected (0.004 sec)
Records: 7 Duplicates: 0 Warnings: 0
</pre>

<p>
	لاحظ أن قيمة العمود <code>result</code> لثلاثة من هذه السجلات هي قيم فارغة <code>NULL</code>. إذ سنفرض كمثال أنّ هؤلاء الأصدقاء لم يُبلغوا بعد عن عدد الأميال التي قطعوها خلال الشهر الماضي لذلك أدخلنا قيم نتائجهم على أنها <code>NULL</code>.
</p>

<p>
	وبذلك غدوتَ جاهزًا للمتابعة بباقي أقسام المقال وبدء تعلم كيفية استخدام عوامل المقارنة والعامل <code><a href="https://wiki.hsoub.com/SQL/is_null" rel="external">IS NULL</a></code> في SQL.
</p>

<h2 id="where">
	فهم التوابع الشرطية الخاصة ببنية WHERE
</h2>

<p>
	يمكنك إدراج بنية <code><a href="https://wiki.hsoub.com/SQL/where" rel="external">WHERE</a></code> بعد بنية <code>FROM</code> في أي عملية SQL تقرأ البيانات من جدول موجود أصلًا وذلك لتحديد البيانات التي ستتأثر بهذه العملية، إذ تُعرّف بنى <code>WHERE</code> شرط بحث، وأي سجل لا يحقق هذا الشرط سيُستثنى من العملية، على عكس السجلات التي تحققه.
</p>

<p>
	وما شرط البحث سوى مجموعة من التوابع الشرطية أو التعبيرات القادرة على تقييم تعبير قيمة واحد أو أكثر لتعيد نتيجة تكون  إمّا "صحيحة True" أو "خاطئة False" أو "غير محددة Unknown". يُعرّف تعبير القيمة في لغة SQL - والذي يُشار إليه أحيانًا باسم التعبير ذو القيمة المفردة - بأنّه أي تعبير يُعيد قيمة واحدة. يمكن أن يكون تعبير القيمة عبارة عن قيمة مُصنفة النوع من قبيل سلسلة نصية أو قيمة عددية، أو تعبير رياضي. ولكن غالبًا ما يكون على الأقل أحد تعبيرات القيمة في شرط بحث بنية <code>WHERE</code> هو اسم عمود من الجدول المُشار إليه ضمن بنية <code>FROM</code> للعملية.
</p>

<p>
	ولدى تشغيل استعلامات SQL المُتضمّنة لبنية <code>WHERE</code>، سيطبق نظام إدارة <a href="https://academy.hsoub.com/devops/servers/databases/%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-database/" rel="">قاعدة البيانات</a> DBMS شرط البحث على كل سجل في الجدول المنطقي المحدد في بنية <code>FROM</code>. ليُعيد فقط السجلات التي يُقيّم من أجلها كل تابع شرطي في شرط البحث على أنه "محقق TRUE".
</p>

<p>
	يُحدّد معيار SQL ثمانية عشر نمطًا مختلفًا من التوابع الشرطية، وعلى الرغم من أنّها لا تتوفر كاملةً في كافّة أنظمة إدارة قواعد البيانات العلاقية RDBMS. إليك خمسة من أكثر أنواع التوابع الشرطية شيوعًا في شروط البحث والعوامل المستخدمة في كل منها:
</p>

<p>
	<strong>المقارنة</strong>: تقارن التوابع الشرطية المقارنِة بين تعبيري قيمة، وفي معظم الاستعلامات يكون أحد هذين التعبيرين هو اسم عمود. وعوامل المقارنة الستة هي كالتالي:
</p>

<ul>
	<li>
		<code>=</code>: يختبر ما إذا كانت القيمتان متساويتين.
	</li>
	<li>
		<code>&lt;&gt;</code>: يختبر ما إذا كانت القيمتان غير متساويتين.
	</li>
	<li>
		<code>&lt;</code>: يختبر ما إذا كانت القيمة الأولى أقل من الثانية.
	</li>
	<li>
		<code>&gt;</code>: يختبر ما إذا كانت القيمة الأولى أكبر من الثانية.
	</li>
	<li>
		<code>&lt;=</code>: يختبر ما إذا كانت القيمة الأولى أقل من أو تساوي الثانية.
	</li>
	<li>
		<code>&gt;=</code>: يختبر ما إذا كانت القيمة الأولى أكبر من أو تساوي الثانية.
	</li>
</ul>

<p>
	<strong>القيم الفارغة</strong>: تختبر التوابع الشرطية التي تستخدم عامل <code>IS NULL</code> ما إذا كانت القيم في عمود معين فارغة.
</p>

<p>
	<strong>النطاق</strong>: تستخدم التوابع الشرطية النطاقية عامل <code>BETWEEN</code> لاختبار ما إذا كان تعبير قيمة ما يقع بين تعبيري قيمة آخرين.
</p>

<p>
	<strong>العضوية</strong>: يستخدم هذا النوع من التوابع الشرطية عامل <code>IN</code> لاختبار ما إذا كانت قيمة ما تُمثّل عضوًا في مجموعة معينة.
</p>

<p>
	<strong>تطابق الأنماط</strong>: تستخدم توابع مطابقة الأنماط الشرطية عامل <code>LIKE</code> لاختبار ما إذا كانت قيمة ما تطابق نمطًا نصيًا يحتوي على محارف بدل.
</p>

<p>
	وكما ذكرنا في المقدمة، يركز هذا المقال على توضيح <span ipsnoautolink="true">كيفية استخدام عوامل المقارنة والعامل IS NULL في SQL لتصفية البيانات</span>. فإذا كنت ترغب في معرفة كيفية استخدام العوامل <code>BETWEEN</code> و<code>IN</code> في SQL مع التوابع الشرطية النطاقية وتلك الخاصة بالعضوية، ننصحك بقراءة مقالنا السابق حول <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B9%D9%88%D8%A7%D9%85%D9%84-between-%D9%88-in-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2263/" rel="">كيفية استخدام عوامل BETWEEN وIN في لغة الاستعلام البنيوية SQL</a>، أمّا إذا كنت ترغب في معرفة كيفية استخدام عامل <code>LIKE</code> لتصفية البيانات بناءً على نمط نصي يحتوي على محارف بدل، فننصحك بقراءة المقال كيفية استخدام محارف البدل في SQL. وللاطلاع على المزيد حول بنى <code>WHERE</code> عمومًا، ننصحك بقراءة مقالنا حول <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D9%86%D9%89-where-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2258/" rel="">كيفية استخدام بنى WHERE في لغة الاستعلام البنيوية SQL</a>.
</p>

<h2 id="where-1">
	التوابع الشرطية للمقارنة في بنية WHERE
</h2>

<p>
	تستخدم التوابع الشرطية للمقارنة في بنية <code>WHERE</code> واحدًا من ستة عوامل مقارنة وذلك لمقارنة تعبير قيمة مع آخر، والتي تتبع عادةً الصيغة العامة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5390_25" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT column_list
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM table_name
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE column_name OPERATOR value_expression</span><span class="pun">;</span></pre>

<p>
	ويأتي بعد كلمة <code>WHERE</code> المفتاحية تعبير قيمة، والذي يكون في معظم عمليات SQL عبارة عن اسم عمود. إنّ توفير اسم عمود كتعبير قيمة في شرط البحث يخبر نظام إدارة قواعد البيانات العلاقية (RDBMS) باستخدام قيمة كل سجل من العمود المحدد كجزء من تعبير القيمة للتكرار الخاص بكل سجل في شرط البحث. وبما أن نظام قاعدة البيانات يطبق شروط البحث على كل سجل تباعًا، سيعمل عامل المقارنة بالتالي على تضمين أو تصفية السجل بناءً على ما إذا كان شرط البحث "صحيحًا True” لقيمته في العمود المحدد.
</p>

<p>
	للتوضيح، لننفّذ الاستعلام التالي. والذي سيُعيد قيم من عمودي <code>name</code> و<code>goal</code> في جدول <code>running_goals</code> لأي سجلات تكون فيها قيمة <code>goal</code> تساوي <code>40</code> وذلك وفق التابع الشرطي للمقارنة المُستخدم ضمن بنية <code>WHERE</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5390_27" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> goal
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM running_goals
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE goal </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">;</span></pre>

<p>
	وقد كان في مثالنا هدف اثنين فقط من الأصدقاء قطع مسافة 40 ميلًا بالتحديد خلال الشهر الماضي، لذا سيعيد الاستعلام هذين السجلين فقط:
</p>

<pre class="ipsCode">الخرج:
+---------+------+
| name    | goal |
+---------+------+
| Bridget |   40 |
| Leslie  |   40 |
+---------+------+
2 rows in set (0.00 sec)
</pre>

<p>
	لتوضيح كيفية عمل عوامل المقارنة الأخرى، لنُنفّذ الاستعلامات التالية المُشابهة للمثال السابق باستثناء كون كل واحد منها يستخدم عامل مقارنة مختلف.
</p>

<p>
	يختبر العامل <code>&lt;&gt;</code> ما إذا كانت قيمتان غير متساويتين، لذا سيُعيد هذا الاستعلام كل سجل تكون فيه قيمة <code>goal</code> <strong>غير</strong> مساوية للقيمة <code>40</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5390_29" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> goal
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM running_goals
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE goal </span><span class="pun">&lt;&gt;</span><span class="pln"> </span><span class="lit">40</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج:
+----------+------+
| name     | goal |
+----------+------+
| Michelle |   55 |
| Jerry    |   25 |
| Milton   |   45 |
| Wanda    |   30 |
| Stewart  |   35 |
+----------+------+
5 rows in set (0.00 sec)
</pre>

<p>
	يختبر العامل <code>&lt;</code> ما إذا كان تعبير القيمة الأول أقل من تعبير القيمة الثاني:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5390_31" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> goal
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM running_goals
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE goal </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">40</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج:
+---------+------+
| name    | goal |
+---------+------+
| Jerry   |   25 |
| Wanda   |   30 |
| Stewart |   35 |
+---------+------+
3 rows in set (0.00 sec)
</pre>

<p>
	يختبر العامل <code>&gt;</code> ما إذا كان تعبير القيمة الأول أكبر من تعبير القيمة الثاني:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5390_33" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> goal
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM running_goals
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE goal </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">40</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج:
+----------+------+
| name     | goal |
+----------+------+
| Michelle |   55 |
| Milton   |   45 |
+----------+------+
2 rows in set (0.00 sec)
</pre>

<p>
	يختبر العامل <code>&lt;=</code> ما إذا كانت القيمة الأولى أقل من أو تساوي القيمة الثانية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5390_35" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> goal
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM running_goals
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE goal </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج:
+---------+------+
| name    | goal |
+---------+------+
| Jerry   |   25 |
| Bridget |   40 |
| Wanda   |   30 |
| Stewart |   35 |
| Leslie  |   40 |
+---------+------+
5 rows in set (0.00 sec)
</pre>

<p>
	يختبر العامل <code>&gt;=</code> ما إذا كانت القيمة الأولى أكبر من أو تساوي القيمة الثانية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5390_37" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> goal
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM running_goals
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE goal </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج:
+----------+------+
| name     | goal |
+----------+------+
| Michelle |   55 |
| Milton   |   45 |
| Bridget  |   40 |
| Leslie   |   40 |
+----------+------+
4 rows in set (0.00 sec)
</pre>

<p>
	تتعامل عوامل المساواة (<code>=</code>) وعدم المساواة (<code>&lt;&gt;</code>) مع القيم من نمط السلاسل النصية كما هو متوقّع. فمثلًا، يُعيد الاستعلام التالي كل سجل تكون فيه قيمة عمود الاسم <code>name</code> تساوي السلسلة النصية 'Leslie':
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5390_39" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM running_goals
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Leslie'</span><span class="pun">;</span></pre>

<p>
	ونظرًا لوجود سجل واحد فقط في الجدول حيث قيمة العمود <code>name</code> تساوي "Leslie"، يقتصر الاستعلام على إعادة هذا السجل فقط.
</p>

<pre class="ipsCode">الخرج:
+--------+
| name   |
+--------+
| Leslie |
+--------+
1 row in set (0.00 sec)
</pre>

<p>
	عند مقارنة القيم من نمط السلاسل النصية، تقيّم كل من عوامل المقارنة <code>&lt;</code>، <code>&gt;</code>، <code>&lt;=</code>، و <code>&gt;=</code> كيفية ترتيب السلاسل أبجديًا. بمعنى آخر، إذا كتبنا تابعًا شرطيًا يختبر ما إذا كانت سلسلة نصية "أقل" من أخرى، فنحن نختبر ما إذا كانت السلسلة الأولى تأتي قبل الثانية أبجديًا. وبالمثل، إذا كان التابع الشرطي يختبر ما إذا كانت سلسلة نصية "أكبر" من أخرى، فنحن نختبر ما إذا كانت السلسلة الأولى تأتي بعد الثانية أبجديًا.
</p>

<p>
	لتوضيح الفكرة، لنُنفّذ الاستعلام التالي الذي سيُعيد قيم كل من العمودين <code>name</code> و<code>goal</code> لكل سجل تكون قيمة <code>name</code> "أقل" من الحرف 'M'. بمعنى آخر، سيُقيّم شرط البحث على أنّه "صحيح True" لكل سجل تأتي قيمته في العمود <code>name</code> أبجديًا قبل الحرف 'M':
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5390_41" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM running_goals
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE name </span><span class="pun">&lt;</span><span class="pln"> </span><span class="str">'M'</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج:
+---------+
| name    |
+---------+
| Jerry   |
| Bridget |
| Leslie  |
+---------+
3 rows in set (0.00 sec)
</pre>

<p>
	نلاحظ أن مجموعة النتائج هذه لا تتضمن الاسمين <code>Michelle</code> أو <code>Milton</code>. السبب في ذلك هو أن الحرف "M" يأتي أبجديًا قبل أي سلسلة نصية تبدأ بالحرف "M" وتحتوي على أكثر من حرف، لذا يُستثنى هذين الاسمين من مجموعة النتائج.
</p>

<h2 id="-1">
	التوابع الشرطية للقيمة الفارغة
</h2>

<p>
	في SQL، <code>NULL</code> عبارة كلمة محجوزة تُستخدم لتمثيل القيم المفقودة أو غير المعروفة. فالقيمة الفارغة Null هي حالة، وليست قيمة فعلية؛ إذ أنّها <strong>لا</strong> تُمثّل الصفر أو السلسلة النصية الفارغة.
</p>

<p>
	يمكنك استخدام عامل <code>IS NULL</code> لاختبار ما إذا كان تعبير قيمة معين فارغًا:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5390_44" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> 
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE column_name IS NULL
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span></pre>

<p>
	سيتتبع نظام قاعدة البيانات في هذا النوع من التوابع الشرطية قيمة كل سجل من العمود المحدد، مُقيّمًا إياها ما إذا كانت فارغة أم لا. فإذا كانت القيمة في العمود فارغة بالفعل، سيُقيّم شرط البحث على أنّه "صحيح True" لذلك السجل وبالتالي سيُضمّن في مجموعة النتائج.
</p>

<p>
	للتوضيح، لنُنفّذ الاستعلام التالي الذي يعيد عموديّ <code>name</code> و<code>result</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5390_46" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> result
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM running_goals
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE result IS NULL</span><span class="pun">;</span></pre>

<p>
	يختبر شرط البحث في بنية <code>WHERE</code> لهذا الاستعلام ما إذا كانت قيمة العمود <code>result</code> لكل سجل فارغة. فإذا كان الأمر كذلك، يُقيّم التابع الشرطي على أنّه "صحيح True" ويتم تضمين السجل في مجموعة النتائج:
</p>

<pre class="ipsCode">الخرج
+---------+--------+
| name    | result |
+---------+--------+
| Jerry   |   NULL |
| Bridget |   NULL |
| Stewart |   NULL |
+---------+--------+
3 rows in set (0.00 sec)
</pre>

<p>
	سُجلّت تلك القيم على أنها فارغة <code>NULL</code> لأن ثلاثة من أصدقائك لم يُبلغوا بعد عن عدد الأميال التي قطعوها خلال الشهر الماضي. ونتيجةً لذلك، يُقيّم شرط البحث في الاستعلام بأنه "صحيح True" لهذه السجلات الثلاثة، ولذا فهي السجلات الوحيدة المُضمنة في مجموعة النتائج.
</p>

<h2 id="-2">
	الخلاصة
</h2>

<p>
	باطلاعك على هذا المقال، اكتسبت المعرفة حول كيفية استخدام عوامل المقارنة والعامل <code>IS NULL</code> في بنى <code>WHERE</code> لتحديد السجلات التي ستتأثر بعملية معينة في SQL. ومن المفترض أن تعمل الأوامر المشروحة في هذا المقال مع أي نظام لإدارة قواعد البيانات يستخدم SQL. لكن تذكر أن لكل قاعدة بيانات SQL تقديمها الخاص للغة، لذا ينبغي مراجعة التوثيق الرسمي لنظام إدارة قواعد البيانات الخاص بك للحصول على وصف أكثر تفصيلاً لكل أمر فيها ومجموعة خياراته الكاملة.
</p>

<p>
	وللمزيد حول SQL، نشجعك على متابعة <a href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20%D8%AA%D8%B9%D9%84%D9%85%20sql/" rel="">سلسلة تعلم SQL</a> في أكاديمية حسوب.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-comparison-and-is-null-operators-in-sql" rel="external nofollow" target="_blank">How To Use Comparison and IS NULL Operators in SQL</a> لصاحبه Mark Drake.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B9%D9%88%D8%A7%D9%85%D9%84-between-%D9%88-in-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2263/" rel="">كيفية استخدام عوامل BETWEEN و IN في لغة الاستعلام البنيوية SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B9%D9%88%D8%A7%D9%85%D9%84-between-%D9%88-in-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2263/" rel="">لماذا يجب عليك تعلم SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%81%D9%87%D9%85-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D9%82%D9%8A%D8%A9-r2216/" rel="">فهم قواعد البيانات العلاقية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D9%85%D9%88%D8%A7%D8%B6%D9%8A%D8%B9-%D9%85%D8%AA%D9%81%D8%B1%D9%82%D8%A9-%D9%81%D9%8A-sql-r857/" rel="">مواضيع متفرقة في SQL</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2270</guid><pubDate>Sun, 10 Mar 2024 12:01:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x639;&#x648;&#x627;&#x645;&#x644; BETWEEN &#x648; IN &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x627;&#x644;&#x627;&#x633;&#x62A;&#x639;&#x644;&#x627;&#x645; &#x627;&#x644;&#x628;&#x646;&#x64A;&#x648;&#x64A;&#x629; SQL</title><link>https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B9%D9%88%D8%A7%D9%85%D9%84-between-%D9%88-in-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2263/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_02/---BETWEEN-IN-----SQL.png.cdb52197fcc9dfeab7f8220c3fe090a5.png" /></p>
<p>
	سنتعرف في مقال اليوم على طريقة استخدام بنى <code>WHERE</code> في بعض تعليمات لغة الاستعلام البنيوية SQL لتحديد سجلات معينة ستتأثر بكل عملية نجريها، وذلك بتعريف معايير محددة تُعرف باسم شروط البحث، إذ يجب أن يستوفيها كل سجل ليتأثر بالعملية. وما شرط البحث سوى مجموعة من التوابع الشرطية أو التعبيرات القادرة على تقييم تعبير قيمة واحد أو أكثر لتعيد نتيجة تكون إمّا "صحيحة True" أو "خاطئة False" أو "غير محددة Unknown"، إذ تؤثر العمليات فقط على تلك السجلات التي يُقيّم من أجلها كل تابع شرطي في بنية <code>WHERE</code> على أنّه "صحيح True".
</p>

<p>
	تتيح لغة SQL للمستخدمين استرجاع مجموعات نتائج دقيقة من قاعدة البيانات، وذلك بتوفيرها مجموعة متنوعة من أنماط التوابع الشرطية، إذ يستخدم كل منها عاملًا محددًا لتقييم السجلات. وسنوضّح في هذا المقال نمطين من التوابع الشرطية وهي:
</p>

<ul>
	<li>
		التوابع الشرطية للنطاق والتي تستخدم عامل <code>BETWEEN.</code>
	</li>
	<li>
		والتوابع الشرطية لعضوية المجموعة التي تستخدم عامل <code>IN</code>.
	</li>
</ul>

<p>
	رغم أننا سنستخدم في أمثلتنا بهذا المقال تعليمات <code>SELECT</code> على وجه التحديد، إلّا أنّه من الممكن استخدام المفاهيم الموضحة فيه في العديد من عمليات <a href="https://academy.hsoub.com/programming/sql/%D9%86%D8%B8%D8%B1%D8%A9-%D8%B3%D8%B1%D9%8A%D8%B9%D8%A9-%D8%B9%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D9%87%D9%8A%D9%83%D9%84%D9%8A%D8%A9-sql-r1368/" rel="">SQL</a>. إذ تعد بنى <code>WHERE</code> في الواقع مكونات أساسية في عمليات التحديث <code>UPDATE</code> والحذف <code>DELETE</code>.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	لمتابعة الخطوات في هذا المقال، ستحتاج إلى جهاز كمبيوتر يُشغّل أحد أنواع أنظمة إدارة قواعد البيانات العلاقية RDBMS التي تستخدم <a href="https://academy.hsoub.com/programming/sql/" rel="">SQL</a>. وقد اختبرنا الأوامر البرمجية والأمثلة في هذا المقال مستخدمين البيئة التالية:
</p>

<ul>
	<li>
		خادم عامل على توزيعة أوبنتو، مع مستخدم ذو صلاحيات مسؤول مختلف عن المستخدم الجذر، وجدار حماية مكوّن باستخدام UFW، كما هو موضح في مقال <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A9-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-%D9%85%D9%86-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A8%D8%A3%D8%A8%D8%B3%D8%B7-%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-r575/" rel="">كيفية تثبيت توزيعة أوبنتو من لينكس بأبسط طريقة</a>.
	</li>
	<li>
		MySQL مثبتة ومؤمنة على الخادم، كما هو موضح في المقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبونتو</a>. وقد نفذنا خطوات هذا المقال باستخدام مستخدم MySQL مختلف عن المستخدم الجذر، مُنشأ وفق الطريقة الموضحة في الخطوة 3 من هذا المقال.
	</li>
	<li>
		ستحتاج أيضًا إلى قاعدة بيانات بجداول مُحمّلة ببعض البيانات التجريبية النموذجية لتتمكن من التدرب على كتابة استعلامات تتضمّن بنى <code>WHERE</code>. لذا ننصحك بقراءة القسم القادم <strong>الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية</strong> للمزيد من التفاصيل حول كيفية الاتصال بخادم MySQL وإنشاء قاعدة البيانات التجريبية المُستخدمة في الأمثلة خلال هذا المقال.
	</li>
</ul>

<p>
	<strong>ملاحظة:</strong> تجدر الإشارة إلى أنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية لها تقديماتها الفريدة من لغة SQL. فبالرغم من كون الأوامر المُقدمة في هذا المقال ستعمل مع معظم هذه الأنظمة، ولكن قد تجد بعض الاختلافات في الصيغة أو الخرج عند تنفيذها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<h2 id="mysql">
	الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	إذا كان نظام قاعدة بيانات SQL الخاص بك يعمل على خادم عن بُعد، اتصل بالخادم مُستخدمًا بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> من جهازك المحلي على النحو:
</p>

<pre class="ipsCode">$ ssh ssh user@your_server_ip
</pre>

<p>
	ثم افتح واجهة سطر الأوامر في خادم MySQL، مُستبدلًا <code>user</code> باسم حساب مستخدم MySQL الخاص بك:
</p>

<pre class="ipsCode">$ mysql -u user -p
</pre>

<p>
	الآن ومن نافذة سطر الأوامر، أنشئ قاعدة بيانات باسم <code>between_in_db</code>:
</p>

<pre class="ipsCode">mysql&gt; CREATE DATABASE between_in_db;
</pre>

<p>
	وبمجرّد إنشاء قاعدة البيانات بنجاح ستحصل على خرجٍ كالتالي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	ولاختيار قاعدة البيانات <code>between_in_db</code>، نفّذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode">mysql&gt; USE between_in_db;
</pre>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	بعد اختيار قاعدة البيانات <code>between_in_db</code>، أنشئ جدولًا ضمنها.
</p>

<p>
	لمتابعة الأمثلة المستخدمة في هذا المقال، لنفرض أننا نرغب بإدارة فريق مبيعات لشركة ما، وبفرض أن هذه الشركة تبيع ثلاث منتجات فقط: أجهزة صغيرة، وأدوات متنوعة، وأجهزة مبتكرة. وقررنا تتبع عدد الوحدات من كل منتج يبيعه كل عضو في الفريق ضمن قاعدة بيانات SQL. وارتأينا أنّ هذه القاعدة ستحتوي على جدول واحد بأربعة أعمدة:
</p>

<ul>
	<li>
		<code>name</code>: أسماء أعضاء فريق المبيعات، معبرًا عنها باستخدام نمط البيانات <code>varchar</code> بحد أقصى 20 محرفًا.
	</li>
	<li>
		<code>widgets</code>: العدد الإجمالي للأجهزة الصغيرة التي باعها كل بائع، معبرًا عنها بنمط بيانات الأعداد الصحيحة <code>int.</code>
	</li>
	<li>
		<code>doodads</code>: عدد الأدوات المتنوعة التي باعها كل بائع، معبرًا عنها أيضًا بنمط بيانات الأعداد الصحيحة <code>int.</code>
	</li>
	<li>
		<code>gizmos</code>: عدد الأجهزة المبتكرة التي باعها كل بائع، معبرًا عنها أيضًا بنمط بيانات الأعداد الصحيحة <code>int.</code>
	</li>
</ul>

<p>
	لننفّذ تعليمة <code>CREATE TABLE</code> التالية لإنشاء جدول باسم <code>sales</code> يحتوي على هذه الأعمدة الأربعة:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_918_14" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE sales </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> name varchar</span><span class="pun">(</span><span class="lit">20</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> widgets </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> doodads </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> gizmos </span><span class="kwd">int</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
Query OK, 0 rows affected (0.01 sec)
</pre>

<p>
	الآن سنملأ جدول <code>sales</code> بعض البيانات التجريبية. نفّذ العملية <code>INSERT INTO</code> التالية لإضافة سبع سجلات من البيانات تمثل أعضاء فريق المبيعات وعدد ما باعوه من كل منتج:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_918_16" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO sales
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Tyler'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">22</span><span class="pun">,</span><span class="pln"> </span><span class="lit">18</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Blair'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">19</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">13</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Lynn'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">29</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Boris'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="lit">15</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Lisa'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">17</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">31</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Maya'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Henry'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">14</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span></pre>

<p>
	وبذلك غدوتَ جاهزًا للمتابعة بباقي أقسام المقال وبدء تعلم كيفية استخدام عوامل <code>BETWEEN</code> و<code>IN</code> لتصفية البيانات.
</p>

<h2 id="where">
	فهم التوابع الشرطية الخاصة ببنية WHERE
</h2>

<p>
	يمكنك إدراج بنية <code>WHERE</code> بعد بنية <code>FROM</code> في أي عملية SQL تقرأ البيانات من جدول موجود أصلًا وذلك لتحديد البيانات التي ستتأثر بهذه العملية، إذ تُعرّف بنى <code>WHERE</code> شرط بحث، وأي سجل لا يحقق هذا الشرط سيُستثنى من العملية، على عكس السجلات التي تحققه.
</p>

<p>
	وما شرط البحث سوى مجموعة من التوابع الشرطية أو التعبيرات القادرة على تقييم تعبير قيمة واحد أو أكثر لتعيد نتيجة تكون إمّا "صحيحة True" أو "خاطئة False" أو "غير محددة Unknown". يُعرّف تعبير القيمة في لغة SQL - والذي يُشار إليه أحيانًا باسم التعبير ذو القيمة المفردة - بأنّه أي تعبير يُعيد قيمة واحدة. ويمكن أن يكون تعبير القيمة عبارة عن قيمة مُصنفة النوع من قبيل سلسلة نصية أو قيمة عددية، أو تعبير رياضي. ولكن غالبًا ما يكون على الأقل أحد تعبيرات القيمة في شرط بحث بنية <code>WHERE</code> هو <strong>اسم عمود.</strong>
</p>

<p>
	ومن الجدير بالملاحظة أنّه في معظم الأحيان نستخدم على الأقل اسم عمود من الجدول المُشار إليه ضمن بنية <code>FROM</code> من العملية كأحد تعابير القيمة في التابع الشرطي ضمن بنية <code>WHERE</code>
</p>

<p>
	ولدى تشغيل استعلامات SQL المُتضمّنة لبنية <code>WHERE</code>، سيطبق نظام إدارة قاعدة البيانات (DBMS) شرط البحث على كل سجل في الجدول المنطقي المحدد في بنية <code>FROM</code> ليُعيد فقط السجلات التي يُقيّم من أجلها كل تابع شرطي في شرط البحث على أنه "محقق TRUE".
</p>

<p>
	يُحدّد معيار SQL ثمانية عشر نمطًا مختلفًا من التوابع الشرطية، وعلى الرغم من أنّها لا تتوفر كاملةً في كافّة أنظمة إدارة قواعد البيانات العلاقية RDBMS. إليك خمسة من أكثر أنواع التوابع المستخدمة في شروط البحث والعوامل المستخدمة في كل منها:
</p>

<p>
	<strong>المقارنة</strong>: تقارن التوابع الشرطية المقارنِة بين تعبيري قيمة، وفي معظم الاستعلامات يكون أحد هذين التعبيرين هو اسم عمود. وعوامل المقارنة الستة هي:
</p>

<ul>
	<li>
		<code>=</code>: يختبر ما إذا كانت القيمتان متساويتين.
	</li>
	<li>
		<code>&lt;&gt;</code>: يختبر ما إذا كانت القيمتان غير متساويتين.
	</li>
	<li>
		<code>&lt;</code>: يختبر ما إذا كانت القيمة الأولى أقل من الثانية.
	</li>
	<li>
		<code>&gt;</code>: يختبر ما إذا كانت القيمة الأولى أكبر من الثانية.
	</li>
	<li>
		<code>&lt;=</code>: يختبر ما إذا كانت القيمة الأولى أقل من أو تساوي الثانية.
	</li>
	<li>
		<code>&gt;=</code>: يختبر ما إذا كانت القيمة الأولى أكبر من أو تساوي الثانية.
	</li>
</ul>

<p>
	<strong>القيم الفارغة</strong>: تختبر التوابع الشرطية التي تستخدم عامل <code>IS NULL</code> ما إذا كانت القيم في عمود معين فارغة.
</p>

<p>
	<strong>النطاق</strong>: تستخدم التوابع الشرطية النطاقية عامل <code>BETWEEN</code> لاختبار ما إذا كان تعبير قيمة ما يقع بين تعبيري قيمة آخرين.
</p>

<p>
	<strong>العضوية</strong>: يستخدم هذا النوع من التوابع الشرطية عامل <code>IN</code> لاختبار ما إذا كانت قيمة ما تُمثّل عضوًا في مجموعة معينة.
</p>

<p>
	<strong>تطابق الأنماط</strong>: تستخدم توابع مطابقة الأنماط الشرطية عامل <code>LIKE</code> لاختبار ما إذا كانت قيمة ما تطابق نمطًا نصيًا يحتوي على محارف بدل.
</p>

<p>
	وكما ذكرنا في المقدمة، يركز هذا المقال على توضيح كيفية استخدام عوامل <code>BETWEEN</code> و<code>IN</code> في SQL لتصفية البيانات. فإذا كنت ترغب في معرفة كيفية استخدام عوامل المقارنة أو عامل <code>IS NULL</code>، ننصحك بقراءة مقالنا حول كيفية استخدام عوامل المقارنة وIS NULL في لغة الاستعلام البنيوية SQL. أمّا إذا كنت ترغب في معرفة كيفية استخدام عامل <code>LIKE</code> لتصفية البيانات بناءً على نمط نصي يحتوي على محارف بدل، فننصحك بقراءة المقال كيفية استخدام محارف البدل في SQL. وللاطلاع على المزيد حول بنى <code>WHERE</code> عمومًا، ننصحك بقراءة مقالنا حول <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D9%86%D9%89-where-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2258/" rel="">كيفية استخدام بنى WHERE في لغة الاستعلام البنيوية SQL</a>.
</p>

<h2 id="-1">
	التوابع الشرطية النطاقية
</h2>

<p>
	تستخدم التوابع الشرطية النطاقية عامل <code>BETWEEN</code> لاختبار ما إذا كان تعبير قيمة محصور بين تعبيري قيمة آخرين. وتتبع بنية <code>WHERE</code> المُتضمنة لتابع شرطي نطاقي في شرط البحث الخاص بها الصيغة العامة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_918_19" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT column_list
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM table_name
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE column_name BETWEEN value_expression1 AND value_expression2</span><span class="pun">;</span></pre>

<p>
	ويأتي بعد كلمة <code>WHERE</code> المفتاحية تعبير قيمة، والذي يكون في معظم عمليات SQL عبارة عن اسم عمود. وبما أن نظام قاعدة البيانات يطبق شروط البحث على كل سجل تباعًا، فإن توفير اسم عمود كتعبير قيمة في شرط البحث يخبر نظام إدارة قواعد البيانات العلاقية RDBMS باستخدام قيمة كل سجل من العمود المحدد كجزء من تعبير القيمة للتكرار الخاص بكل سجل في شرط البحث.
</p>

<p>
	ويأتي بعد اسم العمود عامل <code>BETWEEN</code> وتعبيري قيمة آخرين مفصولين بعامل <code>AND</code>. وسيُقيّم شرط البحث على أنه "صحيح True" لأي سجلات تكون قيمتها في العمود المحدد أكبر من أو تساوي القيمة الأولى من القيمتين المفصولتين بعامل <code>AND</code> وأقل من أو تساوي القيمة الثانية.
</p>

<p>
	لتوضيح كيفية عمل التوابع الشرطية النطاقية، لننفّذ الاستعلام التالي. والذي سيُعيد قيم عمودي <code>name</code> و<code>widgets</code> لأي سجلات تكون فيها قيمة <code>widgets</code> محصورة بين <code>14</code> و<code>19</code>ضمنًا:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_918_21" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> widgets
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM sales
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE widgets BETWEEN </span><span class="lit">14</span><span class="pln"> AND </span><span class="lit">19</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------+---------+
| name  | widgets |
+-------+---------+
| Blair |      19 |
| Boris |      16 |
| Lisa  |      17 |
| Henry |      14 |
+-------+---------+
4 rows in set (0.00 sec)
</pre>

<p>
	تذكر أن النطاق الذي تحدده بعد عامل <code>BETWEEN</code> يمكن أن يتكون من أي زوج من تعابير القيمة، بما في ذلك أسماء الأعمدة.
</p>

<p>
	يعيد الاستعلام التالي جميع الأعمدة من جدول <code>sales</code>. وفيه، وبدلًا من تحديد كل عمود على حدة، ألحقنا الكلمة المفتاحية <code>SELECT</code> بعلامة النجمة (<code>*</code>) مباشرةً، وهي الطريقة المختصرة في SQL للإشارة إلى "جميع الأعمدة". وتحدّ بنية <code>WHERE</code> في هذا الاستعلام من إرجاعه للسجلات التي تكون فيها قيمة <code>gizmos</code> أكبر من قيمة <code>doodads</code> لكنها أقل من قيمة <code>widgets</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_918_23" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM sales
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE gizmos BETWEEN doodads AND widgets</span><span class="pun">;</span></pre>

<p>
	لا يمتلك سوى عضو واحد من فريق المبيعات قيمة في عمود <code>gizmos</code> محصورة بين القيمتين <code>widgets</code> و<code>doodads</code>. لذا فإنّ السجل الخاص به فقط هو الذي يظهر في مجموعة النتائج.
</p>

<pre class="ipsCode">الخرج
+-------+---------+---------+--------+
| name  | widgets | doodads | gizmos |
+-------+---------+---------+--------+
| Blair |      19 |       8 |     13 |
+-------+---------+---------+--------+
1 row in set (0.00 sec)
</pre>

<p>
	ويجب أن تكون على دراية بترتيب تعابير القيمة المُحدّدة للنطاق: فالقيمة الأولى بعد عامل <code>BETWEEN</code> تُمثّل دائمًا الحد الأدنى للنطاق، أمّا القيمة الثانية فتُمثّل الحد الأعلى. الاستعلام التالي مطابق للسابق، باستثناء أنه يعكس ترتيب الأعمدة التي تُحدد طرفيّ النطاق:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_918_25" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM sales
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE gizmos BETWEEN widgets AND doodads</span><span class="pun">;</span></pre>

<p>
	يعيد الاستعلام هذه المرة سجلين حيث تكون في كل منهما قيمة <code>gizmos</code> أكبر من أو تساوي قيمة <code>widgets</code> للسجل ولكنها أقل من أو تساوي قيمة <code>doodads</code>. وكما تدل النتائج، يمكن أن يؤدي تغيير الترتيب بهذه الطريقة إلى استرجاع مجموعة نتائج مختلفة تمامًا.
</p>

<pre class="ipsCode">الخرج
+-------+---------+---------+--------+
| name  | widgets | doodads | gizmos |
+-------+---------+---------+--------+
| Tyler |      12 |      22 |     18 |
| Maya  |       5 |       9 |      7 |
+-------+---------+---------+--------+
2 rows in set (0.00 sec)
</pre>

<p>
	وعلى نحوٍ مشابه لحالة استخدام عوامل المقارنة <code>&lt;</code>، <code>&gt;</code>، <code>&lt;=</code>، و <code>&gt;=</code> لتقييم عمود مُتضمّن لقيم من نمط السلاسل النصية، سيُحدد العامل <code>BETWEEN</code> ما إذا كانت هذه القيم تقع أبجديًا بين قيمتين من نمط السلاسل النصية.
</p>

<p>
	لتوضيح الفكرة، لنُنفّذ الاستعلام التالي الذي يعيد قيم العمود <code>name</code> الموافقة لأي سجل من الجدول <code>sales</code> حيث تكون قيمة <code>name</code> محصورة أبجديَا بين الحرفين <code>A</code> و <code>M</code>.
</p>

<p>
	استخدمنا في هذا المثال سلسلتين نصيتين مجردتين كتعبيريّ قيمة يمثلان طرفي النطاق. ومن الجدير بالملاحظة أنّه يجب حصر هذه القيم المحرفية بين علامات اقتباس مفردة أو مزدوجة، وإلا سيبحث نظام إدارة قواعد البيانات عن أعمدة باسم <code>A</code> و <code>M</code>، مما قد يؤدي إلى فشل الاستعلام.
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_918_27" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM sales
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE name BETWEEN </span><span class="str">'A'</span><span class="pln"> AND </span><span class="str">'M'</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------+
| name  |
+-------+
| Blair |
| Lynn  |
| Boris |
| Lisa  |
| Henry |
+-------+
5 rows in set (0.00 sec)
</pre>

<p>
	نلاحظ أن مجموعة النتائج هذه لا تتضمن الاسم <code>Maya</code> رغم كون النطاق المحدد في شرط البحث يمتد من <code>A</code> إلى <code>M</code>. السبب في ذلك هو أن الحرف M يأتي أبجديًا قبل أي سلسلة نصية تبدأ بالحرف M وتحتوي على أكثر من حرف، لذا يُستثنى الاسم <code>Maya</code> من مجموعة النتائج هذه، بالإضافة إلى أي بائعين آخرين لا تقع أسماؤهم ضمن النطاق المحدد.
</p>

<h2 id="-2">
	التوابع الشرطية للعضوية
</h2>

<p>
	تتيح التوابع الشرطية للعضوية إمكانية تصفية نتائج الاستعلام بناءً على ما إذا كانت قيمة ما تُمثّل عضوًا من مجموعة محددة من البيانات. وعادةً ما تتبع الصيغة التالية في بنى <code>WHERE</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_918_29" style=""><span class="pln">mysql</span><span class="pun">&gt;.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE column_name IN </span><span class="pun">(</span><span class="pln">set_of_data</span><span class="pun">)</span><span class="pln">
mysql</span><span class="pun">&gt;.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span></pre>

<p>
	يأتي بعد الكلمة المفتاحية <code>WHERE</code> تعبير القيمة، وعادةً ما يكون هذا التعبير عبارة عن اسم عمود. يلي ذلك عامل <code>IN</code>، الذي يُتبع بدوره بمجموعة من البيانات. يمكن تحديد هذه المجموعة بوضوح عن طريق إدراج أي عدد من تعابير القيمة الصالحة مفصولةً بعلامات فاصلة، سواء كانت قيمًا مجردة أو أسماء أعمدة أو تعابير رياضية تتضمن أيًا من العناصر آنفة الذكر.
</p>

<p>
	لتوضيح الأمر، لنُنفذ الاستعلام التالي. والذي سيعيد قيم عموديّ <code>name</code> و<code>gizmos</code> لكل سجل تُمثّل فيه قيمة <code>gizmos</code> عضوًا من المجموعة المعرّفة بعد عامل <code>IN</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_918_31" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> doodads
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM sales
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE doodads IN </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">21</span><span class="pun">,</span><span class="pln"> </span><span class="lit">22</span><span class="pun">);</span></pre>

<p>
	نلاحظ أن قيم العمود <code>doodads</code> لثلاثة أعضاء فقط من فريق المبيعات تساوي أي من القيم الموجودة ضمن هذه المجموعة، ولذلك فإن السجلات الخاصة بهم فقط هي المُسترجعة.
</p>

<pre class="ipsCode">الخرج
+-------+---------+
| name  | doodads |
+-------+---------+
| Tyler |      22 |
| Lisa  |       2 |
| Henry |       2 |
+-------+---------+
3 rows in set (0.00 sec)
</pre>

<p>
	وبدلًا من كتابة كل عنصر في مجموعة بنفسك، يُمكنك تكوين مجموعة من خلال إلحاق عامل <code>IN</code> بالاستعلام الفرعي. يُعرف الاستعلام الفرعي أيضًا بالاستعلام المُدمج أو الداخلي، وهو تعليمة <code>SELECT</code> مُدرجة ضمن إحدى بنى تعليمة <code>SELECT</code> أخرى. يُمكن للاستعلام الفرعي أن يستخرج المعلومات من أي جدول ضمن نفس قاعدة البيانات المحددة في بنية <code>FROM</code> للعملية الرئيسة 'الخارجية'.
</p>

<p>
	<strong>ملاحظة</strong>: عند كتابة استعلام فرعي لتحديد مجموعة ضمن التابع الشرطي للعضوية، تأكد من استخدام استعلام فرعي ذو قيمة مفردة، أي استعلام يُعيد عمودًا واحدًا فقط. تميل أنظمة إدارة قواعد البيانات إلى عدم السماح باستعلامات فرعية تُعيد عدة أعمدة ضمن التابع الشرطي للعضوية، نظرًا لعدم وضوح العمود الذي يجب تقييمه لتحديد العناصر المنتمية للمجموعة.
</p>

<p>
	كمثال على استخدام الاستعلام الفرعي لتحديد مجموعة في تابع شرطي للعضوية، نفذ التعليمة التالية لإنشاء جدول يُسمى <code>example_set_table</code> يحتوي على عمود واحد فقط. سنسمى هذا العمود <code>prime_numbers</code> وسيحتوي على قيم من نمط البيانات <code>int</code>، على النحو:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_918_33" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE example_set_table </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> prime_numbers </span><span class="kwd">int</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	لنملأ الآن هذا الجدول ببضعة سجلات من البيانات التجريبية النموذجية. وفقًا لاسم العمود الوحيد في الجدول، ستُضيف التعليمة <code>INSERT</code> التالية عشرة سجلات إلى الجدول، ويحتوي كل منها على أحد الأعداد الأولية العشرة الأولى:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_918_35" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln">INSERT INTO example_set_table
mysql</span><span class="pun">&gt;</span><span class="pln">VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">5</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">7</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">11</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">13</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">17</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">19</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">23</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">29</span><span class="pun">);</span></pre>

<p>
	لنُشغّل الآن الاستعلام التالي الذي يُعيد قيمًا من أعمدة <code>name</code> و <code>widgets</code> في جدول <code>sales</code>، إذ تختبر بنية <code>WHERE</code> ما إذا كانت كل قيمة في عمود <code>widgets</code> تنتمي للمجموعة التي حصلنا عليها من الاستعلام الفرعي <code>SELECT prime_numbers FROM example_set_table</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_918_39" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> widgets
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM sales
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE widgets IN </span><span class="pun">(</span><span class="pln">SELECT prime_numbers FROM example_set_table</span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
+-------+---------+
| name  | widgets |
+-------+---------+
| Blair |      19 |
| Lynn  |       7 |
| Lisa  |      17 |
| Maya  |       5 |
+-------+---------+
4 rows in set (0.00 sec)
</pre>

<p>
	بما أنّ أربعة فقط من أعضاء فريق المبيعات باعوا عددًا من الأجهزة الصغيرة يُطابق أحد الأعداد الأولية الموجودة في جدول <code>example_set_table</code>، سيُعيد هذا الاستعلام تلك السجلات الأربعة فقط.
</p>

<h2 id="-3">
	الخلاصة
</h2>

<p>
	باطلاعك على هذا المقال، ستكون قد اكتسبت المعرفة حول كيفية استخدام العامل <code>BETWEEN</code> في SQL لتحديد ما إذا كانت قيم في عمود ما تندرج ضمن نطاق محدد، كما تعلمت كيفية استخدام عامل <code>IN</code> لتحديد ما إذا كانت قيم في عمود ما تنتمي إلى مجموعة معينة.
</p>

<p>
	ومن المفترض أن تعمل الأوامر المشروحة في هذا المقال مع أي نظام لإدارة قواعد البيانات يستخدم SQL. لكن تذكر أن لكل قاعدة بيانات SQL تقديمها الخاص للغة، لذا ينبغي مراجعة التوثيق الرسمي لنظام إدارة قواعد البيانات الخاص بك للحصول على وصف أكثر تفصيلاً لكل أمر فيها ومجموعة خياراته الكاملة.
</p>

<p>
	وللمزيد حول SQL، نشجعك على متابعة <a href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20%D8%AA%D8%B9%D9%84%D9%85%20sql/" rel="">المقالات المنشورة تحت وسم سلسلة تعلم SQL</a> في أكاديمية حسوب.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-the-between-and-in-operators-in-sql" rel="external nofollow">How To Use the BETWEEN and IN Operators in SQL</a> لصاحبه Mark Drake.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D9%86%D9%89-where-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2258/" rel="">كيفية استخدام بنى WHERE في لغة الاستعلام البنيوية SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%B9%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r588/" rel="">الاستعلام عن البيانات في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D9%81%D8%B1%D8%B9%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%A5%D8%AC%D8%B1%D8%A7%D8%A1%D8%A7%D8%AA-%D9%81%D9%8A-sql-r858/" rel="">الاستعلامات الفرعية والإجراءات في SQL</a><span style="display: none;"> </span>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/SQL" rel="external">التوثيق العربي للغة SQL</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2263</guid><pubDate>Sun, 03 Mar 2024 12:02:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x628;&#x646;&#x649; WHERE &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x627;&#x644;&#x627;&#x633;&#x62A;&#x639;&#x644;&#x627;&#x645; &#x627;&#x644;&#x628;&#x646;&#x64A;&#x648;&#x64A;&#x629; SQL</title><link>https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D9%86%D9%89-where-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2258/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_02/---WHERE-----SQL.png.96e272c63278a0aaca57d94880957eab.png" /></p>
<p>
	سنتعرف في مقال اليوم على دور البنية <code>WHERE</code> في تعليمات لغة الاستعلام البنيوية SQL لتحديد السجلات التي ستتأثر بكل عملية استعلام، وذلك بتعريف معايير أو شروط محددة تُعرف باسم <strong>شروط البحث</strong> والتي يجب أن يستوفيها كل سجل ليتأثر بالعملية المطلوب تنفيذها، حيث سنوفر شرحًا للصيغة العامة المستخدمة في بنية <code>WHERE</code>. ونوضح كيفية دمج عدة شروط بحث في بنية واحدة لتصفية البيانات بدقة أكبر، كما سنوضح كيفية استخدام العامل <code>NOT</code> لاستثناء السجلات التي تلبي شرط بحث معين بدلًا من تضمينها.
</p>

<p>
	وبالرغم من أننا سنستخدم في أمثلتنا بهذا المقال تعليمات <code>SELECT</code> على وجه التحديد، إلّا أنّه من الممكن استخدام المفاهيم الموضحة فيه في العديد من عمليات SQL، إذ يمكن استخدام بنية <code>WHERE</code> كذلك في عمليات <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AD%D8%AF%D9%8A%D8%AB-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2242/" rel="">تحديث البيانات UPDATE</a> أو <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AD%D8%B0%D9%81-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D9%88%D9%8A%D8%A9-sql-r2246/" rel="">حذفها DELETE</a>.
</p>

<h2 id="">
	مستلزمات العمل
</h2>

<p>
	لمتابعة الخطوات في هذا المقال، ستحتاج إلى جهاز كمبيوتر يُشغّل أحد أنواع أنظمة إدارة <a href="https://academy.hsoub.com/devops/servers/databases/%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-%D9%86%D9%85%D9%88%D8%B0%D8%AC-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D8%A6%D9%82%D9%8A%D8%A9-rdm-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-%D8%A7%D9%84%D9%85%D9%87%D9%85%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r522/" rel="">قواعد البيانات العلاقية</a> التي تستخدم لغة <a href="https://academy.hsoub.com/programming/sql/" rel="">SQL</a>. وقد اختبرنا الأوامر البرمجية والأمثلة في هذا المقال مستخدمين البيئة التالية:
</p>

<ul>
	<li>
		خادم عامل على توزيعة أوبنتو، مع مستخدم ذو صلاحيات مسؤول مختلف عن المستخدم الجذر، وجدار حماية مكوّن باستخدام UFW، كما هو موضح في مقال <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A9-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-%D9%85%D9%86-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A8%D8%A3%D8%A8%D8%B3%D8%B7-%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-r575/" rel="">كيفية تثبيت توزيعة أوبنتو من لينكس بأبسط طريقة</a>.
	</li>
	<li>
		MySQL مثبتة ومؤمنة على الخادم، كما هو موضح في المقال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r433/" rel="">كيفية تثبيت MySQL على أوبونتو</a>. وقد نفذنا خطوات هذا المقال باستخدام مستخدم MySQL مختلف عن المستخدم الجذر، مُنشأ وفق الطريقة الموضحة في الخطوة الثالثة من هذا المقال.
	</li>
	<li>
		ستحتاج أيضًا إلى قاعدة بيانات بجداول مُحمّلة ببعض البيانات التجريبية النموذجية لتتمكن من التدرب على كتابة استعلامات تتضمّن بنى <code>WHERE</code>. لذا ننصحك بقراءة الفقرة التالية التي تشرح طريقة <strong>الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية</strong> للمزيد من التفاصيل حول تنفيذ الأمثلة المُستخدمة خلال هذا المقال.
	</li>
</ul>

<p>
	<strong>ملاحظة:</strong> تجدر الإشارة إلى أنّ الكثير من أنظمة إدارة قواعد البيانات العلاقية لها تقديماتها الفريدة من لغة <a href="https://wiki.hsoub.com/SQL" rel="external">SQL</a>، فبالرغم من كون الأوامر المُقدمة في هذا المقال ستعمل مع معظم هذه الأنظمة، ولكن قد تجد بعض الاختلافات الطفيفة في الصيغة أو الخرج عند تنفيذها على أنظمة مختلفة عن <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a>.
</p>

<h2 id="mysql">
	الاتصال بـ MySQL وإعداد قاعدة بيانات تجريبية نموذجية
</h2>

<p>
	إذا كان نظام قاعدة بيانات SQL الخاص بك يعمل على خادم عن بُعد، اتصل بالخادم مُستخدمًا بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> من جهازك المحلي على النحو التالي:
</p>

<pre class="ipsCode">$ ssh ssh user@your_server_ip
</pre>

<p>
	ثم افتح واجهة سطر الأوامر في خادم MySQL، مُستبدلًا <code>user</code> باسم حساب مستخدم MySQL الخاص بك:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_7" style=""><span class="pln">$ mysql </span><span class="pun">-</span><span class="pln">u user </span><span class="pun">-</span><span class="pln">p</span></pre>

<p>
	الآن ومن نافذة سطر الأوامر، أنشئ <a href="https://academy.hsoub.com/devops/servers/databases/%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/" rel="">قاعدة بيانات</a> باسم <code>where_db</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_10" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE DATABASE where_db</span><span class="pun">;</span></pre>

<p>
	بمجرّد إنشاء قاعدة البيانات بنجاح ستحصل على خرجٍ كالتالي:
</p>

<pre class="ipsCode">الخرج
Query OK, 1 row affected (0.01 sec)
</pre>

<p>
	ولاختيار قاعدة البيانات <code>where_db</code>، نفّذ تعليمة <code>USE</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_12" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> USE where_db</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
Database changed
</pre>

<p>
	بعد اختيار قاعدة البيانات <code>where_db</code>، أنشئ جدولًا ضمنها.
</p>

<p>
	ولمتابعة الأمثلة المستخدمة في هذا المقال، سنفترض أننا نرغب بإدارة دوري للغولف في ملعب غولف محلي. وقررنا تتبع المعلومات حول أداء كل لاعب في الدوري خلال الجولات التي سيشارك بها، لذا قررنا تخزين المعلومات في قاعدة بيانات تتضمن جدولًا باسم golfers.
</p>

<p>
	وارتأينا أنّ هذا الجدول يحتاج إلى ستة أعمدة وهي:
</p>

<ul>
	<li>
		<code>name</code>: أسماء لاعبي الغولف، مُعبر عنها باستخدام نمط البيانات <code>varchar</code> بحد أقصى 20 محرفًا.
	</li>
	<li>
		<code>rounds_played</code>: إجمالي عدد الجولات التي لعبها كل لاعب غولف كاملةً، مُعبر عنها بنمط بيانات الأعداد الصحيحة <code>int</code>.
	</li>
	<li>
		<code>best</code>: أفضل أو أدنى نتيجة لكل لاعب جولف لجولة فردية، مُعبرًا عنها أيضًا بنمط الأعداد الصحيحة <code>int</code>.
	</li>
	<li>
		<code>worst</code>: أسوأ أو أعلى نتيجة لكل لاعب جولف لجولة فردية، مُعبرًا عنها كذلك بنمط الأعداد الصحيحة <code>int</code>.
	</li>
	<li>
		<code>average</code>: متوسط تقريبي لنتائج كل لاعب غولف عبر الجولات التي لعبوها، سيحتوي هذا العمود على قيم من نوع <code>decimal</code>، محددة بحد أقصى 4 أرقام، واحد منها على يمين الفاصلة العشرية.
	</li>
	<li>
		<code>wins</code>: عدد الجولات التي حقق فيها كل لاعب غولف أقل نتيجة بين جميع المشاركين في المجموعة، مُعبر عنها باستخدام نمط <code>int</code>.
	</li>
</ul>

<p>
	ملاحظة: قد تستغرب لماذا تكون النتيجة الأفضل <code>best</code> لكل لاعب أدنى من نتيجته الأسوأ <code>worst</code>، وهذا يعود لطبيعة قواعد لعبة الغولف التي تقتضي أن يكون الفائز هو من ينجز اللعبة بأقل عدد من الضربات. بالتالي، وبخلاف الرياضات الأخرى، تُعد النتيجة <strong>الأدنى</strong> هي الأفضل للاعب الغولف.
</p>

<p>
	لإنشاء جدول باسم <code>golfers</code> يحتوي على هذه الأعمدة الستة، نفّذ التعليمة <code>CREATE TABLE</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_16" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> CREATE TABLE golfers </span><span class="pun">(</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> name varchar</span><span class="pun">(</span><span class="lit">20</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> rounds_played </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> best </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> worst </span><span class="kwd">int</span><span class="pun">,</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> average </span><span class="kwd">decimal</span><span class="pln"> </span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="lit">1</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> wins </span><span class="kwd">int</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	الآن سنملأ جدول <code>golfers</code> ببعض البيانات التجريبية. نفّذ العملية <code>INSERT INTO</code> التالية لإضافة سبع سجلات من البيانات تمثل سبعة من لاعبي الدوري للغولف:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_18" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> INSERT INTO golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> VALUES
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'George'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">22</span><span class="pun">,</span><span class="pln"> </span><span class="lit">68</span><span class="pun">,</span><span class="pln"> </span><span class="lit">103</span><span class="pun">,</span><span class="pln"> </span><span class="lit">84.6</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Pat'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">65</span><span class="pun">,</span><span class="pln"> </span><span class="lit">74</span><span class="pun">,</span><span class="pln"> </span><span class="lit">68.7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Grady'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11</span><span class="pun">,</span><span class="pln"> </span><span class="lit">78</span><span class="pun">,</span><span class="pln"> </span><span class="lit">118</span><span class="pun">,</span><span class="pln"> </span><span class="lit">97.6</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Diane'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">23</span><span class="pun">,</span><span class="pln"> </span><span class="lit">70</span><span class="pun">,</span><span class="pln"> </span><span class="lit">92</span><span class="pun">,</span><span class="pln"> </span><span class="lit">78.8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Calvin'</span><span class="pun">,</span><span class="pln"> NULL</span><span class="pun">,</span><span class="pln"> </span><span class="lit">63</span><span class="pun">,</span><span class="pln"> </span><span class="lit">76</span><span class="pun">,</span><span class="pln"> </span><span class="lit">68.5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Rose'</span><span class="pun">,</span><span class="pln"> NULL</span><span class="pun">,</span><span class="pln"> </span><span class="lit">69</span><span class="pun">,</span><span class="pln"> </span><span class="lit">84</span><span class="pun">,</span><span class="pln"> </span><span class="lit">76.7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">),</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">'Raymond'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">18</span><span class="pun">,</span><span class="pln"> </span><span class="lit">67</span><span class="pun">,</span><span class="pln"> </span><span class="lit">92</span><span class="pun">,</span><span class="pln"> </span><span class="lit">81.3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span></pre>

<p>
	نلاحظ أن قيم <code>rounds_played</code> لبعض سجلات اللاعبين فارغة <code>NULL</code>. فلنفترض جدلًا أنّ هؤلاء اللاعبين لم يُبلغوا بعد عن عدد الجولات التي شاركوا فيها، ولهذا السبب سُجلت هذه القيم على أنّها فارغة <code>NULL</code>.
</p>

<p>
	وبذلك غدوتَ جاهزًا للمتابعة بباقي أقسام المقال وبدء تعلم كيفية استخدام بنى <code>WHERE</code> في SQL.
</p>

<h2 id="where">
	تصفية البيانات باستخدام بنى WHERE
</h2>

<p>
	تُعرّف التعليمة البرمجية في SQL بأنّها أي عملية تُرسَل إلى نظام قاعدة البيانات لتنفيذ مهمّة محددة، من قبيل إنشاء جدول أو إدخال أو حذف بيانات أو تغيير هيكلية عمود أو جدول. وتتألف تعليمات SQL من بنى مختلفة تتكون بدورها من كلمات مفتاحية معينة مع المعلومات التي تتطلبها هذه الكلمات.
</p>

<p>
	وكما ذكرنا في المقدمة، تتيح بنى <code>WHERE</code> إمكانية تصفية بعض السجلات كي لا تتأثر بعملية SQL محددة. وتأتي بنية <code>WHERE</code> في الاستعلامات، بعد بنية <code>FROM</code>، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_20" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT columns_to_query
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM table_to_query
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE search_condition</span><span class="pun">;</span></pre>

<p>
	يأتي شرط البحث بعد كلمة <code>WHERE</code> المفتاحية. وما شرط البحث سوى مجموعة من التوابع الشرطية أو التعبيرات القادرة على تقييم تعبير قيمة واحد أو أكثر لتعيد نتيجة تكون إمّا "صحيحة True" أو "خاطئة False" أو "غير محددة Unknown"، ومن الجدير بالملاحظة أنّه في الحالات التي يحتوي فيها شرط البحث على تابع شرطي واحد فقط، يكون المصطلحان "شرط البحث" و"التابع الشرطي" مترادفين.
</p>

<p>
	قد تأخذ التوابع الشرطية في شرط البحث الخاص ببنية <code>WHERE</code> أشكالًا عديدة، لكنها عادةً ما تتبع الصيغة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_3193_14" style=""><span class="pln">WHERE column_name OPERATOR value_expression</span></pre>

<p>
	يُعرّف تعبير القيمة في لغة SQL - والذي يُشار إليه أحيانًا باسم التعبير ذو القيمة المفردة - بأنّه أي تعبير يُعيد قيمة واحدة. يمكن أن يكون تعبير القيمة عبارة عن قيمة مُصنفة النوع من قبيل سلسلة نصية أو قيمة عددية، أو تعبير رياضي. ولكن غالبًا ما يكون على الأقل أحد تعبيرات القيمة في شرط بحث بنية <code>WHERE</code> هو اسم عمود.
</p>

<p>
	فلدى تشغيل استعلامات SQL التي تحتوي على بنية <code>WHERE</code>، سيطبّق نظام إدارة قاعدة البيانات شرط البحث تتابعيًا على كل سجل في الجدول المحدد في بنية <code>FROM</code>. ولن يعيد سوى السجلات التي يُقيّم فيها كل شرط بحث على أنه صحيح True.
</p>

<p>
	لتوضيح هذه الفكرة، شغّل الاستعلام التالي. والذي سيعيد كل قيمة من العمود <code>name</code> في الجدول <code>golfers</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_24" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE </span><span class="pun">(</span><span class="lit">2</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4</span><span class="pun">;</span></pre>

<p>
	يتضمن هذا الاستعلام بنية <code>WHERE</code>، لكن بدلًا من تحديد اسم عمود فإنها تستخدم <code>(2 + 2)</code> كتعبير قيمة أول وتختبر ما إذا كان مساويًا لتعبير القيمة الثاني <code>4</code>. ونظرًا لأنّ التعبير <code>(2 + 2)</code> يساوي <strong>دائمًا</strong> <code>4</code>، فإن شرط البحث هذا يُقيّم على أنه صحيح لكل سجل في الجدول. ونتيجةً لذلك، تُرجع قبم كافّة السجلات في مجموعة النتائج، على النحو التالي:
</p>

<pre class="ipsCode">الخرج
+---------+
| name    |
+---------+
| George  |
| Pat     |
| Grady   |
| Diane   |
| Calvin  |
| Rose    |
| Raymond |
+---------+
7 rows in set (0.01 sec)
</pre>

<p>
	لعلّ بنية <code>WHERE</code> هذه ليست مفيدة بدرجة كبيرة، إذ إنها تُقيم دائمًا على أنها "صحيحة True" وبالتالي تُرجع دائمًا كافة سجلات الجدول. وكما ذكرنا سابقًا، عادةً ما نستخدم اسم عمود واحد على الأقل كأحد تعبيري القيمة في شرط البحث ضمن بنية <code>WHERE</code>. فلدى تشغيل الاستعلامات، سيطبق نظام قاعدة البيانات شرط البحث على كل سجل على حدة تباعًا. ومن خلال توفير اسم عمود كتعبير قيمة في شرط بحث، فإنك تُخبر نظام إدارة قاعدة البيانات باستخدام قيمة كل سجل من العمود المحدد كجزء من تعبير القيمة للتكرار الخاص بكل سجل في شرط البحث.
</p>

<p>
	تُطبّق بنية <code>WHERE</code> في الاستعلام التالي شرط بحث أكثر تحديدًا على كل سجل. إذ ستُرجع قيم كل من عمودي <code>name</code> و<code>wins</code> من أي سجل تساوي فيه<br>
	قيمة <code>wins</code> الرقم <code>1</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_26" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> wins
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE wins </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span></pre>

<p>
	يُرجع هذا الاستعلام سجليّن للاعبي جولف، نظرًا لوجود لاعبين قد فازا بجولة واحدة فقط، فيكون الخرج على هذا النحو:
</p>

<pre class="ipsCode">الخرج
+---------+------+
| name    | wins |
+---------+------+
| Diane   |    1 |
| Raymond |    1 |
+---------+------+
2 rows in set (0.01 sec)
</pre>

<p>
	من الجدير بالملاحظة أنّنا استخدمنا في الأمثلة السابقة علامة المساواة <code>=</code> لاختبار ما إذا كان تعبيرا القيمة متكافئين، إلا أنّ العامل المُستخدم يعتمد على نوع التابع الشرطي الذي ترغب باستخدامه لتصفية مجموعات النتائج الخاصة بك. يُحدّد معيار SQL ثمانية عشر نمطًا مختلفًا من التوابع الشرطية، على الرغم من أنّها لا تتوفر كاملةً في كافّة تقديمات SQL. إليك خمسة من أكثر أنماط التوابع الشرطية استخدامًا، بالإضافة إلى شرح موجز لكل منها والعوامل المستخدمة فيها:
</p>

<h2 id="-1">
	المقارنة
</h2>

<p>
	تقارن التوابع الشرطية المقارنِة بين تعبيري قيمة باستخدام عامل مقارنة، وفي معظم الاستعلامات يكون أحد هذين التعبيرين هو اسم عمود. وعوامل المقارنة الستة هي:
</p>

<ul>
	<li>
		العامل<strong><code> =</code></strong><code>:</code> يختبر ما إذا كانت القيمتان متساويتين
	</li>
</ul>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_28" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'George'</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+--------+
| name   |
+--------+
| George |
+--------+
1 row in set (0.00 sec)
</pre>

<ul>
	<li>
		العامل<strong><code> &lt;&gt;</code></strong>: يختبر ما إذا كانت القيمتان غير متساويتين
	</li>
</ul>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_30" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> wins
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE wins </span><span class="pun">&lt;&gt;</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+--------+------+
| name   | wins |
+--------+------+
| George |    3 |
| Pat    |    9 |
| Grady  |    0 |
| Calvin |    7 |
| Rose   |    4 |
+--------+------+
5 rows in set (0.00 sec)
</pre>

<ul>
	<li>
		العامل<strong><code> &lt;</code></strong>: يختبر ما إذا كانت القيمة الأولى أقل من الثانية تمامًا
	</li>
</ul>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_36" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> wins
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE wins </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+-------+------+
| name  | wins |
+-------+------+
| Grady |    0 |
+-------+------+
1 row in set (0.00 sec)
</pre>

<ul>
	<li>
		العامل<strong><code> &gt;</code></strong>: يختبر ما إذا كانت القيمة الأولى أكبر من الثانية تمامًا
	</li>
</ul>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_34" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> wins
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE wins </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+--------+------+
| name   | wins |
+--------+------+
| George |    3 |
| Pat    |    9 |
| Calvin |    7 |
| Rose   |    4 |
+--------+------+
4 rows in set (0.00 sec)
</pre>

<ul>
	<li>
		العامل<strong><code> &lt;=</code></strong>: يختبر ما إذا كانت القيمة الأولى أقل من أو تساوي الثانية
	</li>
</ul>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_38" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> wins
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE wins </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+---------+------+
| name    | wins |
+---------+------+
| Grady   |    0 |
| Diane   |    1 |
| Raymond |    1 |
+---------+------+
3 rows in set (0.00 sec)
</pre>

<ul>
	<li>
		العامل<strong><code> &gt;=</code></strong>: يختبر ما إذا كانت القيمة الأولى أكبر من أو تساوي القيمة الثانية
	</li>
</ul>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_40" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> wins
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE wins </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+---------+------+
| name    | wins |
+---------+------+
| George  |    3 |
| Pat     |    9 |
| Diane   |    1 |
| Calvin  |    7 |
| Rose    |    4 |
| Raymond |    1 |
+---------+------+
6 rows in set (0.00 sec)
</pre>

<h2 id="-2">
	القيم الفارغة
</h2>

<p>
	تختبر التوابع الشرطية التي تستخدم عامل <code>IS NULL</code> ما إذا كانت القيم في عمود معين فارغة، فإذا كان الأمر كذلك فعلًا، سيقيّم التابع الشرطي على أنه "صحيح"، وبالتالي سيُضمّن السجل في مجموعة النتائج:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_42" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> rounds_played
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE rounds_played IS NULL</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+--------+---------------+
| name   | rounds_played |
+--------+---------------+
| Calvin |          NULL |
| Rose   |          NULL |
+--------+---------------+
2 rows in set (0.00 sec)
</pre>

<h2 id="-3">
	النطاق
</h2>

<p>
	تستخدم التوابع الشرطية النطاقية عامل <code>BETWEEN</code> لاختبار ما إذا كانت قيم العمود محصورة بين تعبيري قيمة آخرين.
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_44" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> best
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE best BETWEEN </span><span class="lit">67</span><span class="pln"> AND </span><span class="lit">73</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+---------+------+
| name    | best |
+---------+------+
| George  |   68 |
| Diane   |   70 |
| Rose    |   69 |
| Raymond |   67 |
+---------+------+
4 rows in set (0.00 sec)
</pre>

<h2 id="-4">
	الانتماء أو العضوية
</h2>

<p>
	يستخدم هذا النوع من التوابع الشرطية عامل <code>IN</code> لاختبار ما إذا كانت قيمة ما تنتمي لمجموعة معينة.
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_48" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> best
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE best IN </span><span class="pun">(</span><span class="lit">65</span><span class="pun">,</span><span class="pln"> </span><span class="lit">67</span><span class="pun">,</span><span class="pln"> </span><span class="lit">69</span><span class="pun">,</span><span class="pln"> </span><span class="lit">71</span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
+---------+------+
| name    | best |
+---------+------+
| Pat     |   65 |
| Rose    |   69 |
| Raymond |   67 |
+---------+------+
3 rows in set (0.00 sec)
</pre>

<h2 id="-5">
	تطابق الأنماط
</h2>

<p>
	تستخدم توابع مطابقة الأنماط الشرطية عامل <code>LIKE</code> لاختبار ما إذا كانت قيمة ما تطابق نمطًا نصيًا يحتوي على <span ipsnoautolink="true">محرف بدل</span> واحد أو أكثر. وتُعرّف SQL محرفي بدل<br>
	هما <code>%</code> و <code>_</code>:
</p>

<ul>
	<li>
		محرف<strong><code> _</code></strong>: شرطة سفلية تُمثّل محرفًا واحدًا مجهولًا
	</li>
</ul>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_52" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> rounds_played
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE rounds_played LIKE </span><span class="str">'2_'</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+--------+---------------+
| name   | rounds_played |
+--------+---------------+
| George |            22 |
| Pat    |            25 |
| Diane  |            23 |
+--------+---------------+
3 rows in set (0.00 sec)
</pre>

<ul>
	<li>
		محرف<strong><code> %</code></strong>: علامة النسبة المئوية تُمثّل صفر أو أكثر من المحارف المجهولة
	</li>
</ul>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_54" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> rounds_played
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE name LIKE </span><span class="str">'G%'</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+--------+---------------+
| name   | rounds_played |
+--------+---------------+
| George |            22 |
| Grady  |            11 |
+--------+---------------+
2 rows in set (0.00 sec)
</pre>

<p>
	لن نفصّل بشرح كل نمط من التوابع الشرطية فهو خارج مجال هذا المقال، ولمعرفة المزيد حول هذه الأنماط، ننصحك بالاطلاع على المقالات التالية:
</p>

<ul>
	<li>
		كيفية استخدام عوامل المقارنة وIS NULL في لغة الاستعلام البنيوية SQL
	</li>
	<li>
		كيفية استخدام عوامل BETWEEN وIN في لغة الاستعلام البنيوية SQL
	</li>
	<li>
		كيفية استخدام محارف البدل في SQL
	</li>
</ul>

<h2 id="andor">
	دمج التوابع الشرطية المتعددة باستخدام عوامل AND وOR
</h2>

<p>
	قد نضطر في الكثير من الأحيان إلى تصفية النتائج على نحوٍ أدق مما توفره بنية <code>WHERE</code> ذات تابع شرطي وحيد. ومن ناحيةٍ أخرى، قد تكون السجلات التي تحقق واحدًا من شروط بحث متعددة مقبولة ضمن مجموعة النتائج في بعض الأحيان. وفي هذه الحالات، يمكنك كتابة بنى <code>WHERE</code> تتضمن توابع شرطية متعددة باستخدام عوامل <code>AND</code> أو <code>OR</code> على التوالي.
</p>

<p>
	للبدء باستخدام هذه العوامل، لنشغّل الاستعلام التالي الذي يعيد قيم من أعمدة <code>name</code> و<code>best</code> و<code>worst</code> و<code>average</code> في جدول <code>golfers</code>. إذ تتضمن بنية <code>WHERE</code> الخاصة بهذا الاستعلام تابعين شرطيين مفصولين بعامل <code>AND</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_56" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> best</span><span class="pun">,</span><span class="pln"> worst</span><span class="pun">,</span><span class="pln"> average
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE best </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">70</span><span class="pln"> AND worst </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">96</span><span class="pun">;</span></pre>

<p>
	يختبر التابع الشرطي الأول ما إذا كانت أفضل قيمة (قيمة العمود <code>best</code>) لكل سجل تقل عن 70، في حين يختبر التابع الشرطي الثاني ما إذا كانت أسوأ قيمة (قيمة العمود <code>worst</code>) لكل سجل تقل عن 96. الآن وفي حال تقييم أي من الشرطين على أنه "خاطئ False" لسجل ما، فإنّ هذا السجل لن يُعاد ضمن مجموعة النتائج:
</p>

<pre class="ipsCode">الخرج
+---------+------+-------+---------+
| name    | best | worst | average |
+---------+------+-------+---------+
| Pat     |   65 |    74 |    68.7 |
| Calvin  |   63 |    76 |    68.5 |
| Rose    |   69 |    84 |    76.7 |
| Raymond |   67 |    92 |    81.3 |
+---------+------+-------+---------+
4 rows in set (0.00 sec)
</pre>

<p>
	الآن، لنشغّل الاستعلام التالي، وهو كما تلاحظ مطابق للمثال السابق، باستثناء فصل التابعين الشرطيين بعامل <code>OR</code> بدلًا من <code>AND</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_58" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> best</span><span class="pun">,</span><span class="pln"> worst</span><span class="pun">,</span><span class="pln"> average
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE best </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">70</span><span class="pln"> OR worst </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">96</span><span class="pun">;</span></pre>

<p>
	نلاحظ أنّ مجموعة النتائج في هذا المثال تتضمّن سجلين أكثر من المثال السابق، وذلك نظرًا لأنّه مع استخدام عامل <code>OR</code> يكفي تقييم تابع شرطي واحد على أنّه "صحيح True" للسجل لكي يُعاد ضمن مجموعة النتائج:
</p>

<pre class="ipsCode">الخرج
+---------+------+-------+---------+
| name    | best | worst | average |
+---------+------+-------+---------+
| George  |   68 |   103 |    84.6 |
| Pat     |   65 |    74 |    68.7 |
| Diane   |   70 |    92 |    78.8 |
| Calvin  |   63 |    76 |    68.5 |
| Rose    |   69 |    84 |    76.7 |
| Raymond |   67 |    92 |    81.3 |
+---------+------+-------+---------+
6 rows in set (0.00 sec)
</pre>

<p>
	يمكنك إدراج العديد من التوابع الشرطية في بنية <code>WHERE</code> واحدة بشرط دمجها باستخدام الصياغة المناسبة. ولكن قد يصعب تنبؤ بالبيانات التي ستُصفى مع زيادة تعقيد شروط البحث.
</p>

<p>
	من الجدير بالذكر أن أنظمة قواعد البيانات تميل لإعطاء الأولوية لعوامل <code>AND</code>. هذا يعني أن التوابع الشرطية التي تفصل بينها عوامل <code>AND</code> تعدّ كشرط بحث موحد مستقل يُختبر قبل أي توابع شرطية أخرى في بنية <code>WHERE</code>.
</p>

<p>
	لتوضيح ذلك، شغّل الاستعلام التالي الذي يسترجع قيمًا من أعمدة <code>name</code> و<code>average</code> و<code>worst</code> و<code>rounds_played</code> لكل سجل يستوفي شروط البحث المحددة في بنية <code>WHERE</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_60" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> average</span><span class="pun">,</span><span class="pln"> worst</span><span class="pun">,</span><span class="pln"> rounds_played
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers 
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE average </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">85</span><span class="pln"> OR worst </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">95</span><span class="pln"> AND rounds_played BETWEEN </span><span class="lit">19</span><span class="pln"> AND </span><span class="lit">23</span><span class="pun">;</span></pre>

<p>
	يبدأ هذا الاستعلام باختبار ما إذا كان كلا التابعين الشرطيين (<code>worst &lt; 95</code> و <code>rounds_played BETWEEN 19 AND 23</code>) المفصولين بعامل <code>AND</code> يقيمان على أنهما محققان  بالنسبة للسجل قيد الاختبار الحالي. فإذا كان الأمر كذلك، سيظهر هذا السجل ضمن مجموعة النتائج.
</p>

<p>
	أمّا إذا قُيّم أحدهما أو كلاهما على أنه غير محقق "false"، فسينتقل الاستعلام لفحص ما إذا كانت قيمة <code>average</code> للصف الحالي أقل من 85. وفي حال كانت كذلك، سيظهر الصف ضمن مجموعة النتائج.
</p>

<pre class="ipsCode">الخرج
+---------+---------+-------+---------------+
| name    | average | worst | rounds_played |
+---------+---------+-------+---------------+
| George  |    84.6 |   103 |            22 |
| Pat     |    68.7 |    74 |            25 |
| Diane   |    78.8 |    92 |            23 |
| Calvin  |    68.5 |    76 |          NULL |
| Rose    |    76.7 |    84 |          NULL |
| Raymond |    81.3 |    92 |            18 |
+---------+---------+-------+---------------+
6 rows in set (0.00 sec)
</pre>

<p>
	يمكنك منح الأولوية لمجموعة من تابعين شرطيين فأكثر بوضعهم داخل أقواس. المثال التالي يطابق المثال السابق، لكنه يضع التابعين الشرطيين <code>average &lt; 85</code> و<code>worst &lt; 95</code>، المفصولين بعامل <code>OR</code>، ضمن أقواس.
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_62" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> average</span><span class="pun">,</span><span class="pln"> worst</span><span class="pun">,</span><span class="pln"> rounds_played
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE </span><span class="pun">(</span><span class="pln">average </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">85</span><span class="pln"> OR worst </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">95</span><span class="pun">)</span><span class="pln"> AND rounds_played BETWEEN </span><span class="lit">19</span><span class="pln"> AND </span><span class="lit">23</span><span class="pun">;</span></pre>

<p>
	نظرًا لأن التابعين الشرطيين في البداية مُحاطين بأقواس، فيعاملهما عامل <code>AND</code> الذي يليهما كشرط بحث مستقل، والذي يجب أن يُقيّم على أنه محقق “True”. فإذا قُيّم كلا التابعين الشرطيين (<code>average &lt; 85</code> و<code>worst &lt; 95</code>) على أنهما غير محققين “false”، فسيُعتبر شرط البحث بأكمله غير محقق “False” وبالتالي سيستبعد الاستعلام هذا السجل من مجموعة النتائج على الفور قبل أن ينتقل لتقييم السجل التالي.
</p>

<p>
	أمّا إذا قُيّم أي من التابعين الشرطيين بين القوسين على أنه محقق “True”، فيبدأ الاستعلام حينها في اختبار ما إذا كانت قيمة <code>rounds_played</code> للاعب الجولف تقع ضمن النطاق 19 و 23. وفي حال تحقق ذلك، يُضم السجل إلى مجموعة النتائج المُعادة.
</p>

<pre class="ipsCode">الخرج
+--------+---------+-------+---------------+
| name   | average | worst | rounds_played |
+--------+---------+-------+---------------+
| George |    84.6 |   103 |            22 |
| Diane  |    78.8 |    92 |            23 |
+--------+---------+-------+---------------+
2 rows in set (0.00 sec)
</pre>

<p>
	كما تُظهر هذه النتائج، بإعطاء الأولوية لمجموعات التوابع الشرطية وضمها ضمن أقواس، يمكن أن تُنتج الاستعلامات المتشابهة ظاهريًا مجموعات نتائج متباينة بدرجة كبيرة.
</p>

<p>
	مع أنه لا يُشترط دومًا اللجوء إلى هذا الأسلوب، إلا أنه يُوصى باستخدام الأقواس عند دمج أكثر من تابعين شرطيين ضمن شرط بحث واحد ليُعزز من مقروئية الاستعلامات ويسهّل فهمها.
</p>

<h2 id="not">
	استثناء النتائج باستخدام عامل NOT
</h2>

<p>
	ركّزت جميع الأمثلة في هذا المقال حتى الآن على كيفية كتابة استعلامات باستخدام بنية <code>WHERE</code>، والتي تُضمّن فقط السجلات التي تُحقّق شروط البحث المُحددة في مجموعة النتائج. ولكن يُمكنك كتابة استعلامات تستثني سجلات مُحددة عبر إدراج عامل <code>NOT</code> ضمن بنية <code>WHERE</code>.
</p>

<p>
	وعادةً ما تتبع بنى التوابع الشرطية للنطاق والعضوية وتطابق الأنماط التي تشمل عامل <code>NOT</code> الصياغة العامة التالية:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_64" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln">
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE column_name NOT OPERATOR value_expression
mysql</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span></pre>

<p>
	للتوضيح، شغّل الاستعلام التالي الذي سيعيد قيمًا من عمود <code>name</code> في جدول <code>golfers</code>، ولكن وجود عامل <code>NOT</code> في بنية <code>WHERE</code> سيُلزم نظام إدارة قاعدة البيانات باستثناء أي سجلات تُطابق نمط محرف البدل:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_66" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE name NOT LIKE </span><span class="str">'R%'</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+--------+
| name   |
+--------+
| George |
| Pat    |
| Grady  |
| Diane  |
| Calvin |
+--------+
5 rows in set (0.00 sec)
</pre>

<p>
	تتغير الأمور قليلًا عند إضافة عامل <code>NOT</code> إلى توابع <code>IS NULL</code> الشرطية. في هذه الحالات، يُدرج العامل <code>NOT</code> بين <code>IS</code> و<code>NULL</code> كما في المثال التالي. يُعيد هذا الاستعلام قيم <code>name</code> و<code>rounds_played</code> لجميع لاعبي الغولف الذين لا تكون قيمة <code>rounds_played</code> الخاصة بهم فارغة، على النحو:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_68" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> rounds_played
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE rounds_played IS NOT NULL</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+---------+---------------+
| name    | rounds_played |
+---------+---------------+
| George  |            22 |
| Pat     |            25 |
| Grady   |            11 |
| Diane   |            23 |
| Raymond |            18 |
+---------+---------------+
5 rows in set (0.00 sec)
</pre>

<p>
	كما يُمكنك وضع عامل <code>NOT</code> مباشرةً بعد الكلمة المفتاحية <code>WHERE</code>. الأمر الذي يعدّ مفيدًا لا سيما في حال رغبتك باستثناء سجلات بناءً على ما إذا كانت تحقق عدة شروط بحث، كما في الاستعلام التالي الذي يعيد قيم كل من أعمدة <code>name</code> و <code>average</code> و <code>best</code> و <code>wins</code> للاعبي الغولف.
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_72" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> average</span><span class="pun">,</span><span class="pln"> best</span><span class="pun">,</span><span class="pln"> wins
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE NOT </span><span class="pun">(</span><span class="pln">average </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">80</span><span class="pln"> AND best </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">70</span><span class="pun">)</span><span class="pln"> OR wins </span><span class="pun">=</span><span class="pln"> </span><span class="lit">9</span><span class="pun">;</span></pre>

<pre class="ipsCode">الخرج
+---------+---------+------+------+
| name    | average | best | wins |
+---------+---------+------+------+
| George  |    84.6 |   68 |    3 |
| Pat     |    68.7 |   65 |    9 |
| Grady   |    97.6 |   78 |    0 |
| Diane   |    78.8 |   70 |    1 |
| Raymond |    81.3 |   67 |    1 |
+---------+---------+------+------+
5 rows in set (0.00 sec)
</pre>

<p>
	بالنظر إلى السجل الثاني من مجموعة النتائج أعلاه، نلاحظ أنّ متوسط نتيجة اللاعبة <code>Pat</code> في العمود <code>average</code> أقل من 80 وأفضل نتيجة لها في العمود <code>best</code> أقل من 70. ومع ذلك سجلها مُضمّن في مجموعة النتائج، وذلك نظرًا لأن عامل <code>NOT</code> يُلغي فقط شروط البحث المُحاطة بالأقواس.
</p>

<p>
	تذكر أنه عند إحاطة توابع شرطية متعددة مفصولة بعوامل <code>AND</code> أو <code>OR</code> بأقواس، تُعطى الأولوية لهذه التوابع في SQL وتُعامل كشرط بحث مستقل. بالتالي، يستثني عامل <code>NOT</code> السجلات بناءً على أول تابعي شرط فقط (<code>average&lt;80</code> و <code>best&lt;70</code>). في حين تُضمّن السجلات بناءً على التابع الشرطي الثالث (<code>wins=9</code>).
</p>

<p>
	يمكنك إعادة كتابة الاستعلام لاستبعاد السجلات بناءً على التابع الشرطي الثالث أيضًا بالإضافة إلى التابعين الأول والثاني وذلك بإحاطة التوابع الثلاثة ضمن أقواس، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_74" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name</span><span class="pun">,</span><span class="pln"> average</span><span class="pun">,</span><span class="pln"> best</span><span class="pun">,</span><span class="pln"> wins
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE NOT </span><span class="pun">((</span><span class="pln">average </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">80</span><span class="pln"> AND best </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">70</span><span class="pun">)</span><span class="pln"> OR wins </span><span class="pun">=</span><span class="pln"> </span><span class="lit">9</span><span class="pun">);</span></pre>

<pre class="ipsCode">الخرج
+---------+---------+------+------+
| name    | average | best | wins |
+---------+---------+------+------+
| George  |    84.6 |   68 |    3 |
| Grady   |    97.6 |   78 |    0 |
| Diane   |    78.8 |   70 |    1 |
| Raymond |    81.3 |   67 |    1 |
+---------+---------+------+------+
4 rows in set (0.00 sec)
</pre>

<p>
	قد يعدّ نظام قاعدة البيانات لديك صيغة الاستعلام غير صالحة في حال تضمين العامل <code>NOT</code> قبل عامل مقارنة وذلك تبعًا لتقديم SQL المُستخدم. كمثال، لنجرب تشغيل الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2540_76" style=""><span class="pln">mysql</span><span class="pun">&gt;</span><span class="pln"> SELECT name
mysql</span><span class="pun">&gt;</span><span class="pln"> FROM golfers
mysql</span><span class="pun">&gt;</span><span class="pln"> WHERE name NOT </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Grady'</span><span class="pun">;</span></pre>

<p>
	سيؤدي استخدام هذا الأمر في MySQL أو أي من الأنظمة المستندة إليها إلى حدوث خطأ.
</p>

<pre class="ipsCode">الخرج
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '= 'Grady'' at line 1
</pre>

<p>
	السبب وراء هذا الخطأ هو أنّ عامل <code>NOT</code> لا يُستخدم عادةً مع عوامل المقارنة (<code>=</code> و<code>&lt;&gt;</code> و<code>&lt;</code> و<code>&lt;=</code> و<code>&gt;</code> و<code>&gt;=</code>)، وذلك نظرًا لإمكانية تحقيق الأثر المعاكس لعامل مقارنة معيّن باستبداله بآخر يعيد السجلات التي كان الأول سيستثنيها. على سبيل المثال، يمكنك استبدال عامل المساواة (<code>=</code>) بعامل عدم المساواة (<code>&lt;&gt;</code>).
</p>

<h2 id="-6">
	الخلاصة
</h2>

<p>
	باطلاعك على هذا المقال،تكون قد اكتسبت المعرفة حول كيفية كتابة بنى <code>WHERE</code> بطريقة تجعل الاستعلام يُعيد فقط السجلات التي تستوفي شرطًا معينًا. كما تعلمت كيفية دمج عدّة توابع شرطية وشروط بحث في استعلام واحد، ناهيك عن كيفية استخدام العامل <code>NOT</code> لاستثناء معلومات من مجموعات النتائج.
</p>

<p>
	ومن المفترض أن تعمل الأوامر المشروحة في هذا المقال مع أي نظام لإدارة قواعد البيانات يستخدم SQL. لكن تذكر أن لكل قاعدة بيانات SQL تقديمها الخاص للغة، لذا ينبغي مراجعة التوثيق الرسمي لنظام إدارة قواعد البيانات الخاص بك للحصول على وصف أكثر تفصيلاً لكل أمر فيها ومجموعة خياراته الكاملة.
</p>

<p>
	وللمزيد حول SQL، نشجعك على متابعة <a href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20%D8%AA%D8%B9%D9%84%D9%85%20sql/" rel="">المقالات المنشورة تحت وسم سلسلة تعلم SQL</a> في أكاديمية حسوب.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-where-clauses-in-sql" rel="external nofollow">How To Use WHERE Clauses in SQL</a> لصاحبه Mark Drake.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/sql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%B9%D9%86-%D8%A7%D9%84%D8%B3%D8%AC%D9%84%D8%A7%D8%AA-%D9%85%D9%86-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r2249/" rel="">كيفية الاستعلام عن السجلات من الجداول في SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/%D9%85%D8%AD%D8%B1%D9%83%D8%A7%D8%AA-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/" rel="">ما هي محركات قواعد البيانات؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/" rel="">أنواع قواعد البيانات وأهم مميزاتها واستخداماتها</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r602/" rel="">التعامل مع قواعد البيانات</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2258</guid><pubDate>Fri, 23 Feb 2024 12:02:00 +0000</pubDate></item></channel></rss>
