web-dev-qa-db-ja.com

MySQL文字列でn番目のWordを抽出してWordの出現回数をカウントする方法は?

次のようなmysqlクエリが必要です。

select <second Word in text> Word, count(*) from table group by Word;

Mysqlのすべての正規表現の例は、テキストが式に一致するかどうかを照会するために使用されますが、式からテキストを抽出するためには使用されません。そのような構文はありますか?

58
Noam

以下は、OPのspecific問題(文字列の2番目の単語を抽出)に対する提案された解決策ですが、mc0eの回答状態として、実際に正規表現の一致を抽出することはサポートされていません-MySQLのボックス。これが本当に必要な場合、基本的には1)クライアントで後処理で行うか、2)それをサポートするMySQL拡張機能をインストールするかを選択します。


ベンウェルズはそれを非常に正確にしています。彼のコードを元に、少し調整したバージョンを次に示します。

SUBSTRING(
  sentence,
  LOCATE(' ', sentence) + CHAR_LENGTH(' '),
  LOCATE(' ', sentence,
  ( LOCATE(' ', sentence) + 1 ) - ( LOCATE(' ', sentence) + CHAR_LENGTH(' ') )
)

実例として、私は以下を使用しました:

SELECT SUBSTRING(
  sentence,
  LOCATE(' ', sentence) + CHAR_LENGTH(' '),
  LOCATE(' ', sentence,
  ( LOCATE(' ', sentence) + 1 ) - ( LOCATE(' ', sentence) + CHAR_LENGTH(' ') )
) as string
FROM (SELECT 'THIS IS A TEST' AS sentence) temp

これにより、Word ISが正常に抽出されます

45
Brendan Bullen

文の2番目の単語を抽出するための短いオプション:

SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('THIS IS A TEST', ' ',  2), ' ', -1) as FoundText

SUBSTRING_INDEXのMySQLドキュメント

27
Damien Goor

http://dev.mysql.com/ によれば、SUBSTRING関数は開始位置と長さを使用するため、2番目のWordの関数は次のようになります。

SUBSTRING(sentence,LOCATE(' ',sentence),(LOCATE(' ',LOCATE(' ',sentence))-LOCATE(' ',sentence)))
14
BenWells

いいえ、正規表現を使用してテキストを抽出するための構文はありません。通常の 文字列操作関数 を使用する必要があります。

または、データベースから値全体を選択し(データ転送が多すぎる場合は最初のn文字)、クライアントで正規表現を使用します。

7
Mark Byers

JSON文字列の特定のフィールドの値を取得するという、私が抱えていた同様の問題の出発点として、ブレンダン・ブレンの答えを使用しました。しかし、私が彼の答えについてコメントしたように、それは完全に正確ではありません。左の境界が元の質問のように単なるスペースではない場合、不一致が増加します。

修正されたソリューション:

SUBSTRING(
    sentence,
    LOCATE(' ', sentence) + 1,
    LOCATE(' ', sentence, (LOCATE(' ', sentence) + 1)) - LOCATE(' ', sentence) - 1
)

2つの違いは、SUBSTRINGインデックスパラメータの+1と、長さパラメータの-1です。

「提供された2つの境界間で文字列の最初の出現を見つける」ためのより一般的な解決策:

SUBSTRING(
    haystack,
    LOCATE('<leftBoundary>', haystack) + CHAR_LENGTH('<leftBoundary>'),
    LOCATE(
        '<rightBoundary>',
        haystack,
        LOCATE('<leftBoundary>', haystack) + CHAR_LENGTH('<leftBoundary>')
    )
    - (LOCATE('<leftBoundary>', haystack) + CHAR_LENGTH('<leftBoundary>'))
)
5

他の人が言ったように、mysqlは部分文字列を抽出するための正規表現ツールを提供していません。ユーザー定義関数でmysqlを拡張する準備ができている場合でも、それらを使用できないというわけではありません。

https://github.com/mysqludf/lib_mysqludf_preg

ソフトウェアをインストールする際の障害であるソフトウェアを配布したい場合、それはあまり役に立たないかもしれませんが、社内ソリューションでは適切かもしれません。

5
mc0e

私はそのようなことが可能だとは思わない。 SUBSTRING関数を使用して、必要な部分を抽出できます。

2
user483085

My home-grown regular expression replace function はこれに使用できます。

デモ

このDB-Fiddleデモ を参照してください。これは、有名なソネットからの2番目の単語( "I")とその出現回数(1)を返します。

[〜#〜] sql [〜#〜]

MySQL 8以降が使用されていると仮定すると( 共通テーブル式 の使用を許可するため)、次は2番目のWordとその出現回数を返します。

WITH cte AS (
     SELECT digits.idx,
            SUBSTRING_INDEX(SUBSTRING_INDEX(words, '~', digits.idx + 1), '~', -1) Word
     FROM
     (SELECT reg_replace(UPPER(txt),
                         '[^''’a-zA-Z-]+',
                         '~',
                         TRUE,
                         1,
                         0) AS words
      FROM tbl) delimited
     INNER JOIN
     (SELECT @row := @row + 1 as idx FROM 
      (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t1,
      (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t2, 
      (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t3, 
      (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t4, 
      (SELECT @row := -1) t5) digits
     ON LENGTH(REPLACE(words, '~' , '')) <= LENGTH(words) - digits.idx)
SELECT c.Word,
       subq.occurrences
FROM cte c
LEFT JOIN (
  SELECT Word,
         COUNT(*) AS occurrences
  FROM cte
  GROUP BY Word
) subq
ON c.Word = subq.Word
WHERE idx = 1; /* idx is zero-based so 1 here gets the second Word */

説明

上記のSQLではいくつかのトリックが使用されており、いくつかの認定が必要です。最初に、正規表現のリプレースメントを使用して、Word以外の文字のすべての連続ブロックを置換します。各ブロックは、単一のチルダ(~) キャラクター。 注:テキストにチルダが表示される可能性がある場合は、代わりに別の文字を選択できます。

this answer の手法は、区切られた値を持つ文字列を個別の行値に変換するために使用されます。 this answer の巧妙なテクニックと組み合わせて、インクリメントする数字のシーケンスで構成されるテーブルを生成します。この場合は0〜10,000です。

0
Steve Chambers