🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
MySQL

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

JOIN

これはSQL文のJOINに関するタグです。リレーショナルデータベースシステムの二つ以上のテーブルを結合する際に、この構文が利用されます。

Q&A

解決済

2回答

2284閲覧

データ抽出後にテーブルを結合、さらに絞り込みをするSQL

Dammyyy

総合スコア7

MySQL

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

JOIN

これはSQL文のJOINに関するタグです。リレーショナルデータベースシステムの二つ以上のテーブルを結合する際に、この構文が利用されます。

0グッド

0クリップ

投稿2020/12/08 03:35

編集2020/12/08 06:45

前提条件

以下のような「articlesテーブル」、「roomsテーブル」、「facilities_traitテーブル」があるとします。

articles.table

idflagtitle
1012××アパート
1021〇〇マンション
1032△マンション
:::

rooms.table

idflagstatusarticles_id
122101
212101
322102
412103
521103
621103
::::

facilities_trait.table

idflagtypetrait_idfacilities_id
12110112
22110130
322131
42110212
52110230
62110312
722328
82110330
922331
102110430
1122431
122310525
132110530
1422531
1522627
162110630

facilities_traitテーブルのtrait_idにはarticles.idrooms.idが混在していて、typeでarticlesかroomsか判別しています。

実装したいこと

1.
まず、facilities_traitテーブルのfacilities_id = 31 を持つ trait_id を全て取り出します。

facilities_trait.table

idflagtypetrait_idfacilities_id
322131
922331
1122431
1422531

2.
type = 2なのでroomsテーブルを見て、facilities.trait_id = rooms.idで紐づくものを取り出します。

rooms.table

idflagstatusarticles_id
122101
322102
412103
521103

その際rooms.flag = 2,rooms.status = 2以外のものは除外します。

rooms.table

idflagstatusarticles_id
122101
322102

3.
articlesテーブルでrooms.articles_idに紐づくものを取り出します。

articles.table

idflagtitle
1012××アパート
1021〇〇マンション

その際articles.flag = 2以外のものは除外します。

articles.table

idflagtitle
1012××アパート

4.
articles.idをもって改めてfacilitiesテーブルに戻り、articles.id = facilities_trait.trait_idを取り出します。

facilities_trait.table

idflagtypetrait_idfacilities_id
12110112
22110130

ここで、facilities_traitテーブルのflag = 2 type = 1 OR type = 3の条件に合致するものを抽出したいです。

最終的に欲しい結果

facilities_trait.table

idflagtypetrait_idfacilities_id
12110112
22110130

現状のコード

分かりやすさを優先させましたが、facilities_trait.trait_idに入るarticlesとroomsのidは被る可能性があります(その為のtypeでもあります)。

SELECT * FROM facilities_trait WHERE flag = 2 AND (type = 1 OR type = 3) AND EXISTS( SELECT 'X' FROM articles WHERE flag = 2 AND facilities_trait.trait_id = articles.id AND EXISTS( SELECT 'X' FROM rooms WHERE flag = 2 AND status = 2 AND rooms.articles_id = articles.id AND EXISTS( SELECT 'X' FROM facilities_trait WHERE rooms.id = facilities_trait.trait_id AND facilities_trait.facilities_id = 31)))

ひとまずは上記のコードで目的の結果は得られておりました。
ただ、クエリがすごく重たいのでINNER JOIN等で書き換えたいと思っております。

皆様の知恵をお借りしたいです。
宜しくお願い致します。

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

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

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

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

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

K_Y_M

2020/12/08 06:36

「実装したいこと」の 2. でtypeが2の場合のみ記載されておりますが、 typeが2以外を取った場合は、どのテーブルのデータと組み合わせるのでしょうか?
Dammyyy

2020/12/08 06:43

すこしややこしいのですが、1,3の場合はarticles、2,4の場合にroomsになります。
sazi

2020/12/08 06:45 編集

> 2.type = 2なのでroomsテーブルを見て、facilities.trait_id = rooms.idで紐づくものを取り出します。 type = 2だけを対象にするという事でOK? そうじゃないなら、type =2以外の場合の条件を追記して下さい。
Dammyyy

2020/12/08 06:55

type = 2だけを対象とするという認識で大丈夫です。 facilities_id = 31 を持つのはtype = 2だけになります。
guest

回答2

0

ベストアンサー

質問のSQLに間違いがないなら相関がおかしい事になっているので、そのせいで遅いのではないでしょうか。

条件をみると、existsによる相関である必要も無いようですので、素直に記述すると以下の様になると思います。

SQL

1select * from facilities_trait 2where flag = 2 and type in (1, 3) and facilities_id = 31 3 and rooms_id in ( 4 select id from rooms 5 where flag = 2 and status = 2 6 and articles_id in ( 7 select id from articles where flag = 2 8 ) 9 )

