{"id":"l-sql-2-2","courseId":"sql-for-analysts","lang":"ar","kind":"lesson","summary":"اربط الجداول بـ INNER JOIN وLEFT JOIN على المفاتيح الصحيحة، وحدّد الجدول لكل عمود يتكرّر اسمه، واحتفظ بالصفوف غير المطابقة بوضع شروط الجدول الأيمن في عبارة ON.","html":"<p>يسأل كريم من المالية: «كم كان إيرادنا في 2025 حسب فئة المنتج؟». الإيراد موجود في <code dir=\"ltr\">order_items</code>، والسنة في <code dir=\"ltr\">orders</code>، واسم الفئة في <code dir=\"ltr\">categories</code>، على بُعد خطوتين عبر <code dir=\"ltr\">products</code>. لا يوجد جدول واحد يجيب عن السؤال. الـ JOIN يجمعها معًا، وضبطه يدور في معظمه حول معرفة الصفوف التي يُبقيها كل JOIN.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">تتبّع المفاتيح</h2>\n<p>كل JOIN يحتاج إلى شرط يقول أيّ الصفوف تنتمي إلى بعضها، وهو في الغالب مفتاح: <code dir=\"ltr\">order_items.order_id</code> يشير إلى <code dir=\"ltr\">orders.order_id</code>، و<code dir=\"ltr\">order_items.product_id</code> إلى <code dir=\"ltr\">products.product_id</code>، وهكذا. في Cartwheel يُحسب الإيراد من كل الطلبات ما عدا الملغاة.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"sql\" data-run=\"sql\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">SQL</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"sql\" data-index=\"0\" aria-label=\"شغّل هذا المثال\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>تشغيل</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">SELECT</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  c</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">name</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> AS</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> category,</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  COUNT</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">DISTINCT</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> o</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">order_id</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">AS</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders,</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  ROUND</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">SUM</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">oi</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">quantity</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> *</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> oi</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">unit_price</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> *</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> -</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> oi</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">discount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)), </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">AS</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> revenue</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">FROM</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">AS</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> o</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">JOIN</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> order_items </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">AS</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> oi </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">ON</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> oi</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">order_id</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> o</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">order_id</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">JOIN</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> products    </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">AS</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> p  </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">ON</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">product_id</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> oi</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">product_id</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">JOIN</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> categories  </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">AS</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> c  </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">ON</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> c</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">category_id</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">category_id</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">WHERE</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> o</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">status</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> &#x3C;></span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'cancelled'</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  AND</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> o</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">order_date</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> >=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> '2025-01-01'</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  AND</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> o</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">order_date</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> &#x3C;</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  '2026-01-01'</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">GROUP BY</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> c</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">name</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">ORDER BY</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> revenue </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">DESC</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span></code></pre><div class=\"run-output\" data-run-output=\"0\" hidden></div></div>\n<p>تتصدّر Desks &amp; Chairs بنحو 77,000. لكن صفّين يبدوان غريبين: “Kitchen” و&quot;Outdoor&quot; فئتان في المستوى الأعلى، ويُفترض أن تقع المنتجات في فئات فرعية. احتفظ بهذه الملاحظة؛ إنها اكتشاف في جودة البيانات ستتعقّبه في القسم 4.</p>\n<p>عادتان تجعلان الـ queries التي فيها JOIN مقروءة وآمنة. الأسماء المستعارة القصيرة (<code dir=\"ltr\">o</code>، <code dir=\"ltr\">oi</code>، <code dir=\"ltr\">p</code>، <code dir=\"ltr\">c</code>) تُبقي الأسطر قصيرة. ونسبة كل عمود إلى اسمه المستعار تزيل التخمين، وهذا مهم هنا: <code dir=\"ltr\">order_items.unit_price</code> هو السعر المدفوع يوم الطلب، و<code dir=\"ltr\">products.unit_price</code> هو سعر القائمة اليوم. كتابة <code dir=\"ltr\">unit_price</code> بلا نسبة في هذا الـ query تعطي خطأ “ambiguous column name”، واختيار العمود الخطأ يغيّر الإيراد بصمت.</p>\n<p>و<code dir=\"ltr\">COUNT(DISTINCT o.order_id)</code> مقصود أيضًا. بعد ربط العناصر، يظهر كل طلب مرة لكل سطر عنصر، فيعدّ <code dir=\"ltr\">COUNT(*)</code> السطور. وهذا التكرار هو موضوع الدرس التالي.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">INNER مقابل LEFT</h2>\n<p><code dir=\"ltr\">JOIN</code> تعني <code dir=\"ltr\">INNER JOIN</code>: لا يبقى الصف إلا إذا وجد تطابقًا في الجانب الآخر. هذا صحيح للإيراد، حيث لا معنى لعنصر بلا طلب. لكنه خاطئ حين يكون السؤال عن <em>كل ما في جانب واحد</em>، بما في ذلك ما ليس له تطابق.</p>\n<p>تطلب جوليا عدد التذاكر لكل موظف في العمليات، «بما في ذلك من لا يتولّون التذاكر، حتى أرى من يمكنه المساعدة في موسم الذروة»:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"sql\" data-run=\"sql\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">SQL</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"sql\" data-index=\"1\" aria-label=\"شغّل هذا المثال\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>تشغيل</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">SELECT</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  e</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">employee_id</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  e</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">first_name</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  e</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">title</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  COUNT</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">t</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">ticket_id</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">AS</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> tickets</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">FROM</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> employees </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">AS</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> e</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">LEFT JOIN</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> support_tickets </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">AS</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> t</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  ON</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> t</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">agent_id</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> e</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">employee_id</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">WHERE</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> e</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">department</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'Operations'</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">GROUP BY</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> e</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">employee_id</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">ORDER BY</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> tickets </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">DESC</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span></code></pre><div class=\"run-output\" data-run-output=\"1\" hidden></div></div>\n<p>ثمانية صفوف. لدى كل واحد من الوكلاء الأربعة نحو 200 تذكرة؛ ولدى مارك وسمير وبن وآدم صفر. يُبقي الـ <code dir=\"ltr\">LEFT JOIN</code> كل صف من الجدول الأيسر، وهو هنا <code dir=\"ltr\">employees</code>، وحيث لا تطابقه أي تذكرة تكون أعمدة التذاكر NULL. يتخطّى <code dir=\"ltr\">COUNT(t.ticket_id)</code> هذه القيم، ولهذا يظهر لغير الوكلاء 0 لا 1.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 720 230\" role=\"img\" aria-labelledby=\"l-sql-2-2-t1\"><defs><marker id=\"arrow-l-sql-2-2-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-sql-2-2-t1\">على اليسار الموظفون جوليا وتوم ومارك؛ وعلى اليمين التذكرتان t1 وt2 اللتان تولّتهما جوليا وt3 التي تولّاها توم. نتيجة الـ inner join فيها ثلاثة صفوف. ونتيجة الـ left join فيها الصفوف الثلاثة نفسها إضافةً إلى مارك بتذكرة قيمتها NULL.</title>\n  <text class=\"d-label-strong\" x=\"85\" y=\"28\" text-anchor=\"middle\">employees</text>\n  <rect class=\"d-box\" x=\"20\" y=\"40\" width=\"130\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-label\" x=\"85\" y=\"62\" text-anchor=\"middle\">6 Julia</text>\n  <rect class=\"d-box\" x=\"20\" y=\"84\" width=\"130\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-label\" x=\"85\" y=\"106\" text-anchor=\"middle\">8 Tom</text>\n  <rect class=\"d-box-warn\" x=\"20\" y=\"128\" width=\"130\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-label\" x=\"85\" y=\"150\" text-anchor=\"middle\">2 Mark</text>\n  <text class=\"d-label-strong\" x=\"285\" y=\"28\" text-anchor=\"middle\">tickets</text>\n  <rect class=\"d-box\" x=\"220\" y=\"40\" width=\"130\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"285\" y=\"62\" text-anchor=\"middle\">t1 agent 6</text>\n  <rect class=\"d-box\" x=\"220\" y=\"84\" width=\"130\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"285\" y=\"106\" text-anchor=\"middle\">t2 agent 6</text>\n  <rect class=\"d-box\" x=\"220\" y=\"128\" width=\"130\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"285\" y=\"150\" text-anchor=\"middle\">t3 agent 8</text>\n  <path class=\"d-line\" d=\"M150 57 L220 57\"/>\n  <path class=\"d-line\" d=\"M150 57 L220 101\"/>\n  <path class=\"d-line\" d=\"M150 101 L220 145\"/>\n  <text class=\"d-label-strong\" x=\"490\" y=\"28\" text-anchor=\"middle\">INNER</text>\n  <rect class=\"d-box-success\" x=\"430\" y=\"40\" width=\"120\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"490\" y=\"62\" text-anchor=\"middle\">Julia t1</text>\n  <rect class=\"d-box-success\" x=\"430\" y=\"84\" width=\"120\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"490\" y=\"106\" text-anchor=\"middle\">Julia t2</text>\n  <rect class=\"d-box-success\" x=\"430\" y=\"128\" width=\"120\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"490\" y=\"150\" text-anchor=\"middle\">Tom t3</text>\n  <text class=\"d-label-strong\" x=\"640\" y=\"28\" text-anchor=\"middle\">LEFT</text>\n  <rect class=\"d-box-success\" x=\"580\" y=\"40\" width=\"120\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"640\" y=\"62\" text-anchor=\"middle\">Julia t1</text>\n  <rect class=\"d-box-success\" x=\"580\" y=\"84\" width=\"120\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"640\" y=\"106\" text-anchor=\"middle\">Julia t2</text>\n  <rect class=\"d-box-success\" x=\"580\" y=\"128\" width=\"120\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"640\" y=\"150\" text-anchor=\"middle\">Tom t3</text>\n  <rect class=\"d-box-warn\" x=\"580\" y=\"172\" width=\"120\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-code\" x=\"640\" y=\"194\" text-anchor=\"middle\">Mark NULL</text>\n</svg><figcaption>الـ INNER JOIN يُبقي المتطابقات؛ والـ LEFT JOIN يُبقي أيضًا صفوف اليسار غير المطابقة</figcaption></figure>\n<h2 id=\"sec-3\" tabindex=\"-1\">ON مقابل WHERE في الـ LEFT JOIN</h2>\n<p>الآن تضيّق جوليا الطلب: «القائمة نفسها، لكن اعدد التذاكر العاجلة فقط». التعديل البديهي يضيف <code dir=\"ltr\">AND t.priority = 'urgent'</code> إلى عبارة WHERE، فيختفي غير الوكلاء الأربعة. صفوفهم فيها <code dir=\"ltr\">t.priority</code> قيمته NULL، فيكون الشرط NULL، ويحذفهم WHERE. الشرط على الجدول الأيمن في WHERE يعيد الـ LEFT JOIN إلى INNER JOIN.</p>\n<p>مكان هذا الشرط هو عبارة <code dir=\"ltr\">ON</code>، حيث يحدّد أي التذاكر <em>تتطابق</em>، لا أي الصفوف <em>تبقى</em>:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"sql\" data-run=\"sql\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">SQL</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"sql\" data-index=\"2\" aria-label=\"شغّل هذا المثال\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>تشغيل</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">SELECT</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  e</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">first_name</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  COUNT</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">t</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">ticket_id</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">AS</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> urgent_tickets</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">FROM</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> employees </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">AS</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> e</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">LEFT JOIN</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> support_tickets </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">AS</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> t</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  ON</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> t</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">agent_id</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> e</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">employee_id</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> AND</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> t</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">priority</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'urgent'</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">WHERE</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> e</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">department</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'Operations'</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">GROUP BY</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> e</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">employee_id</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">ORDER BY</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> urgent_tickets </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">DESC</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span></code></pre><div class=\"run-output\" data-run-output=\"2\" hidden></div></div>\n<p>ثمانية صفوف من جديد، والأصفار في أماكنها. القاعدة: الشروط على الجدول <strong>الأيسر</strong> في WHERE؛ والشروط على الجدول <strong>الأيمن</strong> في ON.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">اختيار الـ JOIN، والجدول الذي تبدأ منه</h2>\n<p>طريقة موثوقة للقرار: ابدأ <code dir=\"ltr\">FROM</code> بالجدول الذي تريد صفوفه في الإجابة، أي الجدول الذي يطابق مستوى التفصيل الذي كتبته. ثم اسأل عن كل جدول آخر: «هل هذه المعلومة إلزامية أم اختيارية؟». المعلومة الإلزامية تأخذ inner join، لأن الصف الذي يفتقدها لا مكان له في الإجابة. والمعلومة الاختيارية تأخذ LEFT JOIN، حتى تظهر التطابقات المفقودة كقيم NULL يمكنك عدّها أو تسميتها أو تحويلها إلى أصفار.</p>\n<p>في قائمة جوليا تدور الإجابة حول الموظفين، فيأتي <code dir=\"ltr\">employees</code> أولًا، والتذاكر اختيارية. وفي إيراد كريم تدور الإجابة حول المبيعات، فكل جدول في السلسلة إلزامي.</p>\n<p>يدعم SQLite أيضًا <code dir=\"ltr\">RIGHT JOIN</code> و<code dir=\"ltr\">FULL OUTER JOIN</code> (منذ الإصدار 3.39)، وPostgreSQL يدعمهما منذ عقود. نادرًا ما ستحتاج إليهما: الـ right join هو left join مع تبديل الجدولين، ومعظم الفرق تكتب كل شيء بـ LEFT JOIN حتى تُقرأ الـ queries بالطريقة نفسها، من الأعلى إلى الأسفل، من الجدول الرئيسي نحو الخارج.</p>\n<p>أخيرًا، افحص عدد الصفوف بعد كل JOIN تضيفه. إذا كان في <code dir=\"ltr\">employees</code> بعد تصفيته على العمليات 8 صفوف، فيجب أن يعيد الـ LEFT JOIN مع GROUP BY ثمانية أيضًا. الرقم الذي يكبر أو يصغر على غير المتوقع يخبرك أن JOIN ما يفعل شيئًا لم تقصده.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>نسيان شرط الـ JOIN</span></p>\n<p><code dir=\"ltr\">FROM orders o JOIN order_items oi</code> بلا <code dir=\"ltr\">ON</code> صياغة مقبولة في SQLite. إنها تقرن كل طلب بكل عنصر، فتنتج أكثر من ثمانية ملايين صف، وتصبح كل المجاميع هراءً. إذا كان query فيه JOIN بطيئًا ومجاميعه هائلة، فابحث أولًا عن ON مفقود أو خاطئ.</p>\n</aside>\n<p>كان في إيراد الفئات فخ آخر لم يُصبنا، لأن الإيراد كان في الجدول الأكثر تفصيلًا. حين يقع الرقم في الجانب <em>الأقل</em> تفصيلًا من الـ JOIN، تتضخّم المجاميع. وهذا هو موضوعنا التالي.</p>\n","toc":[{"id":"sec-1","text":"تتبّع المفاتيح","level":2},{"id":"sec-2","text":"INNER مقابل LEFT","level":2},{"id":"sec-3","text":"ON مقابل WHERE في الـ LEFT JOIN","level":2},{"id":"sec-4","text":"اختيار الـ JOIN، والجدول الذي تبدأ منه","level":2}],"takeaways":["الـ INNER JOIN لا يُبقي إلا الصفوف المتطابقة في الجانبين؛ والـ LEFT JOIN يُبقي كل صف من الجدول الأيسر ويملأ أعمدة الجانب الأيمن غير المطابقة بـ NULL.","أعطِ كل جدول اسمًا مستعارًا قصيرًا وانسب إليه كل عمود، خاصةً الأسماء الموجودة في أكثر من جدول مثل unit_price.","الشرط على الجدول الأيمن في LEFT JOIN مكانه عبارة ON؛ أما في WHERE فيحذف صفوف NULL ويعيد الـ JOIN إلى inner join.","بعد LEFT JOIN اعدد عمودًا من الجدول الأيمن لا *، حتى تُحسب الصفوف غير المطابقة 0 لا 1."],"further":[{"title":"SQLite SELECT, the FROM clause and joins","url":"https://www.sqlite.org/lang_select.html#fromclause"},{"title":"PostgreSQL tutorial on joins between tables","url":"https://www.postgresql.org/docs/current/tutorial-join.html"}],"quiz":[{"q":"<p>في <code dir=\"ltr\">orders</code> يوجد 2,066 صفًا وفي <code dir=\"ltr\">order_items</code> يوجد 3,991. لكل طلب عنصر واحد على الأقل. كم صفًا يعيد <code dir=\"ltr\">orders o JOIN order_items oi ON oi.order_id = o.order_id</code>؟</p>","options":[{"text":"2,066، صف لكل طلب","why":"الطلب الذي فيه ثلاثة عناصر يطابق ثلاثة صفوف عناصر، فيظهر ثلاث مرات. يأخذ الـ JOIN مستوى تفصيل جانب «المتعدد»."},{"text":"6,057، الجدولان مكدّسان فوق بعضهما","why":"تكديس الصفوف هو UNION ALL. أما الـ JOIN فيضع الأعمدة جنبًا إلى جنب للصفوف المتطابقة."},{"text":"8,245,406، كل تركيبة ممكنة","why":"هذا cross join، وهو ما تحصل عليه إن نسيت شرط ON."},{"text":"3,991، صف لكل عنصر طلب","why":"صحيح. كل عنصر يطابق طلبًا واحدًا بالضبط، فيكون في النتيجة صف لكل عنصر، مع تكرار أعمدة الطلب."}],"answer":3},{"q":"<p>يُفترض أن يعرض هذا كل عميل في المملكة المتحدة مع عدد طلباته من الـ marketplace، بما فيها الصفر. لماذا يختفي العملاء الذين ليس لديهم طلبات من الـ marketplace؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"sql\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">SQL</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">SELECT</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> c</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">customer_id</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">COUNT</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">o</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">order_id</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">AS</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> marketplace_orders</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">FROM</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> customers c</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">LEFT JOIN</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders o </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">ON</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> o</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">customer_id</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> c</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">customer_id</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">WHERE</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> c</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">country</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'United Kingdom'</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  AND</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> o</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">channel</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'marketplace'</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">GROUP BY</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> c</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">customer_id</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span></code></pre></div>\n","options":[{"text":"يجب أن يكون <code dir=\"ltr\">COUNT(o.order_id)</code> هو <code dir=\"ltr\">COUNT(*)</code>.","why":"تعبير العدّ ليس المشكلة؛ العملاء المفقودون لا يصلون إلى GROUP BY أصلًا."},{"text":"اختبار <code dir=\"ltr\">o.channel</code> قيمته NULL للعملاء الذين ليس لهم طلب مطابق، فيحذفهم WHERE.","why":"صحيح. انقل <code dir=\"ltr\">AND o.channel = 'marketplace'</code> إلى عبارة ON؛ ويبقى اختبار الدولة في WHERE لأنه يخص الجدول الأيسر."},{"text":"الـ LEFT JOIN لا يُبقي إلا الصفوف غير المطابقة من الجدول الأيمن.","why":"العكس هو الصحيح. يحتفظ الـ LEFT JOIN بكل صف من الجدول الأيسر، وهو هنا <code dir=\"ltr\">customers</code>."}],"answer":1},{"q":"<p>يفشل <code dir=\"ltr\">SELECT unit_price FROM order_items oi JOIN products p ON p.product_id = oi.product_id</code>. لماذا؟</p>","options":[{"text":"<code dir=\"ltr\">unit_price</code> موجود في الجدولين، فالاسم ملتبس.","why":"صحيح. اكتب <code dir=\"ltr\">oi.unit_price</code> (السعر المدفوع) أو <code dir=\"ltr\">p.unit_price</code> (سعر القائمة). وهما رقمان مختلفان بعد رفع الأسعار في مارس 2025."},{"text":"لا يمكنك اختيار عمود استُخدم في JOIN.","why":"يمكن اختيار أي عمود؛ ومفتاح الـ JOIN ليس طرفًا هنا أصلًا."},{"text":"الأسماء المستعارة يجب أن تُعرَّف بـ AS.","why":"AS اختيارية للأسماء المستعارة للجداول في SQLite وPostgreSQL."}],"answer":0},{"q":"<p>بعد <code dir=\"ltr\">employees e LEFT JOIN support_tickets t ON t.agent_id = e.employee_id</code>، أي تعبير يعطي 0 تذاكر للموظفين الذين لم يتولّوا أي تذكرة؟</p>","options":[{"text":"<code dir=\"ltr\">COUNT(*)</code>","why":"الموظف غير المطابق ما زال ينتج صفًا واحدًا (بأعمدة تذاكر قيمتها NULL)، فيعيد COUNT(*) القيمة 1."},{"text":"<code dir=\"ltr\">COUNT(e.employee_id)</code>","why":"معرّف الموظف ليس NULL أبدًا، فهذا أيضًا يعدّ الصف غير المطابق 1."},{"text":"<code dir=\"ltr\">COUNT(t.ticket_id)</code>","why":"صحيح. معرّف التذكرة قيمته NULL في الصف غير المطابق، وCOUNT يتخطّى NULL."},{"text":"<code dir=\"ltr\">SUM(t.ticket_id)</code>","why":"هذا يجمع أرقام المعرّفات، وهو بلا معنى، ويعيد NULL للموظفين غير المطابقين."}],"answer":2}],"exercise":{"kind":"sql","starter":"-- Every Jordan customer with their 2025 orders (zero included)\nSELECT\n  c.customer_id,\n  c.first_name,\n  COUNT(o.order_id) AS orders_2025\nFROM customers AS c\nLEFT JOIN orders AS o\n  ON o.customer_id = c.customer_id\nWHERE c.country = 'Jordan'\n  AND o.status <> 'cancelled'\n  AND o.order_date >= '2025-01-01'\n  AND o.order_date < '2026-01-01'\nGROUP BY c.customer_id;","solution":"SELECT\n  c.customer_id,\n  c.first_name,\n  COUNT(o.order_id) AS orders_2025\nFROM customers AS c\nLEFT JOIN orders AS o\n  ON o.customer_id = c.customer_id\n AND o.status <> 'cancelled'\n AND o.order_date >= '2025-01-01'\n AND o.order_date < '2026-01-01'\nWHERE c.country = 'Jordan'\nGROUP BY c.customer_id, c.first_name;","orderMatters":false,"title":"كل عملاء الأردن، بما فيهم الأصفار","prompt":"<p>تخطّط هدى لرسالة بريد لاستعادة العملاء في الأردن، وتحتاج إلى <strong>كل عميل في الأردن</strong> مع عدد <strong>الطلبات غير الملغاة التي قدّمها في 2025</strong>، مع إظهار 0 لمن لم يطلب.</p>\n<p>أعِد <code dir=\"ltr\">customer_id</code> و<code dir=\"ltr\">first_name</code> و<code dir=\"ltr\">orders_2025</code>. ترتيب الصفوف لا يهم. في الأردن 32 عميلًا، فتوقّع 32 صفًا؛ والكود المبدئي يعيد أقل من ذلك.</p>\n","hints":["أي العملاء مفقودون من نتيجة الكود المبدئي؟ ما قيم <code dir=\"ltr\">o.status</code> و<code dir=\"ltr\">o.order_date</code> لديهم بعد الـ LEFT JOIN؟","الشروط على الجدول الأيمن (<code dir=\"ltr\">orders</code>) تحدّد أي الطلبات تتطابق. ومكانها عبارة ON.","أبقِ <code dir=\"ltr\">c.country = 'Jordan'</code> في WHERE (لأنه يخص الجدول الأيسر)، وانقل اختباري الحالة والتاريخ إلى <code dir=\"ltr\">ON ... AND ...</code>."],"explanation":"<p>لعميل في الأردن ليس لديه طلبات في 2025، ينتج الـ LEFT JOIN صفًا واحدًا كل أعمدة <code dir=\"ltr\">o.</code> فيه NULL. في الكود المبدئي تكون قيمة <code dir=\"ltr\">o.status &lt;&gt; 'cancelled'</code> لهذا الصف NULL، فيرميه WHERE ويختفي العميل. نقل شروط الطلبات إلى ON يعني أنها تحدّد فقط أي الطلبات تُرفق؛ ويبقى صف العميل في الحالتين، ويعدّ <code dir=\"ltr\">COUNT(o.order_id)</code> له 0. تسعة عملاء لم يطلبوا شيئًا في 2025، وهم بالضبط من تستهدفهم رسالة الاستعادة.</p>\n","checkLabels":{}},"words":706,"readMinutes":4}