2011-11-12 8 views
0

誰も私にこの種の検索をデータベースで行う方法を教えてもらえますか?JOINを使ってMySQLを検索するには?

私はこれらのテーブルを得た:

posts (id, tags_cache) 
tags (id, name) 
posts_tags (post_id, tag_id) 

ユーザーが検索クエリを入力する(「水青」と言う)と私は両方のタグを持っている投稿を表示したいです。

SELECT p.*, GROUP_CONCAT(t.name) AS tags_search 
FROM posts p 
LEFT JOIN posts_tags pt ON p.id = pt.post_id 
LEFT JOIN tags t ON pt.tag_id = t.id 
GROUP BY p.id 
HAVING FIND_IN_SET('water', tags_search) > 0 
AND FIND_IN_SET('blue', tags_search) > 0 

posts.tags_cacheテキスト列は、それが(:water:15 blue:20この方法)に属している名前とタグのIDを格納します。私は検索すると考えることができます 唯一の方法は、FIND_IN_SET、この方法を使用しています。回避するために

は、検索のために、この列を使用してJOINを、私は LIKE INSTRを試みたが、あなたは'LLは「タグ付けの記事を取得します「TER」、あなたのために検索することができますので、これらには、不正確な結果が得られます 'と' termal 'などです。私はまた、正確な結果を与えるREGEXPを試みましたが、それは遅いプロセスです。

私は使用できませんMATCHはテーブルとしてInnoDBを使用しています。

だから、これを達成する他の方法はありますか?

[編集]

私は(だけでなく、2)ユーザーが多くのタグを検索できることを言及し、さらにタグを除外するのを忘れ:検索の投稿は「水」をタグ付けしましたが、「青」ではありません。 FIND_IN_SETでは、これは私の作品:

HAVING FIND_IN_SET('water', tags_search) > 0 
AND NOT FIND_IN_SET('blue', tags_search) > 0 

[EDIT2]

ypercubeが示唆したように、私は(つまり、唯一のクエリは、キャッシュされたのにかかった時間にチェック)いくつかの性能試験を行った、そしてこれらの結果、次のとおりです。

muists | Bill K | ypercu | includes:excludes 
-------------------------- 
0.0137 | 0.0009 | 0.0029 | 2:0 
0.0096 | 0.0081 | 0.0033 | 2:1 
0.0111 | 0.0174 | 0.0033 | 2:2 
0.0281 | 0.0081 | 0.0025 | 5:1 
0.0014 | 0.0013 | 0.0015 | 0:2 

この情報が有効なリソースであるかどうかはわからない...しかし、それはタグごとに登録しようとypercubeの方法が最速であることを示しています。

+3

"なぜジョインを避けようとしていますか? –

+0

さて、私はどこかで、多くのJOINを使うのはあまり良くないと読んでいます... – Parziphal

+3

あなたはそれをどこで覚えていますか?将来そこで読むことを避けてください。 –

答えて

1

追加のリクエストでは、一部のタグを除いて、次の方法を使用できます。それは水と青の両方のタグを持っていて、黒、白、赤のタグを持たないすべての投稿を表示します:

SELECT p.* 
FROM posts p 
    INNER JOIN posts_tags pt1 ON p.id = pt1.post_id 
    INNER JOIN tags t1 ON pt1.tag_id = t1.id 
    INNER JOIN posts_tags pt2 ON p.id = pt2.post_id 
    INNER JOIN tags t2 ON pt2.tag_id = t2.id 
WHERE (t1.name, t2.name) = ('water', 'blue')   --- include 
    AND NOT EXISTS 
     (SELECT * 
     FROM posts_tags pt 
      INNER JOIN tags t ON pt.tag_id = t.id 
     WHERE p.id = pt.post_id 
      AND t.name IN ('black', 'white', 'red')  --- exclude 
    ) 
+0

mu-is-too-shortと似ていますが、muのタグごとに1つのJOINはありません(今これらはJOINですそれは避けることができる)。 – Parziphal

+1

