この問題を解決するために覚えておく必要のある「ゲームのルール」をいくつか示します。あなたはおそらくこれらをすでに知っているでしょうが、それらを明確に述べることは他の読者のために確認するのを助けるかもしれません。
- MySQLのすべてのインデックスは、単一のベーステーブルの列のみを参照できます。複数のテーブルにまたがってインデックスを作成するフルテキストインデックスを作成することはできません。
- ビューのインデックスを定義することはできず、ベーステーブルのみを定義できます。
-
MATCH()
フルテキストインデックスに対するクエリは、インデックスで宣言された順序で、フルテキストインデックスのすべての列と一致する必要があります。
インデックスを作成するコンテンツを格納するための3番目のテーブルを作成します。このコンテンツを冗長に保存する必要はありません。3番目のテーブルにのみ保存してください。これは、オブジェクト指向設計から「共通のスーパークラス」の概念を借用しています(RDBMS設計に適用できるため非公式です)。
CREATE TABLE Searchable (
`id` SERIAL PRIMARY KEY,
`title` varchar(100) default NULL,
`description` text,
`keywords` text,
`url` varchar(255) default '',
FULLTEXT KEY `TitleDescFullText` (`keywords`,`title`,`description`,`url`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `shopitems` (
`id` INT UNSIGNED NOT NULL,
`ShopID` INT UNSIGNED NOT NULL,
`ImageID` INT UNSIGNED NOT NULL,
`pricing` varchar(45) NOT NULL,
`datetime_created` datetime NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`id`) REFERENCES Searchable (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `shops` (
`id` INT UNSIGNED NOT NULL,
`owner_id` varchar(255) default NULL,
`datetime_created` datetime default NULL,
`created_by` varchar(255) default NULL,
`datetime_modified` datetime default NULL,
`modified_by` varchar(255) default NULL,
`overall_rating_avg` decimal(4,2) default '0.00',
PRIMARY KEY (`id`),
FOREIGN KEY (`id`) REFERENCES Searchable (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
自動インクリメントキーを持つ唯一のテーブルがSearchable
になっていることに注意してください 。テーブルshops
およびshopitems
互換性のあるデータ型のキーを使用しますが、自動インクリメントは使用しません。したがって、Searchable
に行を作成する必要があります id
を生成します いずれかのshops
で対応する行を作成する前の値 またはshopitems
。
FOREIGN KEY
を追加しました MyISAMはこれらの制約を黙って無視しますが、説明のための宣言です(また、全文索引をサポートするにはMyISAMを使用する必要があることをすでに知っています)。
これで、両方のshops
のテキストコンテンツを検索できます。 およびshopitems
単一のフルテキストインデックスを使用した単一のクエリで:
SELECT S.*, sh.*, si.*,
MATCH(keywords, title, description, url) AGAINST('dummy') As score
FROM Searchable S
LEFT OUTER JOIN shops sh ON (S.id = sh.id)
LEFT OUTER JOIN shopitems si ON (S.id = si.id)
WHERE MATCH(keywords, title, description, url) AGAINST('dummy')
ORDER BY score DESC;
もちろん、Searchable
の特定の行について ショップまたはショップアイテムのいずれか1つのテーブルのみが一致する必要があり、これらのテーブルには異なる列があります。したがって、sh.*
のいずれかです。 またはsi.*
結果ではNULLになります。アプリケーションで出力をフォーマットするのはあなた次第です。
他のいくつかの回答では、 SphinxSearch の使用が提案されています。 。これは、MySQLを補完し、より高度な全文検索機能を追加するもう1つのテクノロジーです。クエリのパフォーマンスが優れているため、かなり魅了されている人もいます。
ただし、インデックスの作成、特にインデックスへの段階的な追加にはコストがかかります。実際、Sphinx Searchインデックスの更新にはコストがかかるため、推奨される解決策は、古いアーカイブデータ用に1つのインデックスを作成し、更新される可能性が高い最近のデータ用に別の小さなインデックスを作成することです。次に、すべての検索で、2つの別々のインデックスに対して2つのクエリを実行する必要があります。また、データが変更されていない古いデータのパターンに自然に役立たない場合は、とにかくこのトリックを利用できない可能性があります。
コメントを再確認してください:SphinxSearchのドキュメント からの抜粋です。 インデックスのライブアップデートについて:
Sphinx Searchインデックスの更新にはコストがかかるため、更新するインデックスをできるだけ小さくすることが解決策であるという考え方です。そのため、(例では)最新のフォーラム投稿のみが変更されますが、アーカイブされたフォーラム投稿のより大きな履歴は変更されないため、そのコレクションの2番目のより大きなインデックスを一度作成します。もちろん、検索を行う場合は、両方のインデックスをクエリする必要があります。
定期的に、たとえば週に1回、「最近の」フォーラムメッセージは「アーカイブ済み」と見なされ、最近の投稿の現在のインデックスをアーカイブ済みインデックスにマージして、小さい方のインデックスを最初からやり直す必要があります。彼らは、2つのSphinx Searchインデックスをマージする方が、データの更新後にインデックスを再作成するよりも効率的であることを示しています。
しかし、私のポイントは、すべてのデータセットが、頻繁に更新される最近のデータとは異なり、変更されないアーカイブされたデータセットを持つパターンに自然に分類されるわけではないということです。
たとえば、データベースを考えてみましょう。ショップとショップアイテムがあります。これらを、新しい行ではなく、変更されない行にどのように分離できますか?カタログ内のすべてのショップまたは製品は、それらの説明を更新することを許可されるべきです。ただし、変更を加えるたびにSphinx Searchインデックス全体を再構築する必要があるため、非常にコストのかかる操作になります。おそらく、変更をキューに入れてバッチで適用し、週に1回インデックスを再構築します。ただし、ショップの説明の小さな変更が日曜日の夜まで有効にならない理由をショップベンダーに説明してみてください。