都内で働くSEの技術的なひとりごと / Technical soliloquy of System Engineer working in Tokyo

都内でサラリーマンやってます。SQL Server を中心とした (2023年からは Azure も。) マイクロソフト系(たまに、OSS系などマイクロソフト以外の技術も...)の技術的なことについて書いています。日々の仕事の中で、気になったことを技術要素関係なく気まぐれに選んでいるので記事内容は開発言語、インフラ等ばらばらです。なお、当ブログで発信、発言は私個人のものであり、所属する組織、企業、団体等とは何のかかわりもございません。ブログの内容もきちんと検証して使用してください。英語の勉強のため、英語の

『 昨日から片頭痛ある 』、『 昨日から頭痛がある 』という文がCONTAINS 関数で『 頭痛 』を指定すると、『 昨日から片頭痛ある 』という文がヒットしないので、なんとかヒットさせる術がないかどうか悩んでみる

編集

今回のやることを説明してみる

 SQL Server のフルテキストインデックスの問題について説明します。

使用する環境を説明してみる

 今回は、Microsoft Azure 上の仮想サーバではなく、私の Windows 8.1 Update1、64ビット 上で 動作している  SQL Server 2014 です。

困っていることを説明してみる

 下記の二つの文のうち一つの文が CONTAINS 関数で『 頭痛 』を指定しても検索されないので困っています。( 前からわかってましたが.... )

  • 昨日から片頭痛がある。
  • 昨日から頭痛がある。

 上記の二つの文を下図のようなテーブルに格納します。データベースは TestDB 、テーブルは TestTable としています。Col1 がプライマリーキーで、Col2 に対してフルテキストインデックスを張っています。

f:id:koogucc11:20140928110440p:plain

 テーブルに対して下記の SQL を発行します。しかし、Col1 = 2 のレコードのみしかヒットしません。

1
2
USE TestDB
SELECT * FROM dbo.TestTable WHERE CONTAINS(Col2,'"*頭痛*"')

f:id:koogucc11:20140928110750p:plain

 ここで問題なるのは、それぞれの文の形態要素解析結果です。それぞれの結果を見てみましょう。

  • 昨日から頭痛がある。( 昨日、から、頭痛、が、ある )

    f:id:koogucc11:20140928111236p:plain

  • 昨日から片頭痛がある。( 昨日、から、片頭痛、が、ある )

    f:id:koogucc11:20140928111248p:plain

 CONTAINS 関数は、形態要素解析で抽出された単語単位での一致または前方一致のみサポートされています。Col1 = 1 のレコードは、上文の下線部分『片頭痛』という単語で認識されているため、『頭痛』という検索条件ではヒットしません。

 さて、このままでは困ってしまいますね。どうしましょう?改善策はあるのでしょうか?

FREETEXT も試してみる

 全文検索の関数は、CONTAINS のほかに FREETEXT があります。これは、

Transact-SQL SELECT ステートメントTransact-SQL WHERE 句で使用される述語です。文字ベースのデータ型を含むフルテキスト インデックス列で SQL Server のフルテキスト検索を実行します。 この述語は、検索条件の文字列の並びと正確に一致しなくても意味が合っている値を検索できます。 FREETEXT を使用すると、フルテキスト クエリ エンジンによって、freetext_string を基に次の内部操作が実行され、各語に重みが割り当てられた後、一致するものが検索されます。

  • 単語の区切りに基づいて、文字列を個々の単語に分割。
  • 単語の語尾変化形を生成 (語幹への分割)。
  • 類義語との一致に基づいて、語の拡張と置き換えの一覧を決定。 

 ということです。

 CONTAINS は、

SQL Server で、単語または語句との完全一致検索やあいまい一致検索、特定の範囲内での検索、または重み付き検索を行います。 CONTAINS は、Transact-SQL SELECT ステートメントの WHERE 句で使用される関数です。文字ベースのデータ型を含むフルテキスト インデックス列で SQL Server のフルテキスト検索を実行します。
次に CONTAINS の検索対象を示します。

  • 単語または語句。
  • 単語または語句のプレフィックス。
  • 別の単語の近くにある単語。
  • 他の単語を語形変化して生成した単語。たとえば、drive という単語からは、drives、drove、driving、driven などの単語が生成されます。
  • 別の単語のシノニムになっている単語 (類義語を使用)。たとえば、"金属" という単語には、"アルミニウム" や "スチール" などのシノニムがあります。

 FREETEXT を使ってみましょう。使い方はほぼ、CONTAINS と同じです。FREETEXT はいろいろ試してはみましたが、どうも日本語検索では実用に少し厳しいような気がします。文を指定すると、かなり緩い感じでヒットするので、本当に探したいデータが検索できないような気がします。( ざっと検証しただけなので、なんとも言えませんが... )

1
2
USE TestDB
SELECT * FROM dbo.TestTable WHERE  FREETEXT(Col2,'頭痛')

f:id:koogucc11:20140928173837p:plain

希望を語ってみる

 MeCab のように、『片』を接頭詞と判断してくれるといいんですが、 SQL Server 標準のワードブレーカーは、『片』を接頭詞として判断せず、『 片頭痛 』を単語として判断してしまうんですよね。SQL Server のワードブレーカーも MeCab のようにワードブレークしてくれないですかね?

f:id:koogucc11:20140928174104p:plain

 IWordBreaker を実装し、MeCab を使った自作のワードブレーカーを開発するしかないのでしょうか。

次回何をするのか宣言してみる

  次回は独自のワードブレーカー開発ですかね.....SQL Server のフルテキストインデックスはまだまだ課題が多いです。

※2008の本ですが、買う価値ありかな...

Pro Full-Text Search in SQL Server 2008 (Expert's Voice in SQL Server)

Pro Full-Text Search in SQL Server 2008 (Expert's Voice in SQL Server)