何かを避ける前に、パフォーマンスをテストしてください。あなたは驚くかもしれません。 –

4

JOINを使用したくない理由、LEFT JOINを使用しようとしている理由がわかりません。あなたはそこにいるのではなく)そこにあるものを探しているので、LEFT JOINを取り除いてただのJOINを取り除いてください。そして、tags_cacheの列を取り除くと、あなたはその種の問題に悩んでいるだけです。

select p.id 
from posts p 
join posts_tags pt on p.id = pt.post_id 
join tags t on pt.tag_id = t.id 
where t.name in ('water', 'blue') 
group by p.id 
having count(t.id) = 2 

HAVING句で2は、あなたが探しているタグの数である。このような

何かがあなたが探しているものです。

そして、あなたが特定のタグを除外したい場合、あなたはこのようなWHERE句にそれを追加することができます。

select p.id 
from posts p 
join posts_tags pt on p.id = pt.post_id 
join tags t on pt.tag_id = t.id 
where t.name in ('water', 'blue') 
    and p.id not in (
    select pt.post_id 
    from posts_tags pt 
    join tags t on pt.tag_id = t.id 
    where t.name in ('pancakes', 'eggs') -- Exclude these 
) 
group by p.id 
having count(t.id) = 2 
+0

これはいいですが、ユーザーがタグを除外している場合はどうなりますか? (上記の[編集]をお読みください) – Parziphal

+0

@renocor:私の更新のように 'NOT IN'を使って世話をすることができます。 –

+0

申し訳ありませんが、これは動作します。これは私が探していたものです。(私が探していたものは本当にわかりませんが、私は以前見たことのないものが欲しかったです)。どうもありがとう! – Parziphal

3

異なる行にいくつかの条件のすべてに一致する記事を見つけるには、共通の問題です。

SELECT p.* 
FROM posts p 
INNER JOIN posts_tags pt ON p.id = pt.post_id 
INNER JOIN tags t ON pt.tag_id = t.id 
WHERE t.name IN ('water', 'blue') 
GROUP BY p.id 
HAVING COUNT(DISTINCT t.name) = 2; 

または::ここで

はそれを行うには、2つの方法があり

SELECT p.* 
FROM posts p 
INNER JOIN posts_tags pt1 ON p.id = pt1.post_id 
INNER JOIN tags t1 ON pt1.tag_id = t1.id 
INNER JOIN posts_tags pt2 ON p.id = pt2.post_id 
INNER JOIN tags t2 ON pt2.tag_id = t2.id 
WHERE (t1.name, t2.name) = ('water', 'blue'); 

再コメントして編集:

有する溶液との問題は、それがなければならないということですテーブルスキャンを実行し、テーブル内のすべての行を検索します。これは、JOINよりもはるかに遅い場合があります(適切なインデックスがある場合)。ここでは、タグの除外条件をサポートする

は、私はそれを書くだろう方法は次のとおりです。

SELECT p.* 
FROM posts p 
INNER JOIN posts_tags pt1 ON p.id = pt1.post_id 
INNER JOIN tags t1 ON pt1.tag_id = t1.id AND t1.name = 'water' 
LEFT OUTER JOIN (posts_tags pt2 
INNER JOIN tags t2 ON pt2.tag_id = t2.id AND t2.name = 'blue') 
    ON p.id = pt2.post_id 
WHERE t2.id IS NULL; 

は使用を避けるあなたはそれがどこかに彼らが悪いということは無意味である読んでいるので参加します。 JOINはリレーショナルデータベースの基本的な操作であることを理解しておく必要があります。

+0

最初のオプションは@ mu-is-too-shortと似ていますが、タグを除外するとどうなりますか? – Parziphal

+2

@renocor:これを達成するためのさまざまな方法と、パフォーマンステスト(PostgreSQL用ですがMySQLにも同様の結果があります)を参照すると、 'JOIN'がおそらく高速になることがわかります:http://stackoverflow.com/質問/ 7364969/how-to-filter-sql-results-in-a-has-many-through-relation –

関連する問題