2012-01-25 25 views
2

私はポストは、タグ(多対多の関係を)与えられた関係を表す以下の3つのテーブルがあるとします。クエリ(多対多の関係)

create table posts (id integer, content text, primary key (id)); 
create table tags (tag varchar(30), description text, primary key (tag)); 
create table post_tags (post_id integer, tag varchar(10), 
    primary key (post_id, tag), 
    foreign key (post_id) references posts (id), 
    foreign key (tag) references tags (tag)); 

ここで、正確に{clever、interesting}のタグを含むすべての投稿を探したいとします。

私の試みは失敗です。それは{clever、interesting}のタグを持つ投稿を見つけるが、{clever、interesting、noyoying}や{clever、interesting、unethical}のタグを持つ投稿も見つける。

select t1.post_id from post_tags as t1 
    inner join post_tags as t2 on t2.post_id=t1.post_id 
    where t1.tag='clever' and t2.tag='interesting'; 

この操作を簡単にするために構造を変更するための提案はありません。しかし、私はpostsテーブルにカンマで区切ったタグのリストを追加するようなことは避けたい。

+2

'tags'には' post_id'が含まれていません。これは '実行エラー'である必要があります。また、 '興味深い'には11文字が含まれています。これは 'varchar(10)'の問題かもしれません。おそらく 'tag 'を別のintベースのフィールドにして' description 'フィールドを使うか、大きい、ユニークな) '名前'フィールド。 –

+0

''interesting' 'は' VARCHAR(10) 'フィールドの中にどのように格納されていますか? –

+1

あなたが必要とするのは、[余りのない完全な関係分割]です。(http://www.simple-talk.com/sql/t-sql-programming/divided-we-stand-the-sql-of-relational-division/ )。あなたがMySQLを使っているとき、私は実際に 'GROUP_CONCAT'がどのようにこれを実行するのかを調べます。 –

答えて

3

これは、正確なリレーショナル分割問題で、この使用NOT EXISTSを行うことができるはず。 (post_id,tagにユニーク制約を仮定して)SQL Serverでの

well performing方法は

SELECT post_id 
FROM post_tags 
GROUP BY post_id 
HAVING MIN(CASE 
      WHEN Keyword IN ('clever', 'interesting') THEN 1 
      ELSE 0 
      END) = 1 
     AND SUM(CASE 
       WHEN Keyword IN ('clever', 'interesting') THEN 1 
       ELSE 0 
       END) = 2 

だから私は代わりにHAVINGGROUP_CONCATを使用してのアイデアを排除しないでしょうです。

+0

これは素晴らしいですね。この問題の名前をありがとうございます。 – Snowball

+0

私はこのルートに行きました。クエリはGROUP_CONCATメソッドを使用して約0.8秒で実行されますが、これはおそらく十分です。それが問題になったら、簡単な方法で 'posts'テーブルにカンマ区切り形式でタグを保存します。 – Snowball

2

は、例えば、

select t1.post_id 
from post_tags as t1 
    inner join post_tags as t2 on t2.post_id = t1.post_id 
where 
    t1.tag = 'clever' 
and t2.tag = 'interesting' 
and not exists (
    select * 
    from post_tags t3 
    where 
     t3.tag not in ('clever', 'interesting') 
    and t3.post_id = t1.post_id 
) 
+0

これは完全に機能しますが、残念ながら遅すぎます。私の 'posts'テーブルは〜80k個のエントリを持ち、' ​​tags'は〜8k個あります。 – Snowball

+0

パフォーマンスは決して良い解決策を拒否する理由ではありません。クエリは正しいと思われ、できるだけリーンに見えます。多分PKやインデックスをpost_tagsに追加すると助けになるでしょう。ところで、ig0774はpost_tabsテーブル(間違っているようです)を使用していないため、元のクエリを再構成したようです。 – wildplasser

+0

@wildplasser:良い点。このソリューションを再評価し、PK /インデックスの提案を念頭に置いていきます。タグ/ post_tagsの彼の間違いは、実際は私の間違いでした。私は元の質問でそれらを混ぜたが、今編集した。 – Snowball