0Pricing
SQL Academy · درس

DISTINCT ON في PostgreSQL

اختر صفًا واحدًا لكل مجموعة

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

ما هو DISTINCT ON؟

يوفّر PostgreSQL امتدادًا قويًا للكلمة المفتاحية القياسية DISTINCT يُسمى DISTINCT ON. ففي حين يزيل DISTINCT العادي الصفوف المتطابقة تمامًا، يتيح لكم DISTINCT ON اختيار صف واحد لكل مجموعة استنادًا إلى عمود واحد أو عدة أعمدة تختارونها.

فكّروا فيه بهذه الطريقة: «لكل قيمة فريدة في هذا العمود، أعطني صفًا واحدًا». وهذا مفيد للغاية عندما تريدون الحصول على أحدث طلب لكل عميل، أو أعلى نتيجة لكل طالب، أو أول حدث لكل فئة.

الصيغة الأساسية لـ DISTINCT ON

تضع الصيغة DISTINCT ON (column) مباشرةً بعد SELECT. ويحدد العمود الموجود بين القوسين التجميع — إذ يعيد PostgreSQL صفًا واحدًا لكل قيمة فريدة في ذلك العمود.

يعيد المثال التالي صفًا واحدًا لكل customer_id من جدول orders. ويحدد PostgreSQL الصف الذي سيعيده استنادًا إلى عبارة ORDER BY التالية.

SELECT DISTINCT ON (customer_id)
  customer_id,
  order_id,
  order_date,
  total_amount
FROM orders
ORDER BY customer_id, order_date DESC;

إعداد الجداول المثال

لننشئ جدول orders بسيطًا وندرج فيه بعض الصفوف النموذجية حتى نجرّب DISTINCT ON عمليًا. لدينا ثلاثة عملاء، ولكل منهم عدة طلبات في تواريخ مختلفة.

CREATE TABLE orders (
  order_id      SERIAL PRIMARY KEY,
  customer_id   INT,
  order_date    DATE,
  total_amount  NUMERIC(10, 2)
);

INSERT INTO orders (customer_id, order_date, total_amount) VALUES
  (1, '2024-01-05', 120.00),
  (1, '2024-03-12', 85.50),
  (1, '2024-06-20', 200.00),
  (2, '2024-02-14', 45.00),
  (2, '2024-05-30', 310.00),
  (3, '2024-04-01', 75.00);

أحدث طلب لكل عميل

إحدى حالات الاستخدام الشائعة جدًا هي العثور على أحدث طلب لكل عميل. فعند ترتيب order_date DESC داخل كل مجموعة من مجموعات customer_id، يختار DISTINCT ON الصف ذي التاريخ الأحدث.

لاحظوا أن عبارة ORDER BY يجب أن تبدأ بالعمود أو الأعمدة نفسها المدرجة في DISTINCT ON. وهذا أحد متطلبات PostgreSQL.

SELECT DISTINCT ON (customer_id)
  customer_id,
  order_id,
  order_date,
  total_amount
FROM orders
ORDER BY customer_id, order_date DESC;

أقدم طلب لكل عميل

للحصول على أول طلب (الأقدم) لكل عميل بدلًا من ذلك، غيّروا اتجاه الترتيب ببساطة إلى ASC. والاختلاف الوحيد هو ترتيب الصفوف داخل كل مجموعة — إذ يختار DISTINCT ON دائمًا الصف الأول بعد الترتيب.

SELECT DISTINCT ON (customer_id)
  customer_id,
  order_id,
  order_date,
  total_amount
FROM orders
ORDER BY customer_id, order_date ASC;

قاعدة ORDER BY

قاعدة مهمة: عند استخدام DISTINCT ON (col)، يجب أن تبدأ عبارة ORDER BY بالعمود أو الأعمدة نفسها المدرجة داخل DISTINCT ON. وإذا لم تفعل ذلك، فسيُصدر PostgreSQL خطأً.

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

-- Correct: ORDER BY starts with the DISTINCT ON column
SELECT DISTINCT ON (customer_id)
  customer_id, order_date, total_amount
FROM orders
ORDER BY customer_id, total_amount DESC;

-- This would cause an error:
-- ORDER BY order_date DESC  (missing customer_id at the start)

أعلى نتيجة لكل طالب

إليكم مثالًا عمليًا آخر باستخدام جدول test_scores. نريد معرفة أعلى نتيجة حققها كل طالب على الإطلاق. ومن خلال ترتيب score DESC داخل مجموعة كل طالب، يعيد DISTINCT ON الصف ذي أعلى نتيجة لكل طالب فقط.

CREATE TABLE test_scores (
  id         SERIAL PRIMARY KEY,
  student_id INT,
  subject    VARCHAR(50),
  score      INT,
  taken_on   DATE
);

INSERT INTO test_scores (student_id, subject, score, taken_on) VALUES
  (101, 'Math',    92, '2024-02-10'),
  (101, 'Math',    78, '2024-04-15'),
  (102, 'Math',    85, '2024-02-10'),
  (102, 'Math',    91, '2024-04-15'),
  (103, 'Math',    67, '2024-02-10');

SELECT DISTINCT ON (student_id)
  student_id, subject, score, taken_on
FROM test_scores
ORDER BY student_id, score DESC;

DISTINCT ON مع عدة أعمدة

