質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Q&A

解決済

2回答

859閲覧

WHEREで絞り込みつつ、他のカラムを取得したい

nikuatsu

総合スコア177

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

0グッド

0クリップ

投稿2022/03/28 09:01

編集2022/04/28 15:57

前提

・MySQLで「タグを持つ投稿」を取得しています。
・テーブル構造は変えずにお願い致します。

実現したいこと

「指導」というタグを指定しつつ、その投稿が持つ他の「鈴木」「佐藤」というタグも取得したいです。次の結果を目指しています。

comment_idcommentgenre_namesauthor_name
1眠らないでください指導鈴木,佐藤
3席についてください指導(null)

発生している問題

「鈴木」「佐藤」というタグが取得されず、次のようにnullとなってしまいます。

comment_idcommentgenre_namesauthor_name
1眠らないでください指導(null)
3席についてください指導(null)

該当のソースコード

テーブル構造です。これは変えたくありません。

SQL

1CREATE TABLE my_comments 2(`ID` int, `comment` varchar(10)); 3INSERT INTO my_comments 4(`ID`, `comment`) 5VALUES 6(1, '眠らないでください'), 7(2, '静かにしてください'), 8(3, '席についてください'), 9(4, 'たまに休んで下さい'); 10 11CREATE TABLE my_tags 12(`ID` int, `tag_kind_id` int, `tag_name` varchar(100)); 13INSERT INTO my_tags 14(`ID`, `tag_kind_id`, `tag_name`) # tag_kind_id=1 なら genre で、tag_kind_id=2 なら author などなど 15VALUES 16(1, 2, '鈴木'), 17(2, 2, '佐藤'), 18(3, 1, '指導'); 19 20CREATE TABLE my_tag_holders 21(`comments_ID` int, `tags_ID` int); 22INSERT INTO my_tag_holders 23(`comments_ID`, `tags_ID`) 24VALUES 25(1, 1),(1, 2),(1, 3), 26(2, 4), 27(3, 3);

問題の生じるSELECT文です。これを改善したいです。
(右記でお試しいただけます → http://sqlfiddle.com/#!9/b95e945/1

SQL

1SELECT comments.ID AS comment_id 2 ,comments.comment 3 4 # ジャンル 5 ,GROUP_CONCAT( 6 CASE WHEN tags.tag_kind_id=1 THEN tags.tag_name 7 ELSE null END 8 ) AS genre_names 9 10 # 人 11 ,GROUP_CONCAT( 12 CASE WHEN tags.tag_kind_id=2 THEN tags.tag_name 13 ELSE null END 14 ) AS author_name 15 16FROM my_comments comments 17 LEFT JOIN my_tag_holders th 18 ON th.comments_ID = comments.ID 19 LEFT JOIN my_tags tags 20 ON tags.ID = th.tags_ID 21 22-- タグ名で指定 23WHERE tags.tag_name = '指導' 24GROUP BY comments.ID 25LIMIT 0, 5

試したこと

IDで指定してみると取得できました。

SQL

1-- IDで指定 2WHERE comments.ID IN ( 1, 3 ) 3GROUP BY comments.ID 4LIMIT 0, 5

そのためtag_nameをWHEREに指定する部分が原因だとわかります。

そこで、「もしかして絞り込まれてしまった後に他のタグは取得できないのか?」と考え、絞り込みのためのJOINを追加して次のSELECTを実行したところ、なんとか目的の結果を得ることができました。

SQL

1FROM my_comments comments 2 LEFT JOIN my_tag_holders th 3 ON th.comments_ID = comments.ID 4 LEFT JOIN my_tags tags 5 ON tags.ID = th.tags_ID 6 7 -- 絞り込みのためのJOINを追加 8 LEFT JOIN my_tag_holders th2 9 ON th2.comments_ID = comments.ID 10 LEFT JOIN my_tags tags2 11 ON tags2.ID = th2.tags_ID 12 13-- タグ名で指定 14WHERE tags2.tag_name = '指導' 15GROUP BY comments.ID 16LIMIT 0, 5

しかし、「このようなJOINは二度手間ではないか?」と疑念を持っています。
より正しい方法があるのでは…と質問させて頂きました。

補足情報(FW/ツールのバージョンなど)

MySQLのバージョンはやや古めな5.7を使用しております。
よろしくお願い致します。

その後

INNER JOIN で検索結果を絞る、以下の書き方が最速でした

SQL

1FROM my_comments comments 2 3 INNER JOIN ( 4 5 SELECT comments2.ID 6 FROM my_comments comments2 7 LEFT JOIN my_tag_holders th2 8 ON th2.comments_ID = comments2.ID 9 LEFT JOIN my_tags tags2 10 ON tags2.ID = th2.tags_ID 11 12 -- タグ名で指定 13 WHERE tags2.tag_name = '指導' 14 GROUP BY comments2.count_likes, comments2.ID 15 ORDER BY comments2.count_likes DESC, comments2.ID DESC 16 LIMIT 0, 20 17 18 ) AS searched ON searched.ID = comments.ID 19 20 LEFT JOIN my_tag_holders th 21 ON th.comments_ID = comments.ID 22 LEFT JOIN my_tags tags 23 ON tags.ID = th.tags_ID 24 25GROUP BY searched.ID 26ORDER BY comments.count_likes DESC, comments.ID DESC 27LIMIT 0, 20

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yambejp

2022/03/28 10:48 編集

テーブル構造がおかしくロジックが破綻していませんか? 「席についてください」と「指導」はどういったリレーションなのでしょうか?
nikuatsu

2022/03/28 12:06

「席についてください」は、my_tag_holders テーブルの (3, 3) レコードによって、「指導」というタグと関連付けられています。 すみません。伝わりますでしょうか… 別の言い方をすると、 「席についてください」が「my_comments.ID=3」で、「指導」が「my_tags.ID=3」なので、my_tag_holders テーブルの (3, 3) レコードでそれらが関連づけられています。
guest

回答2

0

一応こんな感じで

SQL

1select t1.ID, 2max(t1.comment) as comment, 3group_concat(t3.tag_name) as genre_names, 4group_concat(t4.tag_name) as author_name 5 from my_comments as t1 6left join my_tag_holders as t2 on t1.ID=t2.comments_id 7left join my_tags as t3 on t2.tags_id=t3.ID and t3.tag_kind_id=1 8left join my_tags as t4 on t2.tags_id=t4.ID and t4.tag_kind_id=2 9group by ID 10having genre_names is not null or author_name is not null

my_tag_holders調整

SQL

1create table my_tag_holders2 2(pid int primary key,cid int not null,gid int,aid int null,index(cid,gid,aid)); 3 4insert into my_tag_holders2 values 5(1,1,3,1), 6(2,1,3,2), 7(3,3,3,null); 8 9select t1.ID, 10max(comment) as comment, 11t3.tag_name as genre_name, 12group_concat(t4.tag_name) as author_name 13from my_comments as t1 14inner join my_tag_holders2 as t2 on t1.ID=t2.cid 15inner join my_tags as t3 on t2.gid=t3.ID 16left join my_tags as t4 on t2.aid=t4.ID 17group by ID,genre_name; 18

投稿2022/03/29 00:21

編集2022/03/29 05:39
yambejp

総合スコア114968

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yambejp

2022/03/29 04:40

ちょっとサンプルデータが少ないので例外処理とかどこまで必要なのかわからないですね
nikuatsu

2022/03/29 04:55 編集

ありがとうございます。サンプルが少なくて申し訳ございません。 ”「タグを持つ投稿」を取得” と書いたのは、「指導」のタグを持つコメントや、「鈴木」のタグを持つコメント、のように、あくまでタグ指定での取得を目指しているという意味でして・・・
yambejp

2022/03/29 05:00

my_tag_holdersがちょっと不自由なデータの持ち方ですよね たとえば鈴木=指導の組み合わせがあっても佐藤=指導の組み合わせがNGとか表現できないです ちゃんとやるならmy_tag_holdersをcomment_id,genre_id,author_idの組み合わせとスべきです
nikuatsu

2022/03/29 05:12 編集

やはりそのように別カラムに分けるべきでしたか…ご助言ありがとうございます。別カラムに分けるとnullや0が入ってしまうので避けた方がいいかと思い現状の構造にしたのですが… (例えばあるタグがgenreであるときに、genre_idのカラムだけが埋まり、author_idのカラムがnullや0になってしまうという問題です。そしてtag_kind_idは実際は6種類ありますので、5つのカラムはnullや0になってしまうのです。) …ですが、nullや0が入ることよりも、ご指摘のような不自由さが重大な問題だということですね。ありがとうございます。テーブル構造の見直しも検討してみます。
nikuatsu

2022/03/29 05:22

思ったのですが、その場合インデクスはどうされますか? 質問の場合ですと tags_ID,comments_ID だけで済みますが、 その場合ですと genre_id,comments_ID author_id,comments_ID のように、tag_kind_idの数だけベタベタ貼りまくることにならざるをえないでしょうか?
yambejp

2022/03/29 05:41

とりあえず追記しておきました genreとauthorの組み合わせが必要かどうかもわからないのでご提示のソースが ベストかどうかもよくわかりません
nikuatsu

2022/03/29 06:03

「指導」のタグを持つコメントや、「鈴木」のタグを持つコメント、のように、あくまでタグ指定での取得を目指しているのですが、追記にはそれらの指定がなく…ご意図がよく掴みきれませんでした。すみません。 ひとまず当初にご回答いただきました having など参考にしながら引き続き解決に取り組んで参ります。
yambejp

2022/03/29 06:07

質問者さんがわからない以上に回答者は状況がわからないので最大限想像して書いています。 きちんと仕様を提示して、どういうデータからどういうロジックでどういう結果をほしいか サンプルをつけて質問してください
nikuatsu

2022/03/30 03:47

たしかにそうですよね…。分かりにくい質問で申し訳ございませんでした。
guest

0

ベストアンサー

HAVINGで目的の結果になると思うけど、よいテーブル定義かと
言われれば決してそうではないと思う。

SQL

1-- WHERE tags.tag_name = '指導' 2GROUP BY comments.ID 3HAVING genre_names='指導' 4LIMIT 0, 5

追記

genre_namesかどうか不定ならexists内のtag_nameで対処
2回連結するっていうのはその通り。

SQL

1where exists( 2 select 0 3 from my_tag_holders h 4 inner join my_tags t on t.ID=h.tags_ID 5 where h.comments_ID=comments.ID 6 and t.tag_name='指導' 7) 8GROUP BY comments.ID 9LIMIT 0, 5

投稿2022/03/29 00:18

編集2022/03/29 06:34
sousuke

総合スコア3828

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nikuatsu

2022/03/29 04:53

ありがとうございます。せっかくですが、genreかどうかは不定でして、あくまで「タグ名の指定」でのSELECTを考えています。「指導」というgenreでなく、「鈴木」というauthorの指定でのSELECTかもしれないということです。
nikuatsu

2022/03/29 05:01

「指導」は検索ボックスへの入力値とお考え頂ければと思います。入力時点でそれがgenreかauthorかは不定で、もしかしたら指導さんというauthorもいるかもしれません。
nikuatsu

2022/03/30 03:48

追記、拝見しました。ありがとうございます。「exists」句というのは知らなかったです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問