元のテーブルが何を意味するのかが質問からは把握できませんでした。
売上なのか、セットメニューなのかといったところかと推測しています。
そのため、どのようなテーブル構造がよいのかという判断はできていません。
また、分割後のテーブルは、元のテーブルのデータ構造を正規化したものとなっていません。
つまり同じものとして扱うことはできないので、そのままでは元のテーブルを再現することはできません。
列名を動的に与える方法をすぐには思いつきませんでしたのでドリンク名は固定としていますが、下記のようにすれば元のテーブルの再現は可能かと思います。
sql
1 -- CREATE TABLE menus (id INT AUTO_INCREMENT PRIMARY KEY, menu VARCHAR(16));
2 -- INSERT INTO menus (menu) VALUES ('メニュー1'), ('メニュー2'), ('メニュー3');
3
4 -- CREATE TABLE drinks (id INT AUTO_INCREMENT PRIMARY KEY, drink VARCHAR(16));
5 -- INSERT INTO drinks (drink) VALUES ('Water'), ('POCARI SWEAT');
6
7 -- CREATE TABLE menudrink (id INT AUTO_INCREMENT PRIMARY KEY, menu_id INT, drink_id INT, number INT);
8 -- INSERT INTO menudrink (menu_id, drink_id, number) VALUES (1, 1, 1), (1, 2, 1), (2, 1, 2), (2, 2, 2), (3, 1, 1), (3, 2, 1);
9
10 SELECT m . id , m . menu , md1 . number ` Water ` , md2 . number ` POCARI SWEAT ` FROM menus m
11 INNER JOIN menudrink md1 ON m . id = md1 . menu_id AND md1 . drink_id = 1
12 INNER JOIN menudrink md2 ON m . id = md2 . menu_id AND md2 . drink_id = 2
13 ORDER BY 1 ;
14
15 + ----+---------------+-------+--------------+
16 | id | menu | Water | POCARI SWEAT |
17 + ----+---------------+-------+--------------+
18 | 1 | メニュー 1 | 1 | 1 |
19 | 2 | メニュー 2 | 2 | 2 |
20 | 3 | メニュー 3 | 1 | 1 |
21 + ----+---------------+-------+--------------+
22
23 SELECT * FROM
24 ( SELECT m . id , m . menu , md1 . number 'Water' , md2 . number 'POCARI SWEAT' FROM menus m
25 INNER JOIN menudrink md1 ON m . id = md1 . menu_id AND md1 . drink_id = 1
26 INNER JOIN menudrink md2 ON m . id = md2 . menu_id AND md2 . drink_id = 2 ) tmp
27 WHERE ` Water ` = 1 AND ` POCARI SWEAT ` = 1
28 ORDER BY 1 ;
29
30 + ----+---------------+-------+--------------+
31 | id | menu | Water | POCARI SWEAT |
32 + ----+---------------+-------+--------------+
33 | 1 | メニュー 1 | 1 | 1 |
34 | 3 | メニュー 3 | 1 | 1 |
35 + ----+---------------+-------+--------------+
ただし、この構造だとドリンクの数だけ列が増えていくことになります。
Water、POCARI SWEATの個数がそれぞれ1のメニューを取得したいということであれば、下記のようになるのではないかと考えられます。
SELECT m.id, m.menu FROM menus m
WHERE EXISTS (SELECT md.id FROM menudrink md WHERE m.id = md.menu_id AND drink_id = 1 AND md.number = 1)
AND EXISTS (SELECT md.id FROM menudrink md WHERE m.id = md.menu_id AND drink_id = 2 AND md.number = 1)
ORDER BY 1;
+----+---------------+
| id | menu |
+----+---------------+
| 1 | メニュー1 |
| 3 | メニュー3 |
+----+---------------+
どちらの場合も、プログラム側で動的にSQLを組み立てることになりますので、扱いやすい方を選ばれるとよいかと思います。
追記:
今回のデータを考える場合、出発点となる非正規テーブルは下記のような形となるのかと思います。
|id|メニュー名|ドリンク1|個数|ドリンク2|個数|
|:--|:--:|--:|
|1|メニュー1|Water|1|POCARI SWEAT|1|
|2|メニュー2|Water|2|POCARI SWEAT|2|
|3|メニュー3|Water|1|POCARI SWEAT|1|
このテーブルをデータの重複や繰り返しをなくす形で分割した場合、質問に記載の3テーブルへの分割は、正規化後の構造として問題ないものと思われます。
正規化の妥当性を検証するためには、分割後のテーブルから上記のテーブルが作成できることを確認されるとよいかと思います。
前述しているSQLにドリンク名が表示されるように組み立てると、再現できるかと思います。
一番最後のSQLが理解できないです。なぜこのSQLで積集合みたいなことが出来るのですか?
仮にメニュ−2のwaterの個数が1だった場合、一つ目のwhereはメニュー123が該当してしまうため、結果としてメニュー123がピックアップされると思うのですが。。。。。
メニュー2のWaterの個数が1、POCARI SWEATの個数が2の場合を考えます。
この場合、下記のSQLの結果としてメニュー2も含まれるようになります。
SELECT md.id FROM menudrink md WHERE m.id = md.menu_id AND drink_id = 1 AND md.number = 1
ただし、下記のSQLについては、POCARI SWEATの個数が2であるためにメニュー2のidは結果に含まれません。
SELECT md.id FROM menudrink md WHERE m.id = md.menu_id AND drink_id = 2 AND md.number = 1
EXISTSは結果が1行でも存在すれば真(true)、0件であれば偽(false)となる演算で、上記2つのSELECT文をWHERE句のAND条件で使用しています。
AND条件ですので、どちらか一方が真となっても、もう一方が偽であればWHERE句の抽出条件としては成立しません。
ここではAND条件で積集合を求めていると考えていただいてもよいかと思います。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/10/17 09:40
退会済みユーザー
2017/10/17 15:34
2017/10/17 22:31
退会済みユーザー
2017/10/18 01:49 編集
2017/10/18 09:42