0

別のテーブルに複数の行があるかどうかに基づいて、投稿テーブルから行をロードしようとしています。以下のテーブル構造を取ります。

投稿

post_id  post_title
-------------------
1        My Post
2        Another Post

投稿タグ

post_tag_id  post_tag_name
--------------------------
1            My Tag
2            Another Tag

投稿タグ

postTag_id  postTag_tag_id  postTag_post_id
------------------------------------------
1           1               1
2           2               1

当然のことながら、post と post_tags は投稿とタグを格納し、postTags はどの投稿がどのタグを持っているかを結合します。

テーブルを結合するために通常行うことは次のとおりです。

SELECT * FROM (`posts`)
JOIN `postTags` ON (`postTag_post_id` = `post_id`)
JOIN `post_tags` ON (`post_tag_id` = `postTag_tag_id`)

次に、タグに関する情報を取得し、後でクエリに追加して検索語などのタグ名を検索し、検索語に一致する投稿を取得したらグループ化します。

私がやろうとしているのは、投稿にタグ 1 とタグ 2 の両方が含まれている投稿からのみ選択することであり、そのための SQL を解決できません。上記の結合を実行すると、明らかに2行が返されるため、WHERE句を使用するのではなく、実際のJOINで実行する必要があると思います。

WHERE post_tag_id = 1 AND post_tag_id = 2

各行には post_tag_id が 1 つしかないため、1 つの行で同じ列の異なる値を確認することはできません。

私がやろうとしたことは次のようなものです:

SELECT * FROM (`posts`)
JOIN `postTags` ON (postTag_tag_id = 1 AND postTag_tag_id = 2)
JOIN `post_tags` ON (`post_tag_id` = `postTag_tag_id`)

しかし、これを実行すると0の結果が返されます。私は以前、同様のことのためにこのような条件を JOINS に入れました。近いと確信していますが、これが機能しない場合はどうすればよいかわかりません。

私は少なくとも正しい軌道に乗っていますか?うまくいけば、私は明らかな何かを見逃していません。

ありがとう。

4

4 に答える 4

2

あなたはpostTags行に同時に何かを要求しようとしています。

両方を取得するには、post_tags と postTags に 2 つの結合を行う必要があります。または、投稿にはこれら 2 つの間の任意のタグを付けることができ、タグの合計数は 2 に等しくなければならない (投稿が同じタグに複数回関連付けることはできないと仮定して) と言うことができます。

最初のアプローチ:

SELECT *
FROM `posts` as p
WHERE p.`post_id` IN (SELECT pt.`postTag_post_id`
                      FROM `postTags` as pt
                      WHERE pt.`postTag_tag_id` = 1)
AND p.`post_id` IN (SELECT pt.`postTag_post_id`
                    FROM `postTags` as pt
                    WHERE pt.`postTag_tag_id` = 2);

2 番目のアプローチ:

SELECT *
FROM posts as p
WHERE p.post_id IN (SELECT pt.postTag_post_id
                    FROM (SELECT count(0) as c, pt.postTag_post_id
                          FROM postTags as pt
                          WHERE pt.postTag_tag_id IN (1, 2)
                          GROUP BY pt.postTag_post_id
                          HAVING c = 2) as pt);

また、最初のアプローチで IN または EXISTS を使用すると、複数のタグがあるという理由だけで、同じ投稿行に複数の行が表示されることはありません。このようにして、後で 1 つの DISTINCT を保存すると、クエリが遅くなります。私が使用する経験則として、2 番目のアプローチで IN を使用しました。データを表示する必要がない場合は、FROM セクションで JOIN を実行する必要はありません。

于 2012-08-26T11:22:14.280 に答える
1
SELECT p.*, t1.*, t2.* FROM posts p
INNER JOIN postTags pt1 ON pt1.postTag_post_id = p.id AND pt1.postTag_tag_id = 1
INNER JOIN postTags pt2 ON pt2.postTag_post_id = p.id AND pt2.postTag_tag_id = 2
INNER JOIN post_tags t1 ON t1.post_tag_id = pt1.postTag_tag_id
INNER JOIN post_tags t2 ON t2.post_tag_id = pt2.postTag_tag_id
于 2012-08-26T11:14:41.543 に答える
0

タグ ID (1および2) が既にわかっていると仮定すると、次のようなことができます。

SELECT post_id, post_title
FROM posts JOIN postTags ON (postTag_post_id = post_id)
WHERE postTag_tag_id IN (1, 2)
GROUP BY post_id, post_title
HAVING COUNT(DISTINCT postTag_tag_id) = 2

注: に代替キーがある場合、DISTINCT は必要ありpostTags {postTag_tag_id, postTag_post_id}ません。

注: タグ ID を持っていない (タグ名だけを持っている) 場合は、(post_tagsテーブルに向かって) 別の JOIN が必要になります。


postTags.postTag_idところで、ジャンクション テーブル ( ) の代理 PK を捨てて、自然な PK だけを持つことを真剣に検討する必要があります{postTag_tag_id, postTag_post_id}InnoDB テーブルはクラスター化されており、クラスター化されたテーブルのセカンダリ インデックスは、ヒープベースのテーブルよりもファットで低速です。また、一部のクエリは、同じタグでタグ付けされた投稿を物理的に近くに保存することでメリットが得られます (または、PK を逆にする場合は、同じ投稿のタグを近くに保存します)。

于 2012-08-26T12:15:46.807 に答える
0

あなたと同じデータベースを実際に構築しないと、これを確認するのは困難ですが、動作するはずです。

まず、このタイプのクエリは、分析クエリをサポートするデータベース (Oracle、MS SQL Server) でははるかに簡単でパフォーマンスが高いということから始めましょう。そのため、MySQL では、古くてくだらない集約的な方法でそれを行う必要があります。

また、post_tags にタグの名前を格納するテーブルと、postTags の投稿への投稿タグのマッピングが混乱を招くことも言いたいと思います。私だったら、マッピング テーブルの名前を post_tags_map または post_tags_to_post_map に変更します。したがって、post_id を持つ投稿、post_tags_id を持つ post_tags、post_tags_map_id を持つ post_tags_map を持つ投稿ができます。そして、これらの id 列は、すべてのテーブルで同じ名前になります。他のテーブルで異なる名前が付けられた同じ列を持つことも混乱を招きます。

とにかく、あなたの問題を解決しましょう。最初に、行ごとに 1 つの投稿 ID で、タグ 1 と 2 を持つ投稿のみの結果セットが必要です。

select postTag_post_id, count(1) cnt from (
  select postTag_post_id from postTags where postTag_tag_id in (1, 2)
) group by postTag_post_id;`

次のようなデータが返されるはずです。

postTag_post_id | cnt
              1 |   2

次に、その結​​果セットを posts テーブルに結合できます。

select * from posts p,
(
  select postTag_post_id, count(1) cnt from (
    select postTag_post_id from postTags where postTag_tag_id in (1, 2)
  ) group by postTag_post_id;
) t
where p.post_id = t.postTag_post_id
and t.cnt >= 2;

post_tag_name から postTag_tag_id を取得するために、post_tags テーブルに別の結合を行う必要がある場合、最も内側のクエリは次のように変更されます。

select postTag_post_id
from postTags a,
post_tags b
where a.postTag_tag_id = b.post_tag_id
and b.post_tag_name in ('tag 1', 'tag 2');

これでうまくいくはずです。

于 2012-08-26T11:27:02.907 に答える