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

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

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

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

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Q&A

解決済

2回答

1255閲覧

SQLの検索によるFBの共通の友達特定機能

Chandler_Bing

総合スコア673

MySQL

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

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

1グッド

0クリップ

投稿2018/12/19 07:48

編集2018/12/20 10:33

SQL

1CREATE TABLE `follow` ( 2 id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 userid int(11) NOT NULL, 4 followingidid int(11) NOT NULL, 5 status int(11) NOT NULL 6);

SQL

1INSERT INTO `follow` (`userid`,`followingidid`, `status`) VALUES 2(1,2,1),(1,3,1),(1,7,1),(1,8,1),(4,3,1), 3(4,8,1),(4,7,1),(6,10,1),(7,10,2),(19,20,2), 4(8,10,1),(2,3,1)(15,1,1),(14,1,1),(13,1,1) 5(12,1,1),(11,1,2),(1,9,2),(3,20,2);

現在このテーブルで友達情報を管理しておリます。

★テーブルについて★
1は友達、2は申請中、3は申請却下
1行で2ユーザーの情報を管理している
userid1,followingidid4, status1という行があれば1が4に申請をし4が許可をし友達になったということを示す

現在この運用で特に問題なく機能はしております。

SQL

1SELECT * FROM user WHERE userid IN( 2 3SELECT userid AS maykonw FROM follow WHERE followingid IN (SELECT userid FROM follow WHERE followingid = 1 and status = 1 UNION ALL SELECT followingid FROM follow WHERE userid = 1 and status = 1) 4 AND userid NOT IN (SELECT userid FROM follow WHERE followingid = 1 and status IN (1,2,3) UNION ALL SELECT followingid FROM follow WHERE userid = 1 and status IN (1,2,3)) AND status =1 5UNION ALL 6 7SELECT followingid AS maykonw FROM follow WHERE userid IN (SELECT userid FROM follow WHERE followingid = 1 and status = 1 UNION ALL SELECT followingid FROM follow WHERE userid = 1 and status = 1) 8 AND followingid NOT IN (SELECT userid FROM follow WHERE followingid = 1 and status IN (1,2,3) UNION ALL SELECT followingid FROM follow WHERE userid = 1 and status IN (1,2,3)) AND status =1 9) 10 11AND userid NOT IN (1);

このSQL文はFBの友達かも機能です。
AB
BC
AC
BD

という友達関係が成立していた場合、AからみてCとDはB(友達)の友達にあたりますが表示させるのはDのみです。
すなわち友達の友達であるが自分は友達と友達に関係にないということです。

実装したい機能としましては、Dを表示させる際にDが自分と友達関係にある友達の中で誰の友達かを特定したいです。
表示イメージは

友達かも
D さん(共通の友達 B さん)

のような感じです。よろしくお願いします。

又、提示のSQL文についてはまだまだ未熟と思いますが、現在のところ問題なく機能しておりますのであまり深掘りしないで頂けると助かります。

【追記❶】

SQL

1select v1.friend 2from ( 3 SELECT case when userid=1 then followingid else userid end as friend 4 FROM follow 5 WHERE 1 in (userid, followingid) and status = 1 6 ) v1 inner join ( 7 SELECT case when userid=4 then followingid else userid end as friend 8 FROM follow 9 WHERE 4 in (userid, followingid) and status = 1 10 ) v2 11 on v1.friend=v2.friend

このSQLをPHPファイルの変数に代入するとSQLとして認識されません。
イメージ説明
解決方法をご回答ください。

bochan2👍を押しています

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

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

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

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

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

guest

回答2

0

ベストアンサー

繰り返しになりますが、今のままでは相当効率がわるいです。
1レコードでユーザーidを2個管理するとどうしても主従関係が発生します
検索の際はunionして処理するのでパフォーマンスもSQLの視認性も期待できません。

followテーブルからstatusを切り離し、triggerでデータ投入・削除時に
別テーブルで管理すると良いかもしれません

sample

私の方からの説明が不足しているので以下サンプルをあげておきます

  • テーブル作成

SQL

