###実現したいこと
以下3つのテーブルがあります
・記事テーブル「posts」
・タグテーブル「tags」
・タグリレーションテーブル「tag_relations」
以下2つの条件で記事を取得したいです
【条件1】指定タグを持っている
【条件2】タグ単位で2件ずつ
###テーブル
sql
1-- 記事 2CREATE TABLE posts 3 (`ID` int, `user_ID` int, `contents` varchar(100)) 4; 5INSERT INTO posts 6 (`ID`, `user_ID`, `contents`) 7VALUES 8 (1, 10, '#タグ1 と #タグ2 を持っている記事1の本文です'), 9 (2, 20, '#タグ1 を持っている記事2の本文です'), 10 (3, 30, '記事3の本文です'), 11 (4, 10, '#タグ1 と #タグ3 と #タグ4 を持っている記事4の本文です'), 12 (5, 30, '#タグ5 を持っている記事5の本文です'), 13 (6, 40, '#タグ2 と #タグ3 と #タグ7 を持っている記事6の本文です'), 14 (7, 10, '#タグ2 と #タグ4 と #タグ8 を持っている記事7の本文です'), 15 (8, 50, '#タグ1 を持っている記事8の本文です'), 16 (9, 10, '#タグ2 と #タグ4 を持っている記事9の本文です'), 17 (10, 20, '#タグ1 と #タグ2 と #タグ6 を持っている記事10の本文です') 18; 19 20-- タグ 21CREATE TABLE tags 22 (`ID` int, `tag_name` varchar(100), `rank_weekly` int) 23; 24INSERT INTO tags 25 (`ID`, `tag_name`, `rank_weekly`) 26VALUES 27 (1, 'タグ1', 1), 28 (2, 'タグ2', 2), 29 (3, 'タグ3', 3), 30 (4, 'タグ4', 4), 31 (5, 'タグ5', 5), 32 (6, 'タグ6', 6), 33 (7, 'タグ7', 7), 34 (8, 'タグ8', 8) 35; 36 37-- タグリレーション 38CREATE TABLE tag_relations 39 (`tags_ID` int, `contents_ID` int ) 40; 41INSERT INTO tag_relations 42 (`tags_ID`, `contents_ID` ) 43VALUES 44 # contents_ID=1が、tag_ID=1とtag_ID=2を持っている 45 (1, 1), (2, 1), 46 # contents_ID=2が、tag_ID=1を持っている 47 (1, 2), 48 # contents_ID=4が、tag_ID=1とtag_ID=3とtag_ID=4を持っている 49 (1, 4), (3, 4), (4, 4), 50 # contents_ID=5が、tag_ID=5を持っている 51 (5, 5), 52 # contents_ID=6が、tag_ID=2とtag_ID=3とtag_ID=7を持っている 53 (2, 6), (3, 6), (7, 6), 54 # contents_ID=7が、tag_ID=2とtag_ID=4とtag_ID=8を持っている 55 (2, 7), (4, 7), (8, 7), 56 # contents_ID=8が、tag_ID=1を持っている 57 (1, 8), 58 # contents_ID=9が、tag_ID=2とtag_ID=4を持っている 59 (2, 9), (4, 9), 60 # contents_ID=10が、tag_ID=1とtag_ID=2とtag_ID=6を持っている 61 (1, 10), (2, 10), (6, 10) 62;
現状のソースコード
まずは【条件1:指定タグを持っている】だけを採用した場合を考えまして、こちらになります
sql
1select p.ID 2, p.contents 3, group_concat(t.tag_name separator ' ') as tag_names 4from posts p 5left join tag_relations tr ON tr.contents_ID = p.ID 6left join tags t ON t.ID = tr.tags_ID 7where t.tag_name IN ( 'タグ1', 'タグ2', 'タグ4', 'タグ7' ) # タグ名を指定 8AND p.ID NOT IN ( 1 ) # 記事IDを除外 9group by p.ID, p.contents
↓結果はこうなります
ID | contents | tag_name |
---|---|---|
2 | #タグ1 を持っている記事2の本文です | タグ1 |
4 | #タグ1 と #タグ3 と #タグ4 を持っている記事4の本文です | タグ1 タグ4 |
6 | #タグ2 と #タグ3 と #タグ7 を持っている記事6の本文です | タグ2 タグ7 |
7 | #タグ2 と #タグ4 と #タグ8 を持っている記事7の本文です | タグ4 タグ2 |
8 | #タグ1 を持っている記事8の本文です | タグ1 |
9 | #タグ2 と #タグ4 を持っている記事9の本文です | タグ2 タグ4 |
10 | #タグ1 と #タグ2 と #タグ6 を持っている記事10の本文です | タグ1 タグ2 |
###求める結果
あとは上記に【条件2:タグ単位で2件ずつ】という条件を加味して、次の結果を得たいという状況です
※上記でID=6はタグ2とタグ7を持っていますが、ここでタグ2を優先するとタグ7の記事がなくなってしまうので、ID=6はタグ7の記事として取得される。という流れで下記の結果を得たいです
ID | contents | tag_name |
---|---|---|
2 | #タグ1 を持っている記事2の本文です | タグ1 |
4 | #タグ1 と #タグ3 と #タグ4 を持っている記事4の本文です | タグ4 |
6 | #タグ2 と #タグ3 と #タグ7 を持っている記事6の本文です | タグ7 |
7 | #タグ2 と #タグ4 と #タグ8 を持っている記事7の本文です | タグ2 |
8 | #タグ1 を持っている記事8の本文です | タグ1 |
9 | #タグ2 と #タグ4 を持っている記事9の本文です | タグ4 |
10 | #タグ1 と #タグ2 と #タグ6 を持っている記事10の本文です | タグ2 |
###試したこと
しかしこの結果の取得が大変難しく思います
例えば最後に
sql
1limit 2
と指定すれば、ID=2,4しか取得できませんし、
また例えば
sql
1having count(p.ID) <= 2
というような指定をしても、結果は条件1の場合と変わらずでした
あとは、全体をwrapと囲んだサブクエリを作り、それに対してgroup byをかけて以下のようにしてみたのですが、やはりこちらも求める結果になりません
sql
1select * 2from ( 3 select p.ID 4 , p.contents 5 , group_concat(t.tag_name separator ' ') as tag_names 6 from posts p 7 left join tag_relations tr ON tr.contents_ID = p.ID 8 left join tags t ON t.ID = tr.tags_ID 9 where t.tag_name IN ( 'タグ1', 'タグ2', 'タグ4', 'タグ7' ) # タグ名を指定 10 AND p.ID NOT IN ( 1 ) # 記事IDを除外 11 group by p.ID, t.tag_name 12) AS wrap 13group by wrap.ID
###バージョン情報
ちなみに使用しているのは MariaDB の v10.0.33 で、これはMySQLでいうと v5.5 に相当するものになります
あなたの回答
tips
プレビュー