تحديد عدد النتائج LIMIT و OFFSET
بعد أن تعلمنا كيفية ترتيب النتائج باستخدام ORDER BY، نحتاج الآن إلى تعلم كيفية التحكم في عدد النتائج المعروضة. في هذا الدرس الشامل من سلسلة تعلم لغة SQL باللغة العربية، سنتعلم استخدام LIMIT و OFFSET لتحديد عدد الصفوف المسترجعة وتخطي صفوف معينة. هذه المهارة أساسية لبناء أنظمة الصفحات (Pagination) في تطبيقات الويب، وتحسين أداء الاستعلامات، وعرض البيانات بشكل منظم.
1. ما هو LIMIT في SQL؟
LIMIT هو جملة تُستخدم لتحديد الحد الأقصى لعدد الصفوف (Rows) التي يجب إرجاعها من الاستعلام. بدلاً من جلب جميع النتائج (التي قد تكون آلاف أو ملايين الصفوف)، يمكنك تحديد عدد معين فقط.
SELECT column1, column2
FROM table_name
LIMIT number;
- تحسين الأداء: جلب 10 صفوف أسرع بكثير من جلب 10,000 صف
- تجربة المستخدم: عرض البيانات على صفحات بدلاً من قائمة طويلة جداً
- توفير الموارد: تقليل استهلاك الذاكرة والشبكة
- الاختبار: عرض عينة من البيانات للتحقق من الاستعلام
2. أمثلة أساسية على LIMIT
لنفترض أن لدينا جدول المنتجات التالي:
+----+------------------+--------+-------+
| id | name | price | stock |
+----+------------------+--------+-------+
| 1 | لابتوب Dell | 3500 | 15 |
| 2 | ماوس لاسلكي | 50 | 100 |
| 3 | لوحة مفاتيح | 120 | 50 |
| 4 | شاشة Samsung | 800 | 25 |
| 5 | سماعات | 150 | 60 |
| 6 | كاميرا ويب | 200 | 40 |
| 7 | هارد خارجي | 350 | 30 |
| 8 | طابعة HP | 600 | 20 |
... (92 منتج آخر)
+----+------------------+--------+-------+
مثال 1: جلب أول 5 منتجات فقط
SELECT * FROM products
LIMIT 5;
+----+------------------+--------+-------+
| id | name | price | stock |
+----+------------------+--------+-------+
| 1 | لابتوب Dell | 3500 | 15 |
| 2 | ماوس لاسلكي | 50 | 100 |
| 3 | لوحة مفاتيح | 120 | 50 |
| 4 | شاشة Samsung | 800 | 25 |
| 5 | سماعات | 150 | 60 |
+----+------------------+--------+-------+
5 rows in set
-- لاحظ: تم جلب 5 صفوف فقط من أصل 100
مثال 2: أغلى 3 منتجات
SELECT name, price FROM products
ORDER BY price DESC
LIMIT 3;
+------------------+--------+
| name | price |
+------------------+--------+
| لابتوب Dell | 3500 | ← الأغلى
| شاشة Samsung | 800 |
| طابعة HP | 600 | ← الثالث
+------------------+--------+
3 rows in set
مثال 3: أقل 10 منتجات مخزوناً
SELECT name, stock FROM products
ORDER BY stock ASC
LIMIT 10;
شرح: هذا الاستعلام مفيد جداً في أنظمة المخازن لمعرفة المنتجات التي توشك على النفاد.
3. ما هو OFFSET في SQL؟
OFFSET يُستخدم لتخطي (Skip) عدد معين من الصفوف قبل البدء في جلب النتائج. يُستخدم عادة مع LIMIT لتنفيذ نظام الصفحات (Pagination).
SELECT column1, column2
FROM table_name
LIMIT number OFFSET skip_number;
-- أو بصيغة مختصرة:
SELECT column1, column2
FROM table_name
LIMIT skip_number, number;
LIMIT skip, number تعمل في MySQL و MariaDB، لكن الصيغة القياسية LIMIT number OFFSET skip تعمل في معظم أنظمة SQL.
4. أمثلة على OFFSET
مثال 1: تخطي أول 5 صفوف
-- جلب 5 منتجات، بعد تخطي أول 5
SELECT * FROM products
LIMIT 5 OFFSET 5;
+----+------------------+--------+-------+
| id | name | price | stock |
+----+------------------+--------+-------+
| 6 | كاميرا ويب | 200 | 40 | ← بدأ من الصف 6
| 7 | هارد خارجي | 350 | 30 |
| 8 | طابعة HP | 600 | 20 |
| 9 | راوتر | 180 | 35 |
| 10 | كابل HDMI | 30 | 200 | ← انتهى عند الصف 10
+----+------------------+--------+-------+
5 rows in set
-- تم تخطي الصفوف 1-5، وجلب الصفوف 6-10
مثال 2: الصيغة المختصرة (MySQL)
-- هذان الاستعلامان متطابقان في MySQL:
-- الصيغة القياسية (تعمل في كل الأنظمة)
SELECT * FROM products LIMIT 5 OFFSET 10;
-- الصيغة المختصرة (MySQL فقط)
SELECT * FROM products LIMIT 10, 5;
-- ↑ ↑
-- OFFSET LIMIT
LIMIT number OFFSET skip لأنها أوضح وتعمل في معظم أنظمة SQL.
5. بناء نظام الصفحات (Pagination)
الاستخدام الأكثر شيوعاً لـ LIMIT و OFFSET هو بناء نظام الصفحات في تطبيقات الويب. بدلاً من عرض 10,000 منتج في صفحة واحدة، نعرض 20 منتج في كل صفحة.
الصيغة الرياضية للصفحات
-- المتغيرات:
-- page_number = رقم الصفحة (1, 2, 3, ...)
-- items_per_page = عدد العناصر في كل صفحة (مثلاً 20)
-- المعادلة:
OFFSET = (page_number - 1) × items_per_page
LIMIT = items_per_page
مثال عملي: نظام صفحات للمنتجات
لنفترض أننا نريد عرض 10 منتجات في كل صفحة:
-- الصفحة 1: OFFSET = (1-1) × 10 = 0
SELECT * FROM products
ORDER BY id
LIMIT 10 OFFSET 0;
-- أو ببساطة:
SELECT * FROM products
ORDER BY id
LIMIT 10;
-- الصفحة 2: OFFSET = (2-1) × 10 = 10
SELECT * FROM products
ORDER BY id
LIMIT 10 OFFSET 10;
-- الصفحة 3: OFFSET = (3-1) × 10 = 20
SELECT * FROM products
ORDER BY id
LIMIT 10 OFFSET 20;
مثال: كود PHP لنظام الصفحات
<?php
// إعدادات الصفحات
$items_per_page = 20;
$current_page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
// حساب OFFSET
$offset = ($current_page - 1) * $items_per_page;
// بناء الاستعلام
$sql = "SELECT * FROM products
ORDER BY id
LIMIT $items_per_page OFFSET $offset";
// تنفيذ الاستعلام
$result = mysqli_query($conn, $sql);
// عرض النتائج
while ($row = mysqli_fetch_assoc($result)) {
echo $row['name'] . " - " . $row['price'] . "<br>";
}
?>
6. الاختلافات بين أنظمة SQL
ليست كل أنظمة قواعد البيانات تستخدم نفس الصيغة لـ LIMIT. إليك الاختلافات الرئيسية:
| نظام SQL | الصيغة | مثال |
|---|---|---|
| MySQL / MariaDB | LIMIT n OFFSET m |
LIMIT 10 OFFSET 5 |
| PostgreSQL | LIMIT n OFFSET m |
LIMIT 10 OFFSET 5 |
| SQLite | LIMIT n OFFSET m |
LIMIT 10 OFFSET 5 |
| SQL Server | OFFSET m ROWS FETCH NEXT n ROWS ONLY |
OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY |
| Oracle | OFFSET m ROWS FETCH NEXT n ROWS ONLY |
OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY |
مثال: SQL Server
-- SQL Server يتطلب ORDER BY مع OFFSET
SELECT * FROM products
ORDER BY id
OFFSET 10 ROWS
FETCH NEXT 5 ROWS ONLY;
مثال: Oracle (الإصدارات القديمة)
-- Oracle القديم يستخدم ROWNUM
SELECT * FROM (
SELECT p.*, ROWNUM rnum FROM (
SELECT * FROM products ORDER BY id
) p WHERE ROWNUM <= 15
) WHERE rnum > 10;
-- Oracle 12c والأحدث:
SELECT * FROM products
ORDER BY id
OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLY;
7. أمثلة عملية متقدمة
مثال 1: أحدث 10 طلبات
SELECT
order_id,
customer_name,
order_date,
total_amount
FROM orders
ORDER BY order_date DESC
LIMIT 10;
مثال 2: أفضل 5 موظفين في المبيعات
SELECT
employee_name,
COUNT(*) AS total_sales,
SUM(amount) AS total_revenue
FROM sales
WHERE YEAR(sale_date) = 2024
GROUP BY employee_name
ORDER BY total_revenue DESC
LIMIT 5;
مثال 3: المقالات الأكثر قراءة (مع الصفحات)
-- الصفحة 1: المقالات 1-10
SELECT
title,
author,
views,
published_date
FROM articles
WHERE status = 'published'
ORDER BY views DESC
LIMIT 10 OFFSET 0;
-- الصفحة 2: المقالات 11-20
SELECT
title,
author,
views,
published_date
FROM articles
WHERE status = 'published'
ORDER BY views DESC
LIMIT 10 OFFSET 10;
مثال 4: عينة عشوائية من البيانات
-- MySQL: جلب 5 منتجات عشوائية
SELECT * FROM products
ORDER BY RAND()
LIMIT 5;
-- PostgreSQL: جلب 5 منتجات عشوائية
SELECT * FROM products
ORDER BY RANDOM()
LIMIT 5;
مثال 5: تخطي النتائج المكررة
-- جلب الموظفين من الترتيب 2 إلى 4 في كل قسم
SELECT
department,
employee_name,
salary
FROM (
SELECT
department,
employee_name,
salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rank
FROM employees
) ranked
WHERE rank BETWEEN 2 AND 4;
8. حساب عدد الصفحات الإجمالي
لبناء نظام صفحات كامل، نحتاج لمعرفة العدد الإجمالي للصفحات. هذا يتطلب معرفة إجمالي عدد الصفوف.
-- 1. احسب إجمالي عدد المنتجات
SELECT COUNT(*) AS total_products FROM products;
-- النتيجة: 100
-- 2. احسب عدد الصفحات
-- إذا كان لدينا 100 منتج، و 10 منتجات في كل صفحة:
-- عدد الصفحات = CEILING(100 / 10) = 10 صفحات
مثال كامل في PHP
<?php
// الإعدادات
$items_per_page = 20;
$current_page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
// 1. احسب إجمالي عدد المنتجات
$count_sql = "SELECT COUNT(*) AS total FROM products";
$count_result = mysqli_query($conn, $count_sql);
$total_items = mysqli_fetch_assoc($count_result)['total'];
// 2. احسب عدد الصفحات
$total_pages = ceil($total_items / $items_per_page);
// 3. احسب OFFSET
$offset = ($current_page - 1) * $items_per_page;
// 4. جلب البيانات
$sql = "SELECT * FROM products
ORDER BY id
LIMIT $items_per_page OFFSET $offset";
$result = mysqli_query($conn, $sql);
// 5. عرض النتائج
while ($row = mysqli_fetch_assoc($result)) {
echo $row['name'] . "<br>";
}
// 6. عرض روابط الصفحات
echo "<div class='pagination'>";
for ($i = 1; $i <= $total_pages; $i++) {
$active = ($i == $current_page) ? 'active' : '';
echo "<a href='?page=$i' class='$active'>$i</a> ";
}
echo "</div>";
?>
9. الأداء والتحسينات (Performance)
مشكلة OFFSET الكبير
عندما يكون OFFSET كبيراً جداً (مثلاً 100,000)، يصبح الاستعلام بطيئاً لأن قاعدة البيانات يجب أن تقرأ وتتخطى جميع الصفوف قبل الوصول للنتائج المطلوبة.
-- بطيء جداً: يجب قراءة 100,000 صف ثم تخطيها
SELECT * FROM products
ORDER BY id
LIMIT 10 OFFSET 100000;
الحل: استخدام WHERE بدلاً من OFFSET
-- بدلاً من OFFSET، استخدم WHERE مع آخر ID
-- افترض أن آخر منتج في الصفحة السابقة كان id = 100000
-- سريع: يستخدم الفهرس (Index) مباشرة
SELECT * FROM products
WHERE id > 100000
ORDER BY id
LIMIT 10;
مثال: Cursor-based Pagination
-- الصفحة الأولى
SELECT * FROM products
ORDER BY created_at DESC, id DESC
LIMIT 20;
-- الصفحة التالية (بناءً على آخر created_at و id)
SELECT * FROM products
WHERE (created_at, id) < ('2024-01-15 10:30:00', 12345)
ORDER BY created_at DESC, id DESC
LIMIT 20;
10. نصائح وأفضل الممارسات
1. استخدم دائماً ORDER BY مع LIMIT
-- ❌ سيء: النتائج غير متوقعة
SELECT * FROM products LIMIT 10;
-- ✅ جيد: النتائج مرتبة ومتوقعة
SELECT * FROM products ORDER BY id LIMIT 10;
2. تحقق من صحة رقم الصفحة
<?php
// تحقق من صحة رقم الصفحة
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
// تأكد أن الصفحة >= 1
if ($page < 1) {
$page = 1;
}
// تأكد أن الصفحة <= عدد الصفحات الإجمالي
if ($page > $total_pages) {
$page = $total_pages;
}
?>
3. استخدم الفهارس (Indexes)
تأكد من وجود فهرس على الأعمدة المستخدمة في ORDER BY لتحسين الأداء.
-- إنشاء فهرس على عمود created_at
CREATE INDEX idx_created_at ON products(created_at);
-- الآن هذا الاستعلام سيكون أسرع بكثير
SELECT * FROM products
ORDER BY created_at DESC
LIMIT 20;
4. احفظ النتائج في الذاكرة المؤقتة (Cache)
للصفحات التي يتم الوصول إليها بكثرة، احفظ النتائج في Cache لتقليل الضغط على قاعدة البيانات.
5. اعرض عدد النتائج الإجمالي
-- اعرض للمستخدم: "عرض 1-20 من أصل 1,543 منتج"
SELECT COUNT(*) AS total FROM products;
-- ثم اعرض النتائج مع LIMIT
ملخص الدرس
- LIMIT: يحدد عدد الصفوف المسترجعة من الاستعلام
- OFFSET: يتخطى عدداً معيناً من الصفوف قبل البدء في الجلب
- الصيغة:
LIMIT n OFFSET m(قياسية) أوLIMIT m, n(MySQL) - Pagination:
OFFSET = (page - 1) × items_per_page - الأداء: استخدم WHERE بدلاً من OFFSET الكبير للجداول الضخمة
- Best Practice: استخدم دائماً ORDER BY مع LIMIT لنتائج متوقعة
- الاختلافات: SQL Server و Oracle يستخدمان صيغة مختلفة
الخطوة التالية: البحث بالأنماط باستخدام LIKE في SQL
أكمل رحلتك التعليمية وانتقل إلى الدرس التالي لتعلم البحث بالأنماط باستخدام LIKE في وتطوير مهاراتك في قواعد البيانات.
الانتقال إلى الدرس التالي