2

これらのモックアップ テーブルを使用して、多対多の関係のケースを単純化しました。

Posts:
------------------------------
|   id |    title |     body |
------------------------------
|    1 |      One |    text1 |
|    2 |      Two |    text2 |
|    3 |    Three |    text3 |
------------------------------

Tags:
-------------------
|   id |     name |
-------------------
|    1 |      SQL |
|    2 |     GLSL |
|    3 |      PHP |
-------------------

Post_tags:
------------------------------
|   id |    p_id |      t_id |
------------------------------
|    1 |       1 |         1 |
|    2 |       1 |         3 |
|    3 |       2 |         1 |
|    3 |       3 |         2 |
------------------------------

私の目標は、問題のない特定の TAGS を使用して POSTS をクエリすることですが、クエリしたタグだけでなく、投稿に関連するすべてのタグも表示したいと考えています。私のクエリは次のようになります。

SELECT p.Title, p.Body, t.name
FROM Posts p
LEFT JOIN Post_tags pt ON p.id = pt.p_id
LEFT JOIN Tags t ON t.id = pt.t_id
WHERE t.name LIKE '%SQL%'

これは「SQL」タグを持つ投稿を取得しますが、「SQL」文字列が見つかったタグで投稿テーブルを結合するだけなので、投稿に関連付けられた他のタグ「PHP」は結合されません。

明らかに問題は、WHERE 句でテーブルを結合していることですが、これを 1 つのクエリまたは (できればサブクエリで) 解決するにはどうすればよいでしょうか?

現在、アプリケーションの 2 つの個別のクエリでこれを行っています。1 つは一致する投稿を選択するためのもので、もう 1 つは完全な投稿データを取得するためのものです。これはあまり効率的ではなく、不十分な解決策のようにも思えます。私はまだより良い方法を見つけていないので、StackOverflow コミュニティに問い合わせることにしました。

4

5 に答える 5

3

の古い答えは最短ではありません。ここに最短のものがあります:

select p.*, '' as x, t.name, t.name like '%SQL%'
from Posts p
join Posts_tags pt on pt.p_id = p.id 
join Tags t on t.id = pt.t_id;

出力:

ID  TITLE   BODY    X       NAME    T.NAME LIKE '%SQL%'
1   One     text1           SQL     1
1   One     text1           PHP     0
2   Two     text2           SQL     1
3   Three   text3           GLSL    0

したがって、ID でグループ化し、グループ内の要素の少なくとも 1 つ (bit_or によって支援されます。Postgresql にもこれがあり、bool_or という適切な名前が付けられています) が「%SQL%」基準を満たしているかどうかを確認すると、そのビットは ON (別名 boolean) です。 =真)。そのグループを選択すると、そのグループの下のすべてのタグが保持されます。たとえば、タグ ID 1 は投稿 1 に表示され、投稿 1 には他のタグ (#3 または PHP) があります。フィルターを使用しないため、同じ投稿 ID に属するすべてのタグは破棄されません。代わりにフィルターWHEREを使用します。HAVING

select p.*, group_concat(t.name) as tags
from Posts p
join Posts_tags pt on pt.p_id = p.id 
join Tags t on t.id = pt.t_id
group by p.id
having bit_or(t.name like '%SQL%');

これを次のように書き換えることもできます。

select p.*, group_concat(t.name) as tags
from Posts p
join Posts_tags pt on pt.p_id = p.id 
join Tags t on t.id = pt.t_id
group by p.id
having sum(t.name like '%SQL%') >= 1;

BIT_ORIN、またはのようなものなANYので、物事を評価するよりもセマンティックですSUM

出力:

D   TITLE   BODY    TAGS
1   One     text1   PHP,SQL
2   Two     text2   SQL

ライブ テスト: http://www.sqlfiddle.com/#!2/52b3b/26


私はstackoverflowでとても多くを学んでいます。私の古い答えの後、SUM OVER partition. 次に、Postgresql のbool_or,関数について考えました。それから私はMySQLが持っていることを覚えています:-)bool_andeverybit_or

を使用した最後のソリューションは単なる後付けです。少なくとも 1 つが trueのセマンティックであるSUMと考えた場合、使用できることは明らかです。今ではすべてのデータベースで動作します:-)bit_orHAVING SUM(condition) >= 1

ウィンドウ関数で解決することはできませんでしたが、上記の解決策はすべてのデータベースで機能するようになりました:-)

于 2012-05-06T15:00:54.347 に答える
2

すべてのタグに個別の内部結合を設定します

SELECT p.Title, p.Body, t2.name
FROM Posts p
LEFT JOIN Post_tags pt ON p.id = pt.p_id
LEFT JOIN Tags t ON t.id = pt.t_id
INNER JOIN Post_tags pt2 ON p.id = pt2.p_id
INNER JOIN Tags t2 on ON t2.id = pt2.t_id
WHERE t.name LIKE '%SQL%'
于 2012-05-06T10:47:10.033 に答える
1

これを行うさらに別の方法は、posts_tagswith それ自体の内部結合を中心に構築されます。

SELECT *
FROM posts_tags pt1
JOIN posts_tags pt2
USING(p_id)
WHERE pt2.t_id = 1;

+------+------+------+
| p_id | t_id | t_id |
+------+------+------+
|    1 |    1 |    1 |
|    1 |    3 |    1 |
|    1 |    4 |    1 |
|    3 |    1 |    1 |
|    3 |    2 |    1 |
|    5 |    1 |    1 |
|    5 |    3 |    1 |
|    7 |    1 |    1 |
+------+------+------+
8 rows in set (0.00 sec)

この句がなければ、WHERE内部結合は、各投稿に関連付けられたすべてのタグの完全なデカルト積 (t_id 1, t_id 2) を提供します。節をデカルト積の半分に適用すると、WHERE探している「x を含むセットのすべてのメンバー」構造が得られます。(上記の例は、タグ ID 1 に関連付けられた投稿のみが取得されたことを示しています。さらに、それらの投稿に関連付けられたすべてのタグも存在します。) p_id と t_id に関連付けられた情報を取得するための、さらに 2 つの単純な結合です。

SELECT title,name
FROM posts_tags pt1
JOIN posts_tags pt2
  ON(pt1.p_id = pt2.p_id)
JOIN posts
  ON(pt1.p_id = posts.id)
JOIN tags
  ON (pt1.t_id = tags.id)
WHERE pt2.t_id = 1;

+---------+--------+
| title   | name   |
+---------+--------+
| first   | php    |
| first   | skiing |
| first   | tuna   |
| third   | php    |
| third   | sql    |
| fifth   | php    |
| fifth   | skiing |
| seventh | php    |
+---------+--------+
8 rows in set (0.01 sec)
于 2012-05-06T15:01:31.823 に答える
1

これを試して:

SELECT p.Title, p.Body, t.name,GROUP_CONCAT(t2.name) AS `tags`
FROM Posts p
LEFT JOIN Post_tags pt ON p.id = pt.p_id
LEFT JOIN Tags t ON t.id = pt.t_id
JOIN Tags t2 ON t2.id = p.id
WHERE t.name LIKE '%SQL%'

これは GROUP_CONCAT を使用して、その特定の投稿に関連付けられたタグのコンマ区切りリストを作成します。クエリの出力:

TITLE BODY   NAME   tags
One   text1  SQL    SQL,GLSL

SQL フィドル: http://sqlfiddle.com/#!2/2f698/9

于 2012-05-06T10:53:58.687 に答える