0Pricing
SQL Academy · درس

EXISTS وNOT EXISTS

تحقّق بكفاءة من وجود صفوف مرتبطة

EXISTS وNOT EXISTS درس مجاني في SQL Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في SQL Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة SQL Academy 4 دروس في المجموع.

ما هو EXISTS؟

يختبر العامل EXISTS ما إذا كان الاستعلام الفرعي يعيد صفًا واحدًا على الأقل. ويُقيّم إلى TRUE إذا أنتج الاستعلام الفرعي أي نتيجة، وإلى FALSE إذا كان فارغًا.

وعلى خلاف عوامل الاستعلامات الفرعية الأخرى التي تقارن القيم، يهتم EXISTS فقط بوجود نتيجة، ولا يفحص البيانات الفعلية التي يعيدها الاستعلام الفرعي.

إعداد الجداول النموذجية

قبل كتابة استعلامات EXISTS، لننشئ جدولين: customers وorders. سنستخدمهما طوال الدرس لاستكشاف كيفية عمل EXISTS وNOT EXISTS عمليًا.

CREATE TABLE customers (
  customer_id INT PRIMARY KEY,
  name        VARCHAR(100),
  country     VARCHAR(50)
);

CREATE TABLE orders (
  order_id    INT PRIMARY KEY,
  customer_id INT,
  amount      DECIMAL(10,2),
  order_date  DATE
);

INSERT INTO customers VALUES
  (1, 'Alice',   'US'),
  (2, 'Bob',     'UK'),
  (3, 'Charlie', 'US'),
  (4, 'Diana',   'DE');

INSERT INTO orders VALUES
  (101, 1, 250.00, '2024-01-10'),
  (102, 1, 180.00, '2024-02-15'),
  (103, 2,  95.00, '2024-03-01'),
  (104, 3, 430.00, '2024-03-22');

الصيغة الأساسية لـ EXISTS

تضع الصيغة الأساسية لـ EXISTS هذا العامل داخل عبارة WHERE. وعادةً ما يشير الاستعلام الفرعي الموجود داخل EXISTS إلى عمود من الاستعلام الخارجي، ويُسمى ذلك استعلامًا فرعيًا مترابطًا.

يعثر الاستعلام أدناه على كل عميل أجرى طلبًا واحدًا على الأقل.

SELECT customer_id, name
FROM customers c
WHERE EXISTS (
  SELECT 1
  FROM orders o
  WHERE o.customer_id = c.customer_id
);

SELECT 1 داخل EXISTS

ربما لاحظتم أن الاستعلام الفرعي يستخدم SELECT 1 بدلًا من اختيار أي عمود فعلي. وهذا مقصود — إذ يتحقق EXISTS من وجود الصفوف فقط، لا من محتوياتها.

لا يُحدث استخدام SELECT 1 (أو حتى SELECT *) أي فرق في النتيجة، لكن SELECT 1 يوضح لمحرك قاعدة البيانات وللقارئ أن القيم الفعلية لا تهم.

-- Both of these return the same result
SELECT name FROM customers c
WHERE EXISTS (SELECT 1   FROM orders o WHERE o.customer_id = c.customer_id);

SELECT name FROM customers c
WHERE EXISTS (SELECT o.* FROM orders o WHERE o.customer_id = c.customer_id);

كيفية تقييم قاعدة البيانات لـ EXISTS

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

في المقابل، ينشئ JOIN المجموعة الكاملة من الصفوف المطابقة قبل إجراء التصفية، وقد يكون ذلك أبطأ عندما تحتاجون فقط إلى معرفة ما إذا كان التطابق موجودًا.

-- EXISTS short-circuits after first match
-- Efficient even when orders table has millions of rows
SELECT name, country
FROM customers c
WHERE EXISTS (
  SELECT 1
  FROM orders o
  WHERE o.customer_id = c.customer_id
    AND o.amount > 200
);

NOT EXISTS: العثور على الصفوف غير الموجودة

NOT EXISTS هو العكس: إذ يُعيد TRUE عندما لا يجد الاستعلام الفرعي أي صفوف مطابقة. وهذه هي الطريقة القياسية في SQL للإجابة عن أسئلة مثل «ما العملاء الذين لم يضعوا أي طلب من قبل؟»

قد تؤدي محاولة حل ذلك باستخدام JOIN عادي أو NOT IN إلى نتائج غير صحيحة عند وجود قيم NULL — أما NOT EXISTS فيتجنب هذه المشكلة تمامًا.

-- Customers who have NOT placed any order
SELECT customer_id, name
FROM customers c
WHERE NOT EXISTS (
  SELECT 1
  FROM orders o
  WHERE o.customer_id = c.customer_id
);

مقارنة NOT EXISTS بـ NOT IN عند وجود NULL

تتمثل إحدى المزايا المهمة لـ NOT EXISTS مقارنةً بـ NOT IN في الأمان عند وجود NULL. فإذا أعاد الاستعلام الفرعي المستخدم مع NOT IN قيمة NULL واحدة فقط، يصبح تعبير NOT IN بأكمله NULL — ما يعني أن الاستعلام الخارجي لن يُعيد أي صفوف.

لا يتأثر NOT EXISTS بهذه المشكلة لأنه يقيّم وجود الصفوف، لا تساوي القيم.

-- Dangerous: if any customer_id in orders is NULL,
-- NOT IN returns zero rows!
SELECT name FROM customers
WHERE customer_id NOT IN (SELECT customer_id FROM orders);