1create table follow( 2fid int primary key auto_increment, 3userid int not null, 4follow_userid int not null, 5statusid tinyint not null 6); 7create table user_relation ( 8rid int primary key auto_increment,/*リレーションテーブル用のid*/ 9fid int not null, /*followテーブルのid*/ 10uid int not null, /*ユーザーid*/ 11sid int not null, /*ステータスid*/ 12flg tinyint default 0 /*削除用フラグ*/ 13);
  • followテーブルにtriggerを仕込みます

SQL

1drop trigger if exists trg_bef_insert_follow; 2drop trigger if exists trg_aft_insert_follow; 3drop trigger if exists trg_delete_follow; 4delimiter // 5create trigger trg_bef_insert_follow before insert on follow 6for each row begin 7update user_relation as t1,user_relation as t2 8set t1.flg=1,t2.flg=1 where t1.fid=t2.fid and 9(t1.uid=new.userid and t2.uid=new.follow_userid or 10t2.uid=new.userid and t1.uid=new.follow_userid); 11end 12// 13create trigger trg_aft_insert_follow after insert on follow 14for each row begin 15insert user_relation(fid,uid,sid) values 16(new.fid,new.userid,new.statusid), 17(new.fid,new.follow_userid,new.statusid); 18delete from user_relation where flg>0; 19end 20// 21create trigger trg_delete_follow after delete on follow 22for each row begin 23delete from user_relation where fid=old.fid; 24end 25// 26delimiter ;
  • テストデータ

SQL

1insert into follow(userid,follow_userid,statusid) values 2(1,2,1),(1,3,1),(1,7,1),(1,8,1),(4,3,1), 3(4,8,1),(4,7,1),(6,10,1),(7,10,2),(19,20,2), 4(8,10,1),(2,3,1),(15,1,1),(14,1,1),(13,1,1), 5(12,1,1),(11,1,2),(1,9,2),(3,20,2);

※ここまでは命題のまま

test

  • 仮にfollowからデータを削除します

SQL

1delete from follow where fid=3;

このときuser_relationテーブルを確認するとfid=3のデータが削除されていることがわかります

  • userid,follow_useridをひっくり返して投入

テストとして(2,1,3)のデータを投入します
このデータはfid=1のデータのuserid,follow_useridがひっくり返っているものです

SQL

1insert into follow(userid,follow_userid,statusid) values(2,1,3);

user_relationを確認するとfid=1のデータが削除され、新たにケツにデータが2件登録されます

友達の友達

  • 上記testをした場合は1度データは元にもどしておいて下さい

SQL

1truncate follow; 2truncate user_relation; 3insert into follow(userid,follow_userid,statusid) values 4(1,2,1),(1,3,1),(1,7,1),(1,8,1),(4,3,1), 5(4,8,1),(4,7,1),(6,10,1),(7,10,2),(19,20,2), 6(8,10,1),(2,3,1),(15,1,1),(14,1,1),(13,1,1), 7(12,1,1),(11,1,2),(1,9,2),(3,20,2);
  • 友達の友達を表示します

SQL

1select * from ( 2select t1.uid as self,t2.uid as friend,t4.uid as friendoffriend from user_relation as t1 3inner join user_relation as t2 on t1.fid=t2.fid and not t1.rid=t2.rid and t1.sid=1 4inner join user_relation as t3 on t2.uid=t3.uid and not t2.fid=t3.fid and t3.sid=1 5inner join user_relation as t4 on t3.fid=t4.fid and not t3.uid=t4.uid 6) as s1 where not exists( 7select t5.uid as self,t6.uid as friend 8from user_relation as t5 9inner join user_relation as t6 on t5.fid=t6.fid and not t5.rid=t6.rid and t5.sid=1 10where s1.friendoffriend=t6.uid and s1.self=t5.uid 11) 12order by self,friend,friendoffriend;
  • (応用)selfに対してfriendoffirendをユニークに表示します

SQL

