人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

複数フィールド&別テーブルが絡む検索(MySQL)について教えて下さい

記事の投稿ができ、各記事に対してタグ付けができるサイトを検討しています。
その中でワード検索を行いたいと考えています。
例を示します。

[postsテーブル]
pid title city txt
1 映画 東京 のびたがついに大活躍
2 映画 東京 スポーツ系物語
3 演劇 大阪 お笑い系
・・・

[tagsテーブル]
tid pid tag
1 1 どらえもん
2 1 面白い
3 2 青春
・・・

【検索の仕様】
・検索対象フィールドは以下
postsテーブルのtitle, city, txt および tagsテーブルのtag
・複数語検索に対応(空白区切りのAND検索)

例えば、
検索窓に「のびた どらえもん」と入れるとpid1がマッチします。
(検索語がpostsのtxtとtagsのtagにlikeマッチ)
「どらえ 面白」と入れるとpid1がマッチします。
(検索語がtagsのtagにlikeマッチ)
同様に「映画 東京 スポーツ」だとpid2がlikeマッチします。

環境はMySQL、PHPになります。
ここ数日、どのようなSQL文を組んだら良いのか悩んでいます。
アドバイス頂ければ嬉しいです。

(できれば、補足情報としてtagにマッチしたどうかが分かるフラグ情報まで得られればベストです。)

●質問者: puuyan
●カテゴリ:コンピュータ ウェブ制作
○ 状態 :終了
└ 回答数 : 2/2件

▽最新の回答へ

1 ● きゃづみぃ
●100ポイント

検索に使用する単語は 一つに したほうがいいですね。

たとえば 「映画 東京 スポーツ」の場合は 「映画」、「東京」、「スポーツ」の3つにわけて 3回ループして 取得する。

上記のキーワードで
[postsテーブル]
titleとcityとtxtに 対して それぞれ likeでORで pidを取得する。


[tagsテーブル]
こちらは tagで like 検索して pidを取得する。

これを 単語数繰り返して どの 単語でも 取得できた pidを 検索結果とする。


こんな感じで できますか?