-- Safe: NOT EXISTS handles NULLs correctly
SELECT name FROM customers c
WHERE NOT EXISTS (
  SELECT 1 FROM orders o
  WHERE o.customer_id = c.customer_id
);

EXISTS مع شروط متعددة

يمكن أن يحتوي الاستعلام الفرعي داخل EXISTS على أي SQL صالح، بما في ذلك شروط WHERE متعددة. ويتيح لكم ذلك البحث عن صفوف ذات صلة بمواصفات دقيقة جدًا — مثل العملاء الذين وضعوا طلبًا يتجاوز حدًا معينًا في شهر محدد.

-- Customers who placed an order over 200 in March 2024
SELECT c.name, c.country
FROM customers c
WHERE EXISTS (
  SELECT 1
  FROM orders o
  WHERE o.customer_id = c.customer_id
    AND o.amount > 200
    AND o.order_date >= '2024-03-01'
    AND o.order_date <  '2024-04-01'
);

EXISTS في DELETE وUPDATE

لا يقتصر EXISTS على عبارات SELECT، بل يمكنكم استخدامه في UPDATE وDELETE لتعديل الصفوف أو حذفها استنادًا إلى وجود بيانات ذات صلة في جدول آخر.

يحذف المثال أدناه الطلبات التابعة لعملاء من بلد محدد.

-- Delete orders placed by US customers
DELETE FROM orders o
WHERE EXISTS (
  SELECT 1
  FROM customers c
  WHERE c.customer_id = o.customer_id
    AND c.country = 'US'
);

مقارنة EXISTS بـ JOIN

يمكن لـ EXISTS وJOIN غالبًا التعبير عن السؤال نفسه، لكن سلوكهما يختلف. إذ يكرر JOIN الصفوف عند وجود تطابقات متعددة، بينما يُعيد EXISTS كل صف خارجي مرة واحدة على الأكثر.

عندما تحتاجون فقط إلى معرفة ما إذا كانت العلاقة موجودة، لا إلى استرداد بيانات من الجدول ذي الصلة، يكون EXISTS أوضح وعادةً أسرع.

-- JOIN may return duplicate customer rows if a customer has multiple orders
SELECT DISTINCT c.name
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id;

-- EXISTS always returns each customer once
SELECT c.name
FROM customers c
WHERE EXISTS (
  SELECT 1 FROM orders o
  WHERE o.customer_id = c.customer_id
);

استخدام NOT EXISTS لفحص جودة البيانات

يُعد NOT EXISTS أداة قوية لتدقيق جودة البيانات. يمكنكم استخدامه للعثور على السجلات اليتيمة، أو المراجع المفقودة، أو الصفوف التي يُفترض أن تكون لها بيانات ذات صلة لكنها لا تحتوي عليها.

يكتشف الاستعلام أدناه أي صفوف طلبات لا تطابق قيمة customer_id فيها أي صف في جدول العملاء — وهذا مؤشر على خلل في التكامل المرجعي.

-- Find orders with no matching customer (orphaned records)
SELECT o.order_id, o.customer_id, o.amount
FROM orders o
WHERE NOT EXISTS (
  SELECT 1
  FROM customers c
  WHERE c.customer_id = o.customer_id
);

اختبار سريع

اختبروا فهمكم لـ EXISTS وNOT EXISTS.

مراجعة الدرس

تعلمتم في هذا الدرس كيف يتيح لكم EXISTS وNOT EXISTS التحقق من وجود الصفوف ذات الصلة أو غيابها دون مقارنة القيم مباشرةً.

أهم النقاط:

  • يُعيد EXISTS القيمة TRUE بمجرد أن يجد الاستعلام الفرعي صفًا مطابقًا واحدًا (التقييم قصير الدائرة).
  • يُعيد NOT EXISTS القيمة TRUE عندما لا يجد الاستعلام الفرعي أي صفوف مطابقة.
  • استخدموا SELECT 1 داخل EXISTS — فالقيم المُعادة غير مهمة.
  • يتعامل NOT EXISTS بأمان مع NULL؛ أما NOT IN فلا يفعل ذلك — لذا فضّلوا NOT EXISTS عند احتمال وجود قيم NULL.
  • يعمل EXISTS في عبارات SELECT وUPDATE وDELETE.
  • عندما تحتاجون فقط إلى التحقق من الوجود، يكون EXISTS غالبًا أوضح وأسرع من JOIN.

الأسئلة الشائعة

هل درس «EXISTS وNOT EXISTS» مجاني؟

نعم — نص درس «EXISTS وNOT EXISTS» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة SQL Academy، انتقل إلى CoddyKit PRO. تتضمن دورة SQL Academy 4 دروس في المجموع.

ماذا ستتعلم في «EXISTS وNOT EXISTS»؟

تحقّق بكفاءة من وجود صفوف مرتبطة تتمرن على SQL Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ SQL Academy؟

لا تُشترط خبرة سابقة. SQL Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.

كم من الوقت يستغرق درس «EXISTS وNOT EXISTS»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس SQL Academy هذا؟

نعم. كل درس في SQL Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. الاستعلامات الفرعية المترابطة
  2. EXISTS وNOT EXISTS
  3. IN مقابل ANY مقابل ALL
  4. أداء EXISTS مقابل JOIN
← العودة إلى SQL Academy