كيفية تحديد السجلات "N" الأولى من قاعدة بيانات تحتوي على مليون سجل؟

StackOverflow https://stackoverflow.com/questions/1410048

سؤال

لدي قاعدة بيانات أوراكل تحتوي على مليون سجل.أحاول كتابة استعلام SQL يُرجع السجلات المصنفة الأولى "N" (على سبيل المثال 100 سجل) من قاعدة البيانات بناءً على شرط معين.

SELECT * 
FROM myTable 
Where SIZE > 2000 
ORDER BY NAME DESC

ثم حدد برمجياً سجلات N الأولى.

المشكلة في هذا النهج هي:

  • ينتج عن الاستعلام إلى نصف مليون سجل و "طلب بالاسم" يؤدي إلى فرز جميع السجلات بالاسم بالترتيب التنازلي.هذا الفرز يستغرق الكثير من الوقت.(حوالي 30-40 ثانية.إذا قمت بحذف ORDER BY، فسيستغرق الأمر ثانية واحدة فقط).
  • بعد هذا النوع ، أنا مهتم بسجلات N (100) الأولى فقط.لذا فإن فرز السجلات الكاملة ليس مفيدًا.

أسئلتي هي:

  1. هل من الممكن تحديد 'n' في الاستعلام نفسه؟(بحيث ينطبق هذا الفرز على سجلات N فقط ويصبح الاستعلام أسرع).
  2. أي طريقة أفضل في SQL لتحسين الاستعلام لفرز عناصر N فقط والعودة في الوقت السريع.
هل كانت مفيدة؟

المحلول

إذا كان هدفك هو العثور على 100 صف عشوائي وفرزها بعد ذلك حل لاسي صحيح.إذا كنت أعتقد أنك تريد فرز أول 100 صف حسب الاسم مع تجاهل الصفوف الأخرى، فيمكنك إنشاء استعلام مثل هذا:

SELECT * 
  FROM (SELECT * 
          FROM myTable 
         WHERE SIZE > 2000 ORDER BY NAME DESC) 
 WHERE ROWNUM <= 100

سوف يفهم المُحسِّن أنه استعلام TOP-N وسيتمكن من استخدام فهرس في NAME.لن يتعين عليه فرز مجموعة النتائج بأكملها، بل سيبدأ فقط من نهاية الفهرس ويقرأها بشكل عكسي ويتوقف بعد 100 صف.

يمكنك أيضًا إضافة تلميح إلى استعلامك الأصلي للسماح للمُحسِّن بفهم أنك مهتم بالصفوف الأولى فقط.من المحتمل أن يؤدي هذا إلى إنشاء مسار وصول مشابه:

SELECT /*+ FIRST_ROWS*/* FROM myTable WHERE SIZE > 2000 ORDER BY NAME DESC

يحرر: مجرد إضافة AND rownum <= 100 لن يعمل الاستعلام لأنه يُنسب إلى Oraclerownum قبل فرز :ولهذا السبب يجب عليك استخدام استعلام فرعي.بدون الاستعلام الفرعي، ستقوم Oracle بتحديد 100 صف عشوائي ثم فرزها.

نصائح أخرى

هذا يوضح كيفية اختيار الصفوف N العليا اعتمادًا على إصدار Oracle الخاص بك.

من Oracle 9I فصاعدا ، يمكن استخدام وظائف RANK () و DENTER_RANK () لتحديد صفوف N أعلى.أمثلة:

احصل على أفضل 10 موظفين بناءً على راتبهم

حدد Ename ، SAL من (SELECT ENAME ، SAL ، RANK () Over (order by sal desc) sal_rank من EMP) حيث sal_rank <= 10 ؛

حدد الموظفين الذين يصنعون أفضل 10 رواتب

حدد Ename ، SAL من (SELECT ENAME ، SAL ، DENSE_RANK () Over (order by sal desc) sal_dense_rank من EMP) حيث sal_dense_rank <= 10 ؛

وأوضح الفرق بين الاثنين هنا

اضف هذا:

 AND rownum <= 100

إلى شرط WHERE الخاص بك.

ومع ذلك، فإن هذا لن يفي بما تطلبه.

إذا كنت تريد اختيار 100 صف عشوائي، وفرزها، ثم إعادتها، فسيتعين عليك صياغة استعلام بدون ORDER BY أولاً، ثم قصر ذلك على 100 صف، ثم الاختيار من ذلك والفرز.

هذا استطاع العمل، ولكن لسوء الحظ ليس لدي خادم أوراكل متاح للاختبار:

SELECT *
FROM (
    SELECT *
    FROM myTable
    WHERE SIZE > 2000
      AND rownum <= 100
    ) x
ORDER BY NAME DESC

لكن لاحظ الجزء "العشوائي" هناك، فأنت تقول "أعطني 100 صف بحجم أكبر من 2000، ولا يهمني أي 100".

هل هذا حقا ما تريده؟

لا، لن تحصل في الواقع على نتيجة عشوائية، بمعنى أنها ستتغير في كل مرة تقوم فيها بالاستعلام عن الخادم، ولكنك تحت رحمة مُحسِّن الاستعلام.إذا تغيرت إحصائيات تحميل البيانات وفهرستها لهذا الجدول بمرور الوقت، فقد تحصل في مرحلة ما على بيانات مختلفة عما حصلت عليه في الاستعلام السابق.

مشكلتك هي أن الفرز يتم في كل مرة يتم فيها تشغيل الاستعلام.يمكنك حذف عملية الفرز باستخدام فهرس - يمكن للمُحسِّن استخدام فهرس للتخلص من عملية الفرز - إذا تم تعريف العمود الذي تم فرزه بأنه "ليس فارغًا".

(إذا كان العمود فارغًا، فلا يزال ذلك ممكنًا، إما عن طريق (أ) إضافة مسند NOT NULL إلى الاستعلام، أو (ب) إضافة فهرس قائم على الوظيفة وتعديل جملة ORDER BY وفقًا لذلك).

للإشارة فقط، في Oracle 12c، يمكن تنفيذ هذه المهمة باستخدام FETCH بند.يمكنك ان ترى هنا للحصول على أمثلة وروابط مرجعية إضافية بخصوص هذه المسألة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top