それからそれぞれのテーブルに以下のインデックスがあれば、効率が良くなると思います。
facilities_trait=(flag, type, facilities_id, rooms_id)
rooms=(flag, status, articles_id, id)
articles=(flag, id)
※項目の並びは、カーディナリティに合わせて適宜変更が必要です。

追記

※SQLが変更されたので、内容に合わせて追記
素直に記述。

SQL

1SELECT * FROM facilities_trait 2WHERE flag = 2 AND type in (1, 3) 3 AND trait_id in ( 4 SELECT id FROM articles 5 WHERE flag = 2 6 AND id in ( 7 SELECT articles_id FROM rooms 8 WHERE flag = 2 AND status = 2 9 AND id in ( 10 SELECT trait_id FROM facilities_trait 11 WHERE facilities_id = 31 12 ) 13 ) 14 )

条件内をJoinで。

SQL

1SELECT * FROM facilities_trait 2WHERE flag = 2 AND type in (1, 3) 3 AND trait_id in ( 4 select articles.id 5 FROM articles 6 inner join rooms 7 on articles.id = rooms.articles_id 8 and articles.flag = 2 and room.flag = 2 AND room.status = 2 9 inner join facilities_trait 10 on rooms.id=facilities_trait.trait_id 11 and facilities_trait.facilities_id = 31 12 )

投稿2020/12/08 04:52

編集2020/12/08 07:17
sazi

総合スコア25327

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

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

Dammyyy

2020/12/08 05:46

ご回答ありがとうございます。 確認したところ、確かに質問中のSQLは間違っていました。 さらに言えば仕様を理解しきれていなかったようで、さらに複雑なことになりそうです。 詳細を質問の方に追記いたします。 テーブルのインデックスについては失念しておりました。慌てて設定しなおしました。
Dammyyy

2020/12/08 06:30

失礼いたしました。上記のSQLは合っておりました。混乱させてしまい申し訳ありません。
sazi

2020/12/08 06:37 編集

本当ですか? EXISTS()中にある「r,f」の相関参照先はどこを指していますか。
Dammyyy

2020/12/08 06:49

コピペするSQLを間違っていました。。正しくは別名は使っていませんでした。 `SELECT * FROM facilities_trait WHERE flag = 2 AND (type = 1 OR type = 3) AND EXISTS( SELECT 'X' FROM articles WHERE flag = 2 AND facilities_trait.trait_id = articles.id AND EXISTS( SELECT 'X' FROM rooms WHERE flag = 2 AND status = 2 AND rooms.articles_id = articles.id AND EXISTS( SELECT 'X' FROM facilities_trait WHERE rooms.id = facilities_trait.trait_id AND facilities_trait.facilities_id = 31)))`
Dammyyy

2020/12/08 07:34

求めていた結果が得られました。ありがとうございました。 ベストアンサーにさせて頂きます。
sazi

2020/12/08 07:43

相関が効率的な場合というのは、相関元が条件等で結果的に件数が少ない場合です。 そうでない場合は、inなどの条件にする方が効率的です。
sazi

2020/12/08 07:44

因みに追記したパターンは、どちらでも結果はあまり変わらないと推測していましたが、どうだったでしょうか。
Dammyyy

2020/12/08 07:55

仰る通り、どちらのパターンも速度はほとんど同じでした。 (元のEXISTS文とは比べるべくもなくどちらも高速でした。) INや相関について、今一度勉強しなおそうと思います。
sazi

2020/12/08 08:06 編集

質問のSQLでは相関なので、facilities_traitを読み込んだ件数分条件のexistsが実行されるのに対して、回答した内容は条件のサブクエリーが一度だけ実行されて、facilities_traitを絞り込むのです。 性能の差は条件のトータル処理時間に起因しています。
guest

0

以下のSQL文を試してみて下さい

SELECT f.* FROM facilities_trait as f INNER JOIN facilities_trait as f2 ON f2.rooms_id = f.rooms_id AND f2.facilities_id = 31 INNER JOIN articles AS a ON a.id = f.rooms_id AND a.flag = 2 INNER JOIN rooms AS r ON r.id = f.rooms_id AND r.flag = 2 AND r.status = 2 WHERE f.flag = 2 AND (f.type = 1 OR f.type = 3)

投稿2020/12/08 04:04

編集2020/12/08 05:46
K_Y_M

総合スコア100

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

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

sazi

2020/12/08 04:55

条件が変わっていますし、facilities_trait の件数と一致する考慮もなされていないですね。
K_Y_M

2020/12/08 05:40

条件を最後に追加し忘れていた為、追加しました。 >facilities_trait の件数と一致する考慮もなされていない これは質問者の方のどの部分を指されているのでしょうか?
sazi

2020/12/08 05:52

サンプルデータは、1:1のようですけど、一意キーが明確に定義されていない状態なので。 明確でない状態では、抽出条件内でjoinを使用しないと、結果が相違する可能性があるからです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問