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

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

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

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

Q&A

解決済

3回答

3860閲覧

より良いSQLの書き方

jk233

総合スコア55

MySQL

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

2グッド

4クリップ

投稿2016/06/13 02:42

編集2016/06/13 02:52

次の2つのテーブルがあるとします。
(場所マスタ)
場所ID 場所名
0 野菜室
1 冷蔵
2 冷凍

(データ)
データID 場所ID 食品名 数量 削除フラグ
0 0 とまと 3 1
1 1 プリン 2 1
2 1 豚肉 4 0
3 1 牛乳 1 0

次の結果を得たいです。
データID 場所名 食品名 数量
0 野菜室 NULL NULL
1 冷蔵 豚肉 4
1 冷蔵 牛乳 1
2 冷凍 NULL NULL

以下のSQLを考えましたが、より良い方法があったら教えて下さい。
JK_BASHODATAにフルスキャンがかかってしまうので性能的によくないのかなと思いました。

SQL

1SELECT bashoId, bashoName, D.itemName, D.suryo 2FROM JK_BASHOMAS 3LEFT JOIN (SELECT * FROM JK_BASHODATA WHERE sakujoFlg=0) D USING(bashoId)

【参考】

SQL

1CREATE TABLE JK_BASHOMAS ( 2 bashoId int not null primary key, 3 bashoName varchar(255) not null 4); 5CREATE TABLE JK_BASHODATA ( 6 dataId int not null primary key, 7 bashoId int not null, 8 itemName varchar(255) not null, 9 suryo int not null, 10 sakujoFlg boolean not null 11); 12INSERT INTO JK_BASHOMAS (bashoId, bashoName) VALUES (0, '野菜室'); 13INSERT INTO JK_BASHOMAS (bashoId, bashoName) VALUES (1, '冷蔵'); 14INSERT INTO JK_BASHOMAS (bashoId, bashoName) VALUES (2, '冷凍'); 15INSERT INTO JK_BASHODATA (dataId, bashoId, itemName, suryo, sakujoFlg) VALUES (0, 0, 'とまと', 3, 1); 16INSERT INTO JK_BASHODATA (dataId, bashoId, itemName, suryo, sakujoFlg) VALUES (1,1,'プリン',2,1); 17INSERT INTO JK_BASHODATA (dataId, bashoId, itemName, suryo, sakujoFlg) VALUES (2,1,'豚肉',4,0); 18INSERT INTO JK_BASHODATA (dataId, bashoId, itemName, suryo, sakujoFlg) VALUES (3,1,'牛乳',1,0);
stereo_code, KiyoshiMotoki👍を押しています

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

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

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

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

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

guest

回答3

0

ベストアンサー

Before

sql

1SELECT bashoId, bashoName, D.itemName, D.suryo 2FROM JK_BASHOMAS 3LEFT JOIN (SELECT * FROM JK_BASHODATA WHERE sakujoFlg=0) D USING(bashoId);
実行結果
bashoId bashoName itemName suryo 0 野菜室 (null) (null) 1 冷蔵 牛乳 1 1 冷蔵 豚肉 4 2 冷凍 (null) (null)
EXPLAIN
id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY JK_BASHOMAS ALL (null) (null) (null) (null) 3 (null) 1 PRIMARY <derived2> ref <auto_key0> <auto_key0> 4 db_9_a7aa9.JK_BASHOMAS.bashoId 2 (null) 2 DERIVED JK_BASHODATA ALL (null) (null) (null) (null) 4 Using where

After

sql

1SELECT B.bashoId, B.bashoName, D.itemName, D.suryo 2FROM JK_BASHOMAS AS B 3LEFT JOIN JK_BASHODATA AS D ON B.bashoId = D.bashoId AND D.sakujoFlg = 0 4ORDER BY B.bashoId;
実行結果
bashoId bashoName itemName suryo 0 野菜室 (null) (null) 1 冷蔵 豚肉 4 1 冷蔵 牛乳 1 2 冷凍 (null) (null)
EXPLAIN
id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE B ALL (null) (null) (null) (null) 3 Using temporary; Using filesort 1 SIMPLE D ALL (null) (null) (null) (null) 4 Using where; Using join buffer (Block Nested Loop)

これでサブクエリを実行しなくて済むようになります。
あとはKosuke_Shibuya様の回答にあるように適切にインデックスを付けてやれば、十分な性能を得られると思います。

EXPLAIN(インデックス追加後)

sql

1CREATE TABLE JK_BASHODATA ( 2 dataId int not null primary key, 3 bashoId int not null, 4 itemName varchar(255) not null, 5 suryo int not null, 6 sakujoFlg boolean not null, 7 8 index (bashoId), 9 index (sakujoFlg) 10);
id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE B index (null) PRIMARY 4 (null) 3 (null) 1 SIMPLE D ref bashoId,sakujoFlg bashoId 4 db_9_9c8de.B.bashoId 1 Using where

投稿2016/06/13 03:21

編集2016/06/13 03:27
KiyoshiMotoki

総合スコア4791

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

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

jk233

2016/06/13 04:45

ON句にsakujoFlg=0を書くという発想ができていませんでした… ありがとうございました。
guest

0

JK_BASHODATA のカラム、bashoId、sakujoFlg にインデックスをつければよろしいのでは?

投稿2016/06/13 03:02

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

0

正直数百、数千レコード程度であれば、JK_BASHODATA全件の中からsakujoFlgで絞り込む処理はそれほど負荷ではないと思います。しかし、逆にそのJK_BASHODATAが数十万、数百万件あるというのであれば、そもそもsakujoFlgで削除したあとも残し続けていることのほうが負荷になると思います。sakujoFlg=1のデータを定期的に別テーブルにバックアップ後DELETEするなどし、普段アクセスするJK_BASHODATAの件数を減らしてやる等の対処がよろしいかと思います。

投稿2016/06/13 03:01

編集2016/06/13 03:03
masaya_ohashi

総合スコア9206

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

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

jk233

2016/06/13 04:49

JK_BASHODATAは数十万件を想定しています。 削除されるのは全体の1%くらいです。 DELETE時にトリガで別テーブルにコピーするのが参照時の性能的に一番いいのかなと思いましたが、今回のケースではテーブルが増えてしまうデメリットの方が大きいと判断しました。 回答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問