يمكنكم التجميع حسب أكثر من عمود واحد بإدراج عدة أعمدة داخل DISTINCT ON. ويعيد ذلك صفًا واحدًا لكل تركيبة فريدة من تلك الأعمدة.

يختار المثال التالي أعلى نتيجة لكل طالب في كل مادة، مع اعتبار كل زوج (طالب، مادة) مجموعةً مستقلة.

INSERT INTO test_scores (student_id, subject, score, taken_on) VALUES
  (101, 'Science', 88, '2024-03-01'),
  (101, 'Science', 95, '2024-05-20'),
  (102, 'Science', 72, '2024-03-01');

SELECT DISTINCT ON (student_id, subject)
  student_id, subject, score, taken_on
FROM test_scores
ORDER BY student_id, subject, score DESC;

التصفية باستخدام WHERE

يعمل DISTINCT ON بصورة طبيعية إلى جانب عبارات WHERE. تُطبَّق التصفية أولًا، ثم يختار DISTINCT ON صفًا واحدًا لكل مجموعة من النتائج المصفّاة.

نبحث هنا عن أحدث طلب لكل عميل، ولكن للطلبات التي تتجاوز قيمتها 100 فقط.

SELECT DISTINCT ON (customer_id)
  customer_id,
  order_id,
  order_date,
  total_amount
FROM orders
WHERE total_amount > 100
ORDER BY customer_id, order_date DESC;

DISTINCT ON في مقابل GROUP BY

يمكن لكل من DISTINCT ON وGROUP BY إنتاج صف واحد لكل مجموعة، لكن لكل منهما غرض مختلف:

  • GROUP BY يدمج الصفوف، ويتطلب دوال تجميع مثل (SUM وMAX وغيرهما) للأعمدة غير المُجمّعة.
  • DISTINCT ON يحتفظ بصف موجود فعليًا — وتصبح جميع أعمدته متاحة من دون تجميع.

استخدموا GROUP BY عندما تحتاجون إلى قيم مجمّعة، واستخدموا DISTINCT ON عندما تحتاجون إلى البيانات الكاملة لصف محدد داخل كل مجموعة.

-- GROUP BY: only aggregated columns allowed
SELECT customer_id, MAX(order_date) AS latest_date
FROM orders
GROUP BY customer_id;

-- DISTINCT ON: returns the whole row for that latest date
SELECT DISTINCT ON (customer_id)
  customer_id, order_id, order_date, total_amount
FROM orders
ORDER BY customer_id, order_date DESC;

استخدام DISTINCT ON في استعلام فرعي

قد تحتاجون أحيانًا إلى تطبيق تصفية أو ترتيب إضافي على نتيجة DISTINCT ON. ونظرًا إلى ارتباط عبارة ORDER BY الخارجية بعمود التجميع، يمكنكم تغليف الاستعلام داخل استعلام فرعي (أو CTE) لتطبيق ترتيب مختلف على الناتج النهائي.

يختار هذا المثال أولًا أحدث طلب لكل عميل، ثم يرتب النتيجة النهائية حسب total_amount ترتيبًا تنازليًا.

SELECT *
FROM (
  SELECT DISTINCT ON (customer_id)
    customer_id,
    order_id,
    order_date,
    total_amount
  FROM orders
  ORDER BY customer_id, order_date DESC
) AS latest_orders
ORDER BY total_amount DESC;

تحقق سريع

لنتحقق من مدى فهمكم لـ DISTINCT ON. اقرأوا الاستعلام التالي بعناية، واختاروا الإجابة التي تصف ما يعيده بأفضل شكل.

SELECT DISTINCT ON (department_id) department_id, employee_name, salary FROM employees ORDER BY department_id, salary DESC;

مراجعة الدرس

أحسنتم! إليكم ملخصًا لما تعلمتموه عن PostgreSQL DISTINCT ON:

  • يعيد DISTINCT ON (col) صفًا واحدًا بالضبط لكل قيمة فريدة من العمود أو الأعمدة المحددة.
  • يجب أن تبدأ عبارة ORDER BY بالعمود أو الأعمدة نفسها المدرجة في DISTINCT ON — فهي تتحكم في الصف الذي سيُحدد من كل مجموعة.
  • يمكنكم استخدام عدة أعمدة: إذ يجمع DISTINCT ON (col1, col2) حسب تركيبة العمودين معًا.
  • بخلاف GROUP BY، يعيد DISTINCT ON صفًا حقيقيًا بكل أعمدته الأصلية — من دون الحاجة إلى التجميع.
  • غلّفوا الاستعلام داخل استعلام فرعي عندما تحتاجون إلى ترتيب الناتج النهائي حسب عمود مختلف.

إن DISTINCT ON ميزة خاصة بـ PostgreSQL، وتُعد من أكثر الطرق أناقة لحل المسائل التي تتطلب «اختيار صف واحد لكل مجموعة».

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

هل درس «DISTINCT ON في PostgreSQL» مجاني؟

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

ماذا ستتعلم في «DISTINCT ON في PostgreSQL»؟

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

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

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

كم من الوقت يستغرق درس «DISTINCT ON في PostgreSQL»؟

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

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

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

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

  1. أساسيات SELECT DISTINCT
  2. DISTINCT على أعمدة متعددة
  3. DISTINCT ON في PostgreSQL
  4. عدّ القيم الفريدة
← العودة إلى SQL Academy