22

私は最近、いくつかのデータベース検索機能に取り組んでおり、ドキュメントごとの平均単語(データベースのテキストフィールドなど)などの情報を取得したいと考えていました。私がこれまでに見つけた唯一のこと(DB外で選択した言語で処理せずに)は次のとおりです。

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

これはうまくいくようです*が、他に提案はありますか?私は現在MySQL4を使用しています(このアプリのバージョン5にすぐに移行することを望んでいます)が、一般的なソリューションにも興味があります。

ありがとう!

*これは、コンテンツなどのHTMLも考慮していないため、これを判断するためのかなり大まかな方法​​であると想像できます。この特定のプロジェクトでは問題ありませんが、もっと良い方法はありますか?

更新:私が「より良い」とはどういう意味かを定義すること:より正確、より効率的に実行する、またはより「正しい」(保守が容易、グッドプラクティスなど)。私が利用できるコンテンツについては、上記のクエリは十分に高速で、このプロジェクトには正確ですが、将来的には同様のものが必要になる可能性があります(私は尋ねました)。

4

5 に答える 5

45

MySQLのテキスト処理機能は、必要なものに対して十分ではありません。ストアド関数はオプションですが、おそらく遅くなります。MySQL内でデータを処理する最善の策は、ユーザー定義関数を追加することです。とにかく新しいバージョンのMySQLをビルドする場合は、ネイティブ関数を追加することもできます。

「正しい」方法は、DBが処理ではなくストレージ用であり、重い処理を行うとDBMSに過度の負荷がかかる可能性があるため、DBの外部でデータを処理することです。さらに、MySQLの外部で単語数を計算すると、単語として数えるものの定義を簡単に変更できます。単語数をDBに保存し、ドキュメントが変更されたときに更新するのはどうですか?

ストアド関数の例:

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 ;
于 2009-04-15T23:30:35.580 に答える
5

これはかなり高速ですが、精度はわずかに低くなります。カウントが4%軽いことがわかりました。これは、「見積もり」シナリオでは問題ありません。

SELECT
    ROUND (   
        (
            CHAR_LENGTH(content) - CHAR_LENGTH(REPLACE (content, " ", "")) 
        ) 
        / CHAR_LENGTH(" ")        
    ) AS count    
FROM documents
于 2017-08-13T20:02:05.727 に答える
0

word_count()UDFはhttps://github.com/spachev/mysql_udf_bundleから使用できます。私のコードはlatin1文字セットのみをサポートするという違いを除いて、受け入れられた回答からロジックを移植しました。他の文字セットをサポートするには、ロジックを作り直す必要があります。また、両方の実装では、英数字以外の文字を常に区切り文字と見なしますが、これは必ずしも望ましいとは限りません。たとえば、「教師の本」は、両方の実装で3語と見なされます。

もちろん、UDFバージョンは大幅に高速です。簡単なテストとして、合計約3GBの9751レコードで構成されるProjectGuttenbergのデータセットで両方を試しました。UDFはそれらすべてを18秒で実行しましたが、ストアド関数は30レコードのみを処理するのに63秒かかりました(UDFは0.05秒で実行します)。したがって、この場合、UDFは約1000倍高速です。

UDFは、MySQLソースコードの変更を伴わない他の方法よりも高速です。これは、メモリ内の文字列バイトにアクセスでき、バイトを移動しなくてもバイトを直接操作できるためです。また、マシンコードにコンパイルされ、CPU上で直接実行されます。

于 2018-05-31T21:18:47.110 に答える
0

いくつかの同様のケースの簡単な解決策(MySQL):

SELECT *、(CHAR_LENGTH(student)-CHAR_LENGTH(REPLACE(student、''、'')))+ 1as'count'FROM
ドキュメント;

于 2021-05-17T18:43:01.913 に答える
0

さて、私は上で定義された関数を使おうとしました、そしてそれは1つのシナリオを除いて素晴らしかったです。

英語では、単語の一部として'を強く使用します。上記の関数は、少なくとも私にとっては、「しなかった」と2として数えました。

だからここに私の小さな修正があります:

DELIMITER $$
CREATE FUNCTION wordcount(str TEXT)
            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);
         WHILE idx < maxIdx DO
             SET currChar=SUBSTRING(str, idx, 1) RLIKE '[[:alnum:]]' OR SUBSTRING(str, idx, 1) RLIKE "'";
             IF NOT prevChar AND currChar THEN
                 SET wordCnt=wordCnt+1;
             END IF;
             SET prevChar=currChar;
             SET idx=idx+1;
         END WHILE;
         RETURN wordCnt;
       END
     $$
于 2021-07-15T09:57:59.803 に答える