puuyanさんのコメント
ありがとうございます。 よろしければもう少しお聞きしたいです。 (1)保持配列 ご回答頂いた仕組みは理解できます。ありがとうございます。 私が組むとしたら以下のような形になると思います。 この場合、ループ内ではpidのみを取得する形が良いでしょうか? その場合はループ終了後に、pidをキーにして再度DB検索(SQLのINを使う?)を行う必要が出てくると思いますが、仕方ないでしょうか。 もしくは思い切ってループ内で全利用フィールドを取得してしまうのもありだと思いますが。 このあたりは件数見合いで変わってくる気がしますが、アドバイスを頂ければ嬉しいです。 (最終的に必要なのはpidのみではなくpostsテーブルの当該レコードフィールドになります。) $kekka = array(); foreach ($words as $word) { $res1 = postsテーブル(title,city,txt)から検索した結果配列(pidのみ?/全利用フィールド?) $res2 = tagsテーブル(tag)から検索した結果配列(pidのみ?/全利用フィールド?) $res = res1とres2のマージ(array_unique等を使って重複削除) if (!$kekka) { $kekka = $res; } else { $kekka = array_intersect($kekka, $res); // 共通部分を求める } } ・$kekkaに最終結果が入る(pidのみ?/全利用フィールド?) ・ループ内でpidのみを取得している場合は、pidをキーに再度DB検索して当該レコードの必要フィールドを得る。(SQLのIN演算子を利用?) (2)SQL発行回数 今回の仕組みですと「映画 東京 スポーツ」の場合は最低6回のSQLが発行されます。 (ループ3回、ループ内で2回(postsテーブルおよびtagsテーブルを対象)) 出来ればSQL発行回数を抑えたいのですが、妙案はありませんでしょうか。 しかしトリッキーにSQLを使用するのは避けたいと思いますので、通常適用される範囲で良い案があれば教えて頂けると嬉しいです。

きゃづみぃさんのコメント
>この場合、ループ内ではpidのみを取得する形が良いでしょうか? そうですね。メモリとの兼ね合いですが、件数が少ないようでしたら 全部取得したほうが ラクでしょう。 ま、これぐらいの項目数ならば 全部 取得してもいいと思います。 抽出内容が 同じものは UNION で SQLを連結させれば 発行回数自体は 減らせます。

puuyanさんのコメント
ありがとうございます。 PHP側での力技部分が多くなりますが、確かにこの仕組みを採用すれば今回の検索は実現できそうですね。 候補の一つとして考えます。

2 ● taroe
●100ポイント ベストアンサー

今回の場合は、
[postsテーブル]と[tagsテーブル]をJOINして
これをSQLで検索する方向で考えてみるべき

たぶん、SQL1回でOKだと思うけど。
トリッキーでなくて普通に使われる手法。

JOIN自体がトリッキーなら、方法はプログラムでがんばるしかない。


>今回の仕組みですと「映画 東京 スポーツ」の場合は最低6回のSQLが発行されます。
>(ループ3回、ループ内で2回(postsテーブルおよびtagsテーブルを対象))
>出来ればSQL発行回数を抑えたいのですが、妙案はありませんでしょうか。
>しかしトリッキーにSQLを使用するのは避けたいと思いますので、通常適用される範囲で良い案があれば教えて頂けると嬉しいです。


[postsテーブル]と[tagsテーブル]をJOINして
SQLで検索すれば、SQL発行回数は減りますが・・・。


JOINは普通の方法ですし、そんなに遅いわけでもなくて
プログラムでループしてマッチさせるより早いこともあります。

これ、データ件数とかにもよるんですね。
データ件数が多くなるほど、SQLで力尽くJOINというのは遅くなる傾向にあり
プログラムで力尽くの方がかえって早くなるということがあります。


SQLの発行回数よりも
実際の処理時間

SQL3回と
SQL6回でも

6回の方が早いことも多々ありますよ。


puuyanさんのコメント
ご回答ありがとうございます。 >[postsテーブル]と[tagsテーブル]をJOINして >これをSQLで検索する方向で考えてみるべき はい。私もjoinで考え始めました。しかし、私のSQLスキルでは上手く出来ず、ご質問させて頂きました。 joinで詰まっている箇所は以下になります。 [SQL例] select p.* from posts as p left join tags as t on p.pid=t.pid where (p.title like '%のびた%' or p.city like '%のびた%' or p.txt like '%のびた%' or t.tag like '%のびた%') and (p.title like '%どらえもん%' or p.city like '%どらえもん%' or p.txt like '%どらえもん%' or t.tag like '%どらえもん%') 当初、上記のようなSQLを考えました。 例のように「のびた どらえもん」の検索では上手く検索できます。 ですが、「どらえ 面白」の場合は検索できません。('どらえ'と'面白'はいずれもtagsテーブルのtagに存在します。) tagsテーブルがログ型?(ストック型というのでしょうか?、同一のpidが複数存在します。)になっているため、上記のようなSQLではjoinしても検索できないということまでは分かりました。 じゃあどうしたら良いのか、私のSQLスキルでは正直良く分かりません。 先に進めず、ここ数日悩んでいます。 アドバイスを頂ければ嬉しいです。 >SQLの発行回数よりも >実際の処理時間 >SQL3回と >SQL6回でも >6回の方が早いことも多々ありますよ。 おっしゃる通りなのだと思います。 失礼しました。

taroeさんのコメント
■方法1 一番簡単なのは、 1記事につけれるタグの数に制限をつけて [tagsテーブル]を pid tag名1 tag名2 のような構造にして、 LEFT JIONでなくて1対1のJOINをすれば簡単になると思うが・・ テーブル構造も2テーブルでなくて 1.記事 2.pid id 対応表 3.タグマスタ id tag名 の用に分けてみても。 現状、タグidって存在する意味さえなさそうだけど。 ワード検索で ・記事タイトル関係とタグとを同時に扱う ・AND条件 この2つが難しくしてると思う。 SQLのIN句 IN フィールド名 (SECECL * FROM TABLE WHERE ***) または IN フィールド名 (値1,値2,値3 ***) みたいな物も使えると思う。 あとlike検索は元々遅いので この方式自体がどうかなというところもあると思う。

puuyanさんのコメント
ありがとうございます。 >1記事につけれるタグの数に制限をつけて >[tagsテーブル]を >pid tag名1 tag名2 >のような構造にして、 うーん、こちらの方法は余りスマートではありませんね。 >テーブル構造も2テーブルでなくて >1.記事 >2.pid id 対応表 >3.タグマスタ id tag名 >の用に分けてみても。 ありがとうございます。 この3テーブル構成にした場合は、現在ネックになっている部分は解決するのでしょうか?(結論として1対1のjoinになる?) 頭がまわらず、この構成にする利点が良く掴めておりません。 >ワード検索で >・記事タイトル関係とタグとを同時に扱う >・AND条件 >この2つが難しくしてると思う。 おっしゃる通りで悩んでいます。 >あとlike検索は元々遅いので >この方式自体がどうかなというところもあると思う。 他にどんな方式があるのでしょうか? ヒントや障りだけでも教えて頂けると助かります。
関連質問

●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