1select distinct self,friendoffriend from ( 2select t1.uid as self,t2.uid as friend,t4.uid as friendoffriend from user_relation as t1 3inner join user_relation as t2 on t1.fid=t2.fid and not t1.rid=t2.rid and t1.sid=1 4inner join user_relation as t3 on t2.uid=t3.uid and not t2.fid=t3.fid and t3.sid=1 5inner join user_relation as t4 on t3.fid=t4.fid and not t3.uid=t4.uid 6) as t7 where not exists( 7select t5.uid as self,t6.uid as friend 8from user_relation as t5 9inner join user_relation as t6 on t5.fid=t6.fid and not t5.rid=t6.rid and t5.sid=1 10where t7.friendoffriend=t6.uid and t7.self=t5.uid 11) 12order by self,friendoffriend
  • 結果
selffriendoffriend
14
110
24
27
28
212
213
214
215
37
38
312
313
314
315
41
42
410
68
72
73
78
712
713
714
715
82
83
86
87
812
813
814
815
101
104
122
123
127
128
1213
1214
1215
132
133
137
138
1312
1314
1315
142
143
147
148
1412
1413
1415
152
153
157
158
1512
1513
1514

参考

上記SQLはオプティマイザでは以下のように理解されているようです

SQL

1select distinct t1.uid AS self,t4.uid AS friendoffriend 2from user_relation as t1 3,user_relation as t2 4,user_relation as t3 5,user_relation as t4 6where 1 7and t1.sid = 1 8and t2.fid = t1.fid 9and t3.sid = 1 10and t3.uid = t2.uid 11and t4.fid = t3.fid 12and not t2.uid = t4.uid 13and not t1.fid = t3.fid 14and not t1.rid = t2.rid 15and not exists( 16 select 1 17 from user_relation as t5 18 ,user_relation as t6 19 where 1 20 and t5.sid = 1 21 and t6.fid = t5.fid 22 and t4.uid = t6.uid 23 and t1.uid = t5.uid 24 and not t5.rid = t6.rid 25 ) 26order by t1.uid,t4.uid

投稿2018/12/20 00:37

編集2018/12/20 04:06
yambejp

総合スコア114779

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

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

sazi

2018/12/20 01:16

n:nの関係に対して、マップを作るのは普通です。 今回はそれが自己同士の関係であるという事で、主従関係をどう持たせるかとは別な話だと思うのですが。
yambejp

2018/12/20 02:43

すべてuserとfollowuserの関係性が1レコードで管理されているのが問題だと思いますけどね その原因が主従関係をもたせることなので、1レコードを別途2つに分割して管理すれば 解決は速いと思います
sazi

2018/12/20 02:59

「1レコードでユーザーidを2個管理する=主従関係が発生する」 ではないという事を言いたいのです。 問題なのは主従関係の持たせ方によってコストが掛かるようになっているという事なので、 それを解決するアプローチについてのコメントではありません。
sazi

2018/12/20 03:14

実装については、MySQLでの実装経験が豊富なyambejpさんに従った方が確実だと思ってますです。
yambejp

2018/12/20 03:15

自分の文書をよく読み返してみると 「主従関係をもつから非効率的」というような表現になっていて 確かに意味合いがおかしいですね ようは1レコードをいかに効率よく分解するかが、今回の回答の肝です ちなみに今回はupdateされたときの処理は省略しています (やっぱ書いておいたほうがよいかな・・・)
Chandler_Bing

2018/12/20 08:58

ありがとうございます。 行おうとしていることはわかるのですが、 そもそもtriggerの役割はなんののでしょうか。 又 followテーブルにtriggerを仕込みます の直下のSQL文の実行はendで区切って行っても、全て書いても エラーになります。 #1064 - SQL構文エラーです。バージョンに対応するマニュアルを参照して正しい構文を確認してください。 : '' 付近 6 行目
yambejp

2018/12/20 09:13

> triggerの役割 followテーブルの構造がこのままでは煩雑になるのでuser_relationというテーブルに 1レコードを2つにわけています。 > エラーになります。 基本的にはすべてコピペで順番に投入していけば動くように載せたのですが MySQLのバージョンはいくつをご利用でしょうか? ちなみにMySQLへのインタフェースはphpMyAdminなど利用してませんか?
Chandler_Bing

2018/12/20 10:26

5.7.23 - MySQL Community Server がバージョンです
yambejp

2018/12/20 10:50

serverが5.7であればtriggerは普通に実装されているはずです。 create tableとかはどいうインタフェースで処理していますか? phpからプログラムで処理している? 繰り返しになりますがphpMyAdminなど利用してませんか?
Chandler_Bing

2018/12/21 07:48

とりあえず、このトリガー等については、初心者の私のレベルを超越しておりますので一度中級者の参考書等で一度勉強します。 ありがとうございました。
guest

0

SQLがどうこうと云うより、友達たちの共通の友達かもしれないので、一人に特定はでき無い場合があります。

追記

元のSQLから組み立てるのではなく、元から考えるようにしないと、SQLが肥大化してしまいますよ。
[共通の友人]

SQL

1select v1.friend 2from ( 3 SELECT case when userid=1 then followingid else userid end as friend 4 FROM follow 5 WHERE 1 in (userid, followingid) and status = 1 6 ) v1 inner join ( 7 SELECT case when userid=4 then followingid else userid end as friend 8 FROM follow 9 WHERE 4 in (userid, followingid) and status = 1 10 ) v2 11 on v1.friend=v2.friend

投稿2018/12/19 08:08

編集2018/12/19 14:46
sazi

総合スコア25173

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

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

Chandler_Bing

2018/12/19 08:29

ありがとうございます。 複数いる場合は、複数表示したいと考えております。 未だにハッキリは見えませんが、 友達かもの検索で userid を 取得してそれをPHPの変数に一旦格納し、それを使って再び SQL検索をするという方法ではどうでしょうか。
sazi

2018/12/19 08:41

「友達かも」で求めているSQLでのollow のもう一方の項目の値が質問にある求めたいものです。
sazi

2018/12/19 08:51 編集

1ユーザーに対する「友達かも」に対してなら検索回数は多くならないでしょうから、言われるように再度検索の方がシンプルです。
Chandler_Bing

2018/12/19 08:59

ありがとうございます。 では一度サイド検索で模索し、 分からない場合はまた質問させて頂きます。本当にありがとうございます。
sazi

2018/12/19 09:04

割とコストが高いSQLだと思うので、大量データによる性能はチェックしておいた方が良いですよ。 性能出せない場合には、テーブル設計やり直す場合だってありますから。
Chandler_Bing

2018/12/19 11:24

ありがとうございます。必ずチェック致します。 user1の場合友達かもとして表示するユーザーは user4とuser10です。どちらでも問題ないですがuser4に焦点を当てます。 SELECT userid FROM follow WHERE followingid = 1 and status = 1 UNION ALL SELECT followingid FROM follow WHERE userid = 1 and status = 1 これで1の友達(2,3,7,8,12,13,14,15)が表示され、 SELECT userid FROM follow WHERE followingid = 4 and status = 1 UNION ALL SELECT followingid FROM follow WHERE userid = 4 and status = 1 これで4の友達(3,7,8)が表示されます。 この二つの共通する数字(ここでは3,7,8)を使用し、userテーブルにIN句を掛けuserテーブルから ユーザーが欲しいです。 この二つの共通する数字(ここでは3,7,8)を使用し、userテーブルにIN句を掛けuserテーブルから ユーザーが欲しいです。 『この二つの共通する数字(ここでは3,7,8)を使用し、userテーブルにIN句を掛けuserテーブルから ユーザーが欲しいです。』の実現方法をご教授お願いします。
Chandler_Bing

2018/12/19 14:04

お疲れ様です。 直近の質問に対する、SQLを思いついたのですが エラーが出てしまいます。 簡単に解決できそうな問題のような気がしますので、新たに質問を立てますが 引き続きこの質問に対するご教授お願いいたします。
Chandler_Bing

2018/12/20 10:29

select v1.friend from ( SELECT case when userid=1 then followingid else userid end as friend FROM follow WHERE 1 in (userid, followingid) and status = 1 ) v1 inner join ( SELECT case when userid=4 then followingid else userid end as friend FROM follow WHERE 4 in (userid, followingid) and status = 1 ) v2 on v1.friend=v2.friend の実行は問題なく作動す流のですが、 phpファイルの中で、SQL文として読み込まれていません。 追記に書き込みいたします。画像の方が今回は判断しやすいと思いますので画像で書き込みいたします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問