استخدام SQL لتحديد إحصائيات عدد الكلمات في حقل النص

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

سؤال

لقد عملت مؤخرًا على بعض وظائف البحث في قاعدة البيانات وأردت الحصول على بعض المعلومات مثل متوسط ​​الكلمات لكل مستند (على سبيل المثال:حقل النص في قاعدة البيانات).الشيء الوحيد الذي وجدته حتى الآن (بدون المعالجة باللغة المفضلة خارج قاعدة البيانات) هو:

SELECT AVG(LENGTH(content) - LENGTH(REPLACE(content, ' ', '')) + 1)
FROM documents

يبدو أن هذا ناجح* ولكن هل لديك اقتراحات أخرى؟أستخدم حاليًا MySQL 4 (آمل أن أنتقل إلى الإصدار 5 لهذا التطبيق قريبًا)، ولكني مهتم أيضًا بالحلول العامة.

شكرًا!

* أستطيع أن أتخيل أن هذه طريقة تقريبية جدًا لتحديد ذلك لأنها لا تأخذ في الاعتبار HTML في المحتوى وما شابه ذلك أيضًا.هذا جيد لهذا المشروع بالذات ولكن مرة أخرى هل هناك طرق أفضل؟

تحديث: لتحديد ما أعنيه بكلمة "أفضل":إما أكثر دقة، أو يعمل بشكل أكثر كفاءة، أو أكثر "صحة" (سهولة الصيانة، والممارسة الجيدة، وما إلى ذلك).بالنسبة للمحتوى المتوفر لدي، فإن الاستعلام أعلاه سريع بما فيه الكفاية ودقيق لهذا المشروع، ولكن قد أحتاج إلى شيء مماثل في المستقبل (لذلك سألت).

هل كانت مفيدة؟

المحلول

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

الطريقة "الصحيحة" هي معالجة البيانات خارج قاعدة البيانات نظرًا لأن قواعد البيانات مخصصة للتخزين، وليس للمعالجة، وأي معالجة ثقيلة قد تضع عبئًا كبيرًا على نظام إدارة قواعد البيانات.بالإضافة إلى ذلك، فإن حساب عدد الكلمات خارج MySQL يجعل من السهل تغيير تعريف ما يمكن اعتباره كلمة.ماذا عن تخزين عدد الكلمات في قاعدة البيانات وتحديثه عند تغيير المستند؟

مثال للوظيفة المخزنة:

DELIMITER $$
CREATE FUNCTION wordcount(str LONGTEXT)
       RETURNS INT
       DETERMINISTIC
       SQL SECURITY INVOKER
       NO SQL
  BEGIN
    DECLARE wordCnt, idx, maxIdx INT DEFAULT 0;
    DECLARE currChar, prevChar BOOL DEFAULT 0;
    SET maxIdx=char_length(str);
    SET idx = 1;
    WHILE idx <= maxIdx DO
        SET currChar=SUBSTRING(str, idx, 1) RLIKE '[[:alnum:]]';
        IF NOT prevChar AND currChar THEN
            SET wordCnt=wordCnt+1;
        END IF;
        SET prevChar=currChar;
        SET idx=idx+1;
    END WHILE;
    RETURN wordCnt;
  END
$$
DELIMITER ;

نصائح أخرى

وهذا أسرع قليلاً، على الرغم من أنه أقل دقة قليلاً.لقد وجدت أنه 4% خفيف في العد، وهو أمر جيد بالنسبة لسيناريوهات "التقدير".

SELECT
    ROUND (   
        (
            CHAR_LENGTH(content) - CHAR_LENGTH(REPLACE (content, " ", "")) 
        ) 
        / CHAR_LENGTH(" ")        
    ) AS count    
FROM documents

يمكنك استخدام ال word_count() UDF من https://github.com/spachev/mysql_udf_bundle.لقد قمت بنقل المنطق من الإجابة المقبولة مع اختلاف أن الكود الخاص بي يدعم مجموعة الأحرف اللاتينية 1 فقط.سيحتاج المنطق إلى إعادة صياغة لدعم مجموعات الأحرف الأخرى.أيضًا، يعتبر كلا التطبيقين دائمًا أن الحرف غير الأبجدي الرقمي هو المحدد، وهو ما قد لا يكون مرغوبًا دائمًا - على سبيل المثال، يعتبر "كتاب المعلم" عبارة عن ثلاث كلمات في كلا التطبيقين.

إصدار UDF، بالطبع، أسرع بكثير.لإجراء اختبار سريع، قمت بتجريب كليهما على مجموعة بيانات من Project Guttenberg تتكون من 9751 سجلًا يبلغ إجمالي حجمها حوالي 3 جيجابايت.قام UDF بتنفيذ كل هذه المهام في 18 ثانية، بينما استغرقت الوظيفة المخزنة 63 ثانية لمعالجة 30 سجلاً فقط (وهو ما يفعله UDF في 0.05 ثانية).لذا فإن UDF أسرع بنحو 1000 مرة في هذه الحالة.

سوف يتفوق UDF على أي طريقة أخرى من حيث السرعة التي لا تتضمن تعديل كود مصدر MySQL.وذلك لأنه يتمتع بإمكانية الوصول إلى سلسلة البايتات الموجودة في الذاكرة ويمكنه العمل مباشرة على البايتات دون الحاجة إلى نقلها.يتم أيضًا تجميعه في كود الجهاز وتشغيله مباشرة على وحدة المعالجة المركزية.

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