-
仕事では MySQL を使用したシステムに携わることが多く、Web系の仕事の場合にはほぼ100%に近い感じです。
MySQL は Oracle社 が開発したもので現在もメンテナンスがなされている様です。 また、Yahoo や Google などのシステムにも採用されていることからも、安心感もあります。
MySQL について20本ぐらいの記事を書いてきましたが、関数やSELECTなどについて個別の事例に付いてが多かったですが、 今回は少し毛色を変えて、初心者の方がテーブルの考え方で少参考になりそうな内容を記してみます。
以下の内容で、順を追って説明します。■テストテーブル(商品マスタ、売上データ)の作成について
いろんなWeb上のシステムがありますが、データ処理を行う時に、意味合いがまとまった情報を1個のデータ集合として考えます。 例えば、「個人情報」「会社情報」「商品情報」「売上データ」「売上統計データ」などが在ると思います。
それぞれを別のテーブルとして情報を保存しておきますが、 このなかで「個人情報」「会社情報」「商品情報」など1回データを作成すればあまり変化がない情報をマスタデータとして それぞれ別々のテーブルとして持つことが一般的です。 (あまり変化がないと言いましたが、それぞれの情報は更新できる必要があります。この作業をマスタメンテナンスと言います。)
ここでは、「商品情報」と「売上データ」に関してのみを例にして説明したいと思います。
「商品情報」(商品マスタと言い換えます)は以下の様な項目が必要だと思います。- 商品コード(商品の型番?)
- 商品名
- 単価
「売上データ」は以下の様な項目が必要だと思います。 ここでは、誰が買ったかなどの情報は割愛して考えますので、商品と売上のみの情報の項目のみとしています。 (後、消費税についても割愛します。)- 売上NO
- 商品コード
- 日付
- 数量
- 単価(単価の変更があるため)
- 金額
上記の項目でテーブル作成SQLを考えますが、商品マスタの場合は「商品コード」を主キーにしてもいいのですが、 AUTO_INCREMENT な「id」を追加してこちらを主キーとします。
「商品コード」とマスタの識別子となる「id」を分けた方が、後々なにかと便利になるといいますか、 MySQL の作法的に私は「id」を付加しています。 (テーブルカラムの指定で AUTO_INCREMENT があるので気軽に連番のデータが付加できるとも言えますが...)
なお、「商品マスタ」「売上データ」ともに削除フラグ(デフォルトで「0」設定)を持たせました。 それぞれのデータの削除処理の時にフラグを「1」に更新する様に考えています。 こうすれば廃品種となったマスタのデータを残しておくことができます。
作成の SQL は以下の様になります。 (「OR REPLACE」の指定は置き換えを行います)-- 商品マスタ CREATE OR REPLACE TABLE tm_prod ( id INT AUTO_INCREMENT NOT NULL PRIMARY KEY, code VARCHAR(32), -- 商品コード name VARCHAR(32), -- 商品名 price INT, -- 単価 del INT DEFAULT 0 -- 削除フラグ:0(存在),1:(削除) ); -- 売上データ CREATE OR REPLACE TABLE tt_sales ( no INT AUTO_INCREMENT NOT NULL PRIMARY KEY, -- 売上NO id_prd INT, -- 商品マスタ.id salesdate DATE, -- 日付 volume INT, -- 数量 price INT, -- 単価 amount INT, -- 金額 del INT DEFAULT 0 -- 削除フラグ:0(存在),1:(削除) );それでは、上記のテーブルに以下の SQL でテストデータを登録します。
-- 商品テーブル INSERT INTO tm_prod(code, name, price) VALUES ('pen01', '鉛筆HB', 80), ('pen02', '鉛筆B' , 100), ('pen03', '鉛筆B2' , 100), ('cre01', 'くれよん12色', 1000), ('cre02', 'くれよん24色', 2000), ('pen10', '蛍光ペン', 120), ('pen11', '蛍光ペン2', 130), ('era01', '消しゴム', 150), ('era02', '消しゴム2', 200); -- 売上件数テーブル INSERT INTO tt_sales(id_prd, salesdate, volume, price, amount) VALUES (1, '2024-01-04', 10, 80, 800), (4, '2024-01-04', 1, 1000, 1000), (6, '2024-01-04', 2, 120, 240), (7, '2024-01-05', 3, 130, 390), (1, '2024-01-07', 15, 80, 1200), (2, '2024-01-07', 10, 100, 1000), (5, '2024-01-07', 2, 2000, 4000), (1, '2024-01-05', 20, 80, 1600), (3, '2024-01-05', 1, 100, 100), (4, '2024-01-10', 2, 1000, 2000), (2, '2024-01-15', 2, 100, 200), (6, '2024-01-05', 5, 120, 600), (7, '2024-01-14', 3, 130, 390), (8, '2024-01-20', 1, 150, 150), (5, '2024-01-20', 4, 2000, 8000), (8, '2024-01-22', 10, 150, 1500), (9, '2024-01-22', 10, 200, 2000);テストデータを確認します。
MariaDB [test]> select * from tm_prod; +----+-------+--------------+-------+------+ | id | code | name | price | del | +----+-------+--------------+-------+------+ | 19 | pen01 | 鉛筆HB | 80 | 0 | | 20 | pen02 | 鉛筆B | 100 | 0 | | 21 | pen03 | 鉛筆B2 | 100 | 0 | | 22 | cre01 | くれよん12色 | 1000 | 0 | | 23 | cre02 | くれよん24色 | 2000 | 0 | | 24 | pen10 | 蛍光ペン | 120 | 0 | | 25 | pen11 | 蛍光ペン2 | 130 | 0 | | 26 | era01 | 消しゴム | 150 | 0 | | 27 | era02 | 消しゴム2 | 200 | 0 | +----+-------+--------------+-------+------+ 9 rows in set (0.00 sec) MariaDB [test]> select * from tt_sales; +----+--------+------------+--------+-------+--------+------+ | no | id_prd | salesdate | volume | price | amount | del | +----+--------+------------+--------+-------+--------+------+ | 1 | 1 | 2024-01-04 | 10 | 80 | 800 | 0 | | 2 | 4 | 2024-01-04 | 1 | 1000 | 1000 | 0 | | 3 | 6 | 2024-01-04 | 2 | 120 | 240 | 0 | | 4 | 7 | 2024-01-05 | 3 | 130 | 390 | 0 | | 5 | 1 | 2024-01-07 | 15 | 80 | 1200 | 0 | | 6 | 2 | 2024-01-07 | 10 | 100 | 1000 | 0 | | 7 | 5 | 2024-01-07 | 2 | 2000 | 4000 | 0 | | 8 | 1 | 2024-01-05 | 20 | 80 | 1600 | 0 | | 9 | 3 | 2024-01-05 | 1 | 100 | 100 | 0 | | 10 | 4 | 2024-01-10 | 2 | 1000 | 2000 | 0 | | 11 | 2 | 2024-01-15 | 2 | 100 | 200 | 0 | | 12 | 6 | 2024-01-05 | 5 | 120 | 600 | 0 | | 13 | 7 | 2024-01-14 | 3 | 130 | 390 | 0 | | 14 | 8 | 2024-01-20 | 1 | 150 | 150 | 0 | | 15 | 5 | 2024-01-20 | 4 | 2000 | 8000 | 0 | | 16 | 8 | 2024-01-22 | 10 | 150 | 1500 | 0 | | 17 | 9 | 2024-01-22 | 10 | 200 | 2000 | 0 | +----+--------+------------+--------+-------+--------+------+ 17 rows in set (0.00 sec)
■売上データの一覧リストの取得について
それでは商品マスタから「名称」「商品コード」を取得する様にした、売上データの一覧をリストする SQL を示します。
「売上データ」の「商品id」と「商品マスタ」の「商品id」を INNER JOIN にて結合しています。
さらに、結合で「商品マスタ」の「削除フラグ」が「0:未削除」の条件付けを行っています。SELECT tt_sales.no AS '売上no', tt_sales.id_prd AS '商品id', tm_prod.code AS '商品コード', tm_prod.name AS '商品名', tt_sales.volume AS '数量', tt_sales.price AS '単価', tt_sales.amount AS '金額' FROM tt_sales INNER JOIN tm_prod ON tm_prod.id = tt_sales.id_prd AND tm_prod.del = 0 ORDER BY tt_sales.no;この SQL の結果は以下の通りです。
+--------+--------+------------+--------------+------+------+------+ | 売上no | 商品id | 商品コード | 商品名 | 数量 | 単価 | 金額 | +--------+--------+------------+--------------+------+------+------+ | 1 | 1 | pen01 | 鉛筆HB | 10 | 80 | 800 | | 2 | 4 | cre01 | くれよん12色 | 1 | 1000 | 1000 | | 3 | 6 | pen10 | 蛍光ペン | 2 | 120 | 240 | | 4 | 7 | pen11 | 蛍光ペン2 | 3 | 130 | 390 | | 5 | 1 | pen01 | 鉛筆HB | 15 | 80 | 1200 | | 6 | 2 | pen02 | 鉛筆B | 10 | 100 | 1000 | | 7 | 5 | cre02 | くれよん24色 | 2 | 2000 | 4000 | | 8 | 1 | pen01 | 鉛筆HB | 20 | 80 | 1600 | | 9 | 3 | pen03 | 鉛筆B2 | 1 | 100 | 100 | | 10 | 4 | cre01 | くれよん12色 | 2 | 1000 | 2000 | | 11 | 2 | pen02 | 鉛筆B | 2 | 100 | 200 | | 12 | 6 | pen10 | 蛍光ペン | 5 | 120 | 600 | | 13 | 7 | pen11 | 蛍光ペン2 | 3 | 130 | 390 | | 14 | 8 | era01 | 消しゴム | 1 | 150 | 150 | | 15 | 5 | cre02 | くれよん24色 | 4 | 2000 | 8000 | | 16 | 8 | era01 | 消しゴム | 10 | 150 | 1500 | | 17 | 9 | era02 | 消しゴム2 | 10 | 200 | 2000 | +--------+--------+------------+--------------+------+------+------+ 17 rows in set (0.00 sec)
「商品マスタ」との結合ですが、「削除フラグ:0」のデータのみを取得する SELECT文 をサブクエリのようにすれば、 以下の SQL でも同様のことができます。SELECT tt_sales.no AS '売上no', tt_sales.id_prd AS '商品id', TX.code AS '商品コード', TX.name AS '商品名', tt_sales.volume AS '数量', tt_sales.price AS '単価', tt_sales.amount AS '金額' FROM tt_sales INNER JOIN ( SELECT * FROM tm_prod WHERE tm_prod.del = 0 ) TX ON TX.id = tt_sales.id_prd ORDER BY tt_sales.no;■商品マスタが削除済みの場合の売上データの一覧リストの取得について
削除済みの商品マスタを設定し、売上データの一覧リストでどの様に処理するのか説明します。
最初に、商品マスタ内に削除済みデータを更新するため以下の SQL を実行します。UPDATE tm_prod SET del = 1 WHERE id IN(4, 7);
「id」が「4,7」のデータの削除フラグが「1」になることを確認します。
MariaDB [test]> select * from tm_prod; +----+-------+--------------+-------+------+ | id | code | name | price | del | +----+-------+--------------+-------+------+ | 1 | pen01 | 鉛筆HB | 80 | 0 | | 2 | pen02 | 鉛筆B | 100 | 0 | | 3 | pen03 | 鉛筆B2 | 100 | 0 | | 4 | cre01 | くれよん12色 | 1000 | 1 | | 5 | cre02 | くれよん24色 | 2000 | 0 | | 6 | pen10 | 蛍光ペン | 120 | 0 | | 7 | pen11 | 蛍光ペン2 | 130 | 1 | | 8 | era01 | 消しゴム | 150 | 0 | | 9 | era02 | 消しゴム2 | 200 | 0 | +----+-------+--------------+-------+------+ 9 rows in set (0.00 sec)
この状況で、先ほどのどちらかの SQL で売上一覧を実行してみます。
MariaDB [test]> SELECT -> tt_sales.no AS '売上no', -> tt_sales.id_prd AS '商品id', -> TX.code AS '商品コード', -> TX.name AS '商品名', -> tt_sales.volume AS '数量', -> tt_sales.price AS '単価', -> tt_sales.amount AS '金額' -> FROM tt_sales -> INNER JOIN ( -> SELECT * -> FROM tm_prod -> WHERE tm_prod.del = 0 -> ) TX -> ON TX.id = tt_sales.id_prd -> ORDER BY tt_sales.no; +--------+--------+------------+--------------+------+------+------+ | 売上no | 商品id | 商品コード | 商品名 | 数量 | 単価 | 金額 | +--------+--------+------------+--------------+------+------+------+ | 1 | 1 | pen01 | 鉛筆HB | 10 | 80 | 800 | | 3 | 6 | pen10 | 蛍光ペン | 2 | 120 | 240 | | 5 | 1 | pen01 | 鉛筆HB | 15 | 80 | 1200 | | 6 | 2 | pen02 | 鉛筆B | 10 | 100 | 1000 | | 7 | 5 | cre02 | くれよん24色 | 2 | 2000 | 4000 | | 8 | 1 | pen01 | 鉛筆HB | 20 | 80 | 1600 | | 9 | 3 | pen03 | 鉛筆B2 | 1 | 100 | 100 | | 11 | 2 | pen02 | 鉛筆B | 2 | 100 | 200 | | 12 | 6 | pen10 | 蛍光ペン | 5 | 120 | 600 | | 14 | 8 | era01 | 消しゴム | 1 | 150 | 150 | | 15 | 5 | cre02 | くれよん24色 | 4 | 2000 | 8000 | | 16 | 8 | era01 | 消しゴム | 10 | 150 | 1500 | | 17 | 9 | era02 | 消しゴム2 | 10 | 200 | 2000 | +--------+--------+------------+--------------+------+------+------+ 13 rows in set (0.00 sec)確かに、商品マスタの「削除フラグ:1」のデータは一覧に表示されません。
一覧としてはこれで正解のですが、削除済みのものも見たいという要求があれば INNER JOIN を LEFT JOIN に変更し「売上データ」側には「商品id」が存在するが 「商品マスタ」側にはデータが削除されているものも表示する様にします。MariaDB [test]> SELECT -> tt_sales.no AS '売上no', -> tt_sales.id_prd AS '商品id', -> TX.code AS '商品コード', -> TX.name AS '商品名', -> tt_sales.volume AS '数量', -> tt_sales.price AS '単価', -> tt_sales.amount AS '金額' -> FROM tt_sales -> LEFT JOIN ( -> SELECT * -> FROM tm_prod -> WHERE tm_prod.del = 0 -> ) TX -> ON TX.id = tt_sales.id_prd -> ORDER BY tt_sales.no; +--------+--------+------------+--------------+------+------+------+ | 売上no | 商品id | 商品コード | 商品名 | 数量 | 単価 | 金額 | +--------+--------+------------+--------------+------+------+------+ | 1 | 1 | pen01 | 鉛筆HB | 10 | 80 | 800 | | 2 | 4 | NULL | NULL | 1 | 1000 | 1000 | | 3 | 6 | pen10 | 蛍光ペン | 2 | 120 | 240 | | 4 | 7 | NULL | NULL | 3 | 130 | 390 | | 5 | 1 | pen01 | 鉛筆HB | 15 | 80 | 1200 | | 6 | 2 | pen02 | 鉛筆B | 10 | 100 | 1000 | | 7 | 5 | cre02 | くれよん24色 | 2 | 2000 | 4000 | | 8 | 1 | pen01 | 鉛筆HB | 20 | 80 | 1600 | | 9 | 3 | pen03 | 鉛筆B2 | 1 | 100 | 100 | | 10 | 4 | NULL | NULL | 2 | 1000 | 2000 | | 11 | 2 | pen02 | 鉛筆B | 2 | 100 | 200 | | 12 | 6 | pen10 | 蛍光ペン | 5 | 120 | 600 | | 13 | 7 | NULL | NULL | 3 | 130 | 390 | | 14 | 8 | era01 | 消しゴム | 1 | 150 | 150 | | 15 | 5 | cre02 | くれよん24色 | 4 | 2000 | 8000 | | 16 | 8 | era01 | 消しゴム | 10 | 150 | 1500 | | 17 | 9 | era02 | 消しゴム2 | 10 | 200 | 2000 | +--------+--------+------------+--------------+------+------+------+ 17 rows in set (0.00 sec)削除された「商品マスタ」の商品コード、商品名は NULL で表示されます。
NULL が気に入らないのであれば、以下の様にカラム表示の中を変えてやります。MariaDB [test]> SELECT -> tt_sales.no AS '売上no', -> tt_sales.id_prd AS '商品id', -> IFNULL(TX.code, '-----') AS '商品コード', -> IFNULL(TX.name, '*削除済*') AS '商品名', -> tt_sales.volume AS '数量', -> tt_sales.price AS '単価', -> tt_sales.amount AS '金額' -> FROM tt_sales -> LEFT JOIN ( -> SELECT * -> FROM tm_prod -> WHERE tm_prod.del = 0 -> ) TX -> ON TX.id = tt_sales.id_prd -> ORDER BY tt_sales.no; +--------+--------+------------+--------------+------+------+------+ | 売上no | 商品id | 商品コード | 商品名 | 数量 | 単価 | 金額 | +--------+--------+------------+--------------+------+------+------+ | 1 | 1 | pen01 | 鉛筆HB | 10 | 80 | 800 | | 2 | 4 | ----- | *削除済* | 1 | 1000 | 1000 | | 3 | 6 | pen10 | 蛍光ペン | 2 | 120 | 240 | | 4 | 7 | ----- | *削除済* | 3 | 130 | 390 | | 5 | 1 | pen01 | 鉛筆HB | 15 | 80 | 1200 | | 6 | 2 | pen02 | 鉛筆B | 10 | 100 | 1000 | | 7 | 5 | cre02 | くれよん24色 | 2 | 2000 | 4000 | | 8 | 1 | pen01 | 鉛筆HB | 20 | 80 | 1600 | | 9 | 3 | pen03 | 鉛筆B2 | 1 | 100 | 100 | | 10 | 4 | ----- | *削除済* | 2 | 1000 | 2000 | | 11 | 2 | pen02 | 鉛筆B | 2 | 100 | 200 | | 12 | 6 | pen10 | 蛍光ペン | 5 | 120 | 600 | | 13 | 7 | ----- | *削除済* | 3 | 130 | 390 | | 14 | 8 | era01 | 消しゴム | 1 | 150 | 150 | | 15 | 5 | cre02 | くれよん24色 | 4 | 2000 | 8000 | | 16 | 8 | era01 | 消しゴム | 10 | 150 | 1500 | | 17 | 9 | era02 | 消しゴム2 | 10 | 200 | 2000 | +--------+--------+------------+--------------+------+------+------+ 17 rows in set (0.00 sec)
PR -
データの編集などを含んだ SELECT文 を別のテーブルの様に扱えるのが ビュー(VIEW) で毎回同じ SELECT文 を記述しなくても 通常のテーブルの様に扱えます。
この ビュー(VIEW) を作成を CREATE VIEW で行います。
CREATE VIEW について順を追って説明します。■CREATE VIEWの書き方
CREATE VIEW文 の書式は以下の通りです。
CREATE [OR REPLACE] VIEW view_name [(column_list)] AS select_statement -- view_name : ビューの名称 -- [(column_list)] : カラム名リスト(select_statement のカラムリストに名前を付ける) -- select_statement : ビューの元となる SELECT文
なお CREATE OR REPLACE VIEW の指定の場合は、既にビューが存在している場合は上書きを行います。
それでは簡単例のビューの作成を行ってみます。 ビューの元となる SELECT文 はリテラルの SELECT でも問題ないので、UNION の説明で使用した以下の SQL を当てます。
⇒ MySQL 異なるデータ取得(SELECT)結果を結合して取得する方法について(UNION)CREATE VIEW t_view AS SELECT '1' AS 'COL1', '2' AS 'COL2' UNION SELECT 'A' AS 'COLA', 'B' AS 'COLB';作成されたビューの一覧を表示します。
MariaDB [test]> SELECT * FROM t_view; +------+------+ | COL1 | COL2 | +------+------+ | 1 | 2 | | A | B | +------+------+ 2 rows in set (0.00 sec)
それではビューのカラム名を変更してみます。 ビューの上書きなので以下の文となります。
CREATE OR REPLACE VIEW t_view(COLUMN1, COLUMN2) AS SELECT '1' AS 'COL1', '2' AS 'COL2' UNION SELECT 'A' AS 'COLA', 'B' AS 'COLB';変更されたビューの一覧を表示します。 カラム名称が変更されたことが分かります。
MariaDB [test]> SELECT * FROM t_view; +---------+---------+ | COLUMN1 | COLUMN2 | +---------+---------+ | 1 | 2 | | A | B | +---------+---------+ 2 rows in set (0.00 sec)
この様なリテラル表では面白くないので、テーブルを使った例を以降に記します。
■CREATE VIEW の元となるテーブルについて
以下のページで使用した 商品テーブル(マスタ) と 売上数量テーブル を使用してビューにします。
⇒ MySQL データ取得(SELECT)のWHRE句に サブクエリ条件で ANY ALL の使い方について(ANY、SOME、ALL)
商品テーブル(マスタ) と 売上数量テーブル は以下の通りです。MariaDB [test]> SELECT * FROM product; +----+----------+-------+ | id | name | price | +----+----------+-------+ | 1 | 鉛筆A | 80 | | 2 | 鉛筆B | 100 | | 3 | くれよん | 500 | | 4 | 蛍光ペン | 120 | | 5 | 消しゴム | 150 | +----+----------+-------+ 5 rows in set (0.00 sec) MariaDB [test]> SELECT * FROM sales; +----+--------+------------+--------+ | id | id_prd | salesdate | volume | +----+--------+------------+--------+ | 1 | 1 | 2024-01-04 | 10 | | 2 | 1 | 2024-01-07 | 15 | | 3 | 2 | 2024-01-07 | 20 | | 4 | 3 | 2024-01-05 | 1 | | 5 | 3 | 2024-01-10 | 2 | | 6 | 3 | 2024-01-15 | 2 | | 7 | 4 | 2024-01-05 | 5 | | 8 | 4 | 2024-01-14 | 3 | | 9 | 4 | 2024-01-20 | 1 | | 10 | 5 | 2024-01-20 | 5 | | 11 | 5 | 2024-01-22 | 10 | +----+--------+------------+--------+ 11 rows in set (0.01 sec)
商品テーブルと売上数量テーブルを連結しての商品名称入りの一覧を表示してみます。
今回のテストのための基本の SELECT文 となります。SELECT sales.id, sales.id_prd, product.name, sales.salesdate, sales.volume FROM sales LEFT JOIN product ON sales.id_prd = product.id ORDER BY sales.id;
この SQL の結果は以下の通りです。
+----+--------+----------+------------+--------+ | id | id_prd | name | salesdate | volume | +----+--------+----------+------------+--------+ | 1 | 1 | 鉛筆A | 2024-01-04 | 10 | | 2 | 1 | 鉛筆A | 2024-01-07 | 15 | | 3 | 2 | 鉛筆B | 2024-01-07 | 20 | | 4 | 3 | くれよん | 2024-01-05 | 1 | | 5 | 3 | くれよん | 2024-01-10 | 2 | | 6 | 3 | くれよん | 2024-01-15 | 2 | | 7 | 4 | 蛍光ペン | 2024-01-05 | 5 | | 8 | 4 | 蛍光ペン | 2024-01-14 | 3 | | 9 | 4 | 蛍光ペン | 2024-01-20 | 1 | | 10 | 5 | 消しゴム | 2024-01-20 | 5 | | 11 | 5 | 消しゴム | 2024-01-22 | 10 | +----+--------+----------+------------+--------+ 11 rows in set (0.00 sec)
■CREATE VIEW とその利用
上記の商品名称入りの一覧表示の SELECT文 を使ってビューを作成します。
CREATE VIEW v_sales(id_sal, id_prd, prd_name, salesdate, salesvol) AS SELECT sales.id, sales.id_prd, product.name, sales.salesdate, sales.volume FROM sales LEFT JOIN product ON sales.id_prd = product.id;
作成されたビューを表示します。
MariaDB [test]> SELECT * FROM v_sales; +--------+--------+----------+------------+----------+ | id_sal | id_prd | prd_name | salesdate | salesvol | +--------+--------+----------+------------+----------+ | 1 | 1 | 鉛筆A | 2024-01-04 | 10 | | 2 | 1 | 鉛筆A | 2024-01-07 | 15 | | 3 | 2 | 鉛筆B | 2024-01-07 | 20 | | 4 | 3 | くれよん | 2024-01-05 | 1 | | 5 | 3 | くれよん | 2024-01-10 | 2 | | 6 | 3 | くれよん | 2024-01-15 | 2 | | 7 | 4 | 蛍光ペン | 2024-01-05 | 5 | | 8 | 4 | 蛍光ペン | 2024-01-14 | 3 | | 9 | 4 | 蛍光ペン | 2024-01-20 | 1 | | 10 | 5 | 消しゴム | 2024-01-20 | 5 | | 11 | 5 | 消しゴム | 2024-01-22 | 10 | +--------+--------+----------+------------+----------+ 11 rows in set (0.11 sec)
カラム名がビュー作成で指定した名称になっていることが分かります。
なお、ビューは通常のテーブルの様に扱うことができます。
例えば WHERE で条件を付けたり、ORDER BY と LIMIT で表示を制限したりできます。MariaDB [test]> SELECT * FROM v_sales -> WHERE salesdate >= '2024-01-05' -> ORDER BY salesvol -> LIMIT 8; +--------+--------+----------+------------+----------+ | id_sal | id_prd | prd_name | salesdate | salesvol | +--------+--------+----------+------------+----------+ | 4 | 3 | くれよん | 2024-01-05 | 1 | | 9 | 4 | 蛍光ペン | 2024-01-20 | 1 | | 5 | 3 | くれよん | 2024-01-10 | 2 | | 6 | 3 | くれよん | 2024-01-15 | 2 | | 8 | 4 | 蛍光ペン | 2024-01-14 | 3 | | 7 | 4 | 蛍光ペン | 2024-01-05 | 5 | | 10 | 5 | 消しゴム | 2024-01-20 | 5 | | 11 | 5 | 消しゴム | 2024-01-22 | 10 | +--------+--------+----------+------------+----------+ 8 rows in set (0.03 sec)
-
似た様な複数の SELECT文 の結果を結合するために、連結するのが UNION です。
各 SELECT文 のカラム数及びデータ型は一致させておくべきです。
UNION について順を追って説明します。- UNIONの書き方
- UNION DISTINCT と UNION ALL
- UNION での ORDER BY と LIMIT
- UNION での ORDER BY と LIMIT その例1
- UNION での ORDER BY と LIMIT その例2
■UNIONの書き方
複数の SELECT文 の結果を結合する UNION の書式は以下の様です。
SELECT 「カラム一覧1」FROM 「テーブル1」... UNION [ALL | DISTINCT] SELECT 「カラム一覧2」FROM 「テーブル2」... [UNION [ALL | DISTINCT] SELECT 「カラム一覧N」FROM 「テーブルN」...] -- UNION のみ記述 : 重複行は削除されます。(UNION DISTINCT)と同様 -- UNION ALL : 重複行もそのまま表示されます。
UNION 結果セットのカラム名は、最初の SELECT ステートメントのカラム名が採用されます。
各 SELECT ステートメントの対応する位置にリストされているカラムは、同じデータ型に揃えた方が良いようです。
以下にテーブルをリテラルで書いた SQL を示します。MariaDB [test]> SELECT '1' AS 'COL1', '2' AS 'COL2'; +------+------+ | COL1 | COL2 | +------+------+ | 1 | 2 | +------+------+ 1 row in set (0.00 sec) MariaDB [test]> SELECT 'A' AS 'COLA', 'B' AS 'COLB'; +------+------+ | COLA | COLB | +------+------+ | A | B | +------+------+ 1 row in set (0.00 sec) MariaDB [test]> SELECT '1' AS 'COL1', '2' AS 'COL2' -> UNION -> SELECT 'A' AS 'COLA', 'B' AS 'COLB'; +------+------+ | COL1 | COL2 | +------+------+ | 1 | 2 | | A | B | +------+------+ 2 rows in set (0.00 sec)
1番目、2番目の SQL はテーブルを個別にデータ取得し、3番目の SQL で UNION を行っています。
確かに結合された結果セットが返されます。
■UNION DISTINCT と UNION ALL
結合の方法で、重複する行を削除する場合と、全てを取得する場合の例を示します。
以下の SQL は4個の SELECT文 を UNION で連結しています。
3,4番目の SELECT文 は、1,2番目の SELECT文 と同じデータを返しますので 重複分となり、結果セットには 1,2番目の SELECT文 の結果のみが返される様になります。MariaDB [test]> SELECT '1' AS 'COL1', '2' AS 'COL2' -> UNION -> SELECT 'A' AS 'COLA', 'B' AS 'COLB' -> UNION -> SELECT '1' AS 'COL11', '2' AS 'COL22' -> UNION -> SELECT 'A' AS 'COLAA', 'B' AS 'COLBB'; +------+------+ | COL1 | COL2 | +------+------+ | 1 | 2 | | A | B | +------+------+ 2 rows in set (0.00 sec)
以下の SQL は4個の SELECT文 を UNION ALL で連結しています。
結果セットとしては 1,2,3,4番目の SELECT文 のデータを順に返しています。MariaDB [test]> SELECT '1' AS 'COL1', '2' AS 'COL2' -> UNION ALL -> SELECT 'A' AS 'COLA', 'B' AS 'COLB' -> UNION ALL -> SELECT '1' AS 'COL11', '2' AS 'COL22' -> UNION ALL -> SELECT 'A' AS 'COLAA', 'B' AS 'COLBB'; +------+------+ | COL1 | COL2 | +------+------+ | 1 | 2 | | A | B | | 1 | 2 | | A | B | +------+------+ 4 rows in set (0.00 sec)■UNION での ORDER BY と LIMIT
UNION で連結される SELECT文 の中で ORDER BY と LIMIT を使う場合は SELECT文 をカッコ「()」で囲みその中に配置します。 以下の様な感じです。
(SELECT 「カラムリスト1」 FROM table1 WHERE 「条件1」 ORDER BY 「ソート指定1」 LIMIT 「行数1」) UNION (SELECT 「カラムリスト2」 FROM table2 WHERE 「条件2」 ORDER BY 「ソート指定2」 LIMIT 「行数2」);
ORDER BY と LIMIT を使う例を示すために、以下のページで作成したテーブルを使用します。
⇒ MySQL データ取得(SELECT)のWHRE句に サブクエリ条件で ANY ALL の使い方について(ANY、SOME、ALL)
以下の様な 商品テーブル(マスタ) と 売上数量テーブル を使用します。MariaDB [test]> SELECT * FROM product; +----+----------+-------+ | id | name | price | +----+----------+-------+ | 1 | 鉛筆A | 80 | | 2 | 鉛筆B | 100 | | 3 | くれよん | 500 | | 4 | 蛍光ペン | 120 | | 5 | 消しゴム | 150 | +----+----------+-------+ 5 rows in set (0.00 sec) MariaDB [test]> SELECT * FROM sales; +----+--------+------------+--------+ | id | id_prd | salesdate | volume | +----+--------+------------+--------+ | 1 | 1 | 2024-01-04 | 10 | | 2 | 1 | 2024-01-07 | 15 | | 3 | 2 | 2024-01-07 | 20 | | 4 | 3 | 2024-01-05 | 1 | | 5 | 3 | 2024-01-10 | 2 | | 6 | 3 | 2024-01-15 | 2 | | 7 | 4 | 2024-01-05 | 5 | | 8 | 4 | 2024-01-14 | 3 | | 9 | 4 | 2024-01-20 | 1 | | 10 | 5 | 2024-01-20 | 5 | | 11 | 5 | 2024-01-22 | 10 | +----+--------+------------+--------+ 11 rows in set (0.01 sec)
商品テーブルと売上数量テーブルを連結しての商品名称入りの一覧を表示してみます。
今回のテストのための基本の SQL となります。SELECT sales.id, sales.id_prd, product.name, sales.salesdate, sales.volume FROM sales LEFT JOIN product ON sales.id_prd = product.id ORDER BY sales.id;
この SQL の結果は以下の通りです。
+----+--------+----------+------------+--------+ | id | id_prd | name | salesdate | volume | +----+--------+----------+------------+--------+ | 1 | 1 | 鉛筆A | 2024-01-04 | 10 | | 2 | 1 | 鉛筆A | 2024-01-07 | 15 | | 3 | 2 | 鉛筆B | 2024-01-07 | 20 | | 4 | 3 | くれよん | 2024-01-05 | 1 | | 5 | 3 | くれよん | 2024-01-10 | 2 | | 6 | 3 | くれよん | 2024-01-15 | 2 | | 7 | 4 | 蛍光ペン | 2024-01-05 | 5 | | 8 | 4 | 蛍光ペン | 2024-01-14 | 3 | | 9 | 4 | 蛍光ペン | 2024-01-20 | 1 | | 10 | 5 | 消しゴム | 2024-01-20 | 5 | | 11 | 5 | 消しゴム | 2024-01-22 | 10 | +----+--------+----------+------------+--------+ 11 rows in set (0.00 sec)
■UNION での ORDER BY と LIMIT その例1
以下に連結される SELECT文 の中に ORDER BY と LIMIT を持つテスト SQL を実行します。
最初の SELECT文 は、上記の売上数量テーブルの売上日付に対して '2024-01-05' 以前の条件を付けて、出力件数を「2」にし、 後の SELECT文 は、売上数量テーブルの売上日付に対して '2024-01-20' 以降の条件を付けて、出力件数を「2」に設定しています。MariaDB [test]> ( -> SELECT sales.id, sales.id_prd, product.name, sales.salesdate, sales.volume FROM sales -> LEFT JOIN product -> ON sales.id_prd = product.id -> WHERE sales.salesdate <= '2024-01-05' -> ORDER BY sales.id -> LIMIT 2 -> ) -> UNION -> ( -> SELECT sales.id, sales.id_prd, product.name, sales.salesdate, sales.volume FROM sales -> LEFT JOIN product -> ON sales.id_prd = product.id -> WHERE sales.salesdate >= '2024-01-20' -> ORDER BY sales.id -> LIMIT 2 -> ); +----+--------+----------+------------+--------+ | id | id_prd | name | salesdate | volume | +----+--------+----------+------------+--------+ | 1 | 1 | 鉛筆A | 2024-01-04 | 10 | | 4 | 3 | くれよん | 2024-01-05 | 1 | | 9 | 4 | 蛍光ペン | 2024-01-20 | 1 | | 10 | 5 | 消しゴム | 2024-01-20 | 5 | +----+--------+----------+------------+--------+ 4 rows in set (0.00 sec)
最終的な結果セットは「4」行ですが、2個の SELECT文 を個別に LIMIT 無しで実行してみます。MariaDB [test]> SELECT sales.id, sales.id_prd, product.name, sales.salesdate, sales.volume FROM sales -> LEFT JOIN product -> ON sales.id_prd = product.id -> WHERE sales.salesdate <= '2024-01-05' -> ORDER BY sales.id; +----+--------+----------+------------+--------+ | id | id_prd | name | salesdate | volume | +----+--------+----------+------------+--------+ | 1 | 1 | 鉛筆A | 2024-01-04 | 10 | | 4 | 3 | くれよん | 2024-01-05 | 1 | | 7 | 4 | 蛍光ペン | 2024-01-05 | 5 | +----+--------+----------+------------+--------+ 3 rows in set (0.00 sec)MariaDB [test]> SELECT sales.id, sales.id_prd, product.name, sales.salesdate, sales.volume FROM sales -> LEFT JOIN product -> ON sales.id_prd = product.id -> WHERE sales.salesdate >= '2024-01-20' -> ORDER BY sales.id; +----+--------+----------+------------+--------+ | id | id_prd | name | salesdate | volume | +----+--------+----------+------------+--------+ | 9 | 4 | 蛍光ペン | 2024-01-20 | 1 | | 10 | 5 | 消しゴム | 2024-01-20 | 5 | | 11 | 5 | 消しゴム | 2024-01-22 | 10 | +----+--------+----------+------------+--------+ 3 rows in set (0.00 sec)
この結果から、UNION で連結した SQL では確かに個別の LIMIT が効いていることが分かります。
■UNION での ORDER BY と LIMIT その例2
UNION で連結された結果セットに対して ORDER BY と LIMIT を行う場合は、 最後の文の後ろに ORDER BY と LIMIT を配置します。
上記の UNION の例の SQL で個別の SELECT文 では ORDER BY と LIMIT を行わず、最後に配置してみます。MariaDB [test]> ( -> SELECT sales.id, sales.id_prd, product.name, sales.salesdate, sales.volume FROM sales -> LEFT JOIN product -> ON sales.id_prd = product.id -> WHERE sales.salesdate <= '2024-01-05' -> ) -> UNION -> ( -> SELECT sales.id, sales.id_prd, product.name, sales.salesdate, sales.volume FROM sales -> LEFT JOIN product -> ON sales.id_prd = product.id -> WHERE sales.salesdate >= '2024-01-20' -> ) -> ORDER BY volume -> LIMIT 5; +----+--------+----------+------------+--------+ | id | id_prd | name | salesdate | volume | +----+--------+----------+------------+--------+ | 9 | 4 | 蛍光ペン | 2024-01-20 | 1 | | 4 | 3 | くれよん | 2024-01-05 | 1 | | 10 | 5 | 消しゴム | 2024-01-20 | 5 | | 7 | 4 | 蛍光ペン | 2024-01-05 | 5 | | 1 | 1 | 鉛筆A | 2024-01-04 | 10 | +----+--------+----------+------------+--------+ 5 rows in set (0.05 sec)
売上数量の昇順で全ての UNION の結果データをソートしてから、先頭から5行の結果セットになっています。
-
サブクエリとは SELECT で取得した結果を SELECT文、UPDATE文などで利用することをいいます。
2個以上のテーブルが在って、一方のテーブルを処理するにあたって、他方のテーブルから取得したカラム値をWHERE句の条件に使ったりします。
サブクエリの説明のため以下の2個のテーブルを準備します。 1個は 商品テーブル(マスタ) で、もう1個は商品の 売上数量テーブル です。 商品テーブルは「商品id」「名称」「単価」のカラムがあり、 売上数量テーブルは「テーブルid」「商品id」「売上日」「売上数量」のカラムがあります。-- 商品テーブル CREATE TABLE product ( id INT, name VARCHAR(32), price INT ); -- 売上数量テーブル CREATE TABLE sales ( id INT AUTO_INCREMENT NOT NULL PRIMARY KEY, id_prd INT, salesdate DATE, volume INT );それでは、上記のテーブルに以下の SQL でテストデータを登録します。
-- 商品テーブル INSERT INTO product(id, name, price) VALUES (1, '鉛筆A', 80), (2, '鉛筆B', 100), (3, 'くれよん', 500), (4, '蛍光ペン', 120), (5, '消しゴム', 150); -- 売上件数テーブル INSERT INTO sales(id_prd, salesdate, volume) VALUES (1, '2024-01-04', 10), (1, '2024-01-07', 15), (2, '2024-01-07', 20), (3, '2024-01-05', 1), (3, '2024-01-10', 2), (3, '2024-01-15', 2), (4, '2024-01-05', 5), (4, '2024-01-14', 3), (4, '2024-01-20', 1), (5, '2024-01-20', 5), (5, '2024-01-22', 10);登録されたデータの確認を行います。
MariaDB [test]> SELECT * FROM product; +----+----------+-------+ | id | name | price | +----+----------+-------+ | 1 | 鉛筆A | 80 | | 2 | 鉛筆B | 100 | | 3 | くれよん | 500 | | 4 | 蛍光ペン | 120 | | 5 | 消しゴム | 150 | +----+----------+-------+ 5 rows in set (0.00 sec) MariaDB [test]> SELECT * FROM sales; +----+--------+------------+--------+ | id | id_prd | salesdate | volume | +----+--------+------------+--------+ | 1 | 1 | 2024-01-04 | 10 | | 2 | 1 | 2024-01-07 | 15 | | 3 | 2 | 2024-01-07 | 20 | | 4 | 3 | 2024-01-05 | 1 | | 5 | 3 | 2024-01-10 | 2 | | 6 | 3 | 2024-01-15 | 2 | | 7 | 4 | 2024-01-05 | 5 | | 8 | 4 | 2024-01-14 | 3 | | 9 | 4 | 2024-01-20 | 1 | | 10 | 5 | 2024-01-20 | 5 | | 11 | 5 | 2024-01-22 | 10 | +----+--------+------------+--------+ 11 rows in set (0.01 sec)
商品テーブルと売上数量テーブルを連結しての商品名称入りの一覧を表示してみます。SELECT sales.id, sales.id_prd, product.name, sales.salesdate, sales.volume FROM sales LEFT JOIN product ON sales.id_prd = product.id ORDER BY sales.id;
この SQL の結果は以下の通りです。
+----+--------+----------+------------+--------+ | id | id_prd | name | salesdate | volume | +----+--------+----------+------------+--------+ | 1 | 1 | 鉛筆A | 2024-01-04 | 10 | | 2 | 1 | 鉛筆A | 2024-01-07 | 15 | | 3 | 2 | 鉛筆B | 2024-01-07 | 20 | | 4 | 3 | くれよん | 2024-01-05 | 1 | | 5 | 3 | くれよん | 2024-01-10 | 2 | | 6 | 3 | くれよん | 2024-01-15 | 2 | | 7 | 4 | 蛍光ペン | 2024-01-05 | 5 | | 8 | 4 | 蛍光ペン | 2024-01-14 | 3 | | 9 | 4 | 蛍光ペン | 2024-01-20 | 1 | | 10 | 5 | 消しゴム | 2024-01-20 | 5 | | 11 | 5 | 消しゴム | 2024-01-22 | 10 | +----+--------+----------+------------+--------+ 11 rows in set (0.00 sec)
■[1個の値を返すサブクエリ]の例
売上数量テーブルからみて売上日付を条件して、「商品id」の最小値をサブクエリで取得し、 その「商品id」で商品テーブルの「id」の条件付けを行います。
SELECT product.id, product.name FROM product WHERE product.id = ( SELECT MIN(id_prd) FROM sales WHERE salesdate = '2024-01-05' );
この SQL の結果は以下の通りです。 (「2024-01-05」の売上日で最小の「商品id」は「3」となります。)+----+----------+ | id | name | +----+----------+ | 3 | くれよん | +----+----------+ 1 row in set (0.00 sec)
■[複数の値を返すサブクエリで ANY を使う]の例
ANY を使う場合は以下の様な書き方で行います。 ANY は意味合いとして「このサブクエリが返すカラム内の値の "いずれか" に対して比較が TRUE である場合は TRUE を返す」ことを示します。
なお、SOME は ANY のエイリアスなので ANY を SOME に置き換えても同じことになります。
-- ANY を使う場合のSQLの書き方 「条件カラム(式)」「比較演算子」ANY (サブクエリ) -- 条件カラム(式) : 条件式の左側(テーブルカラムや計算式) -- 比較演算子 : = > < >= <= <> !=
それでは実際の例で見てみます。-- 売上件数が10以上の商品 SELECT product.id, product.name FROM product WHERE product.id = ANY ( SELECT id_prd FROM sales WHERE volume >= 10 ) ORDER BY product.id;この SQL の結果は以下の様になります。
+----+----------+ | id | name | +----+----------+ | 1 | 鉛筆A | | 2 | 鉛筆B | | 5 | 消しゴム | +----+----------+ 3 rows in set (0.00 sec)
サブクエリの中身だけ実行すると以下の様になります。
SELECT id_prd FROM sales WHERE volume >= 10;
この SQL の結果は以下の様になります。
+--------+ | id_prd | +--------+ | 1 | | 1 | | 2 | | 5 | +--------+ 4 rows in set (0.00 sec)
売上数量テーブルには該当するレコードが4件ありますが、商品テーブルの「id」から見て比較した場合、 「1,1,2,5」の何れかに該当する場合に条件が TRUE になるので商品テーブルの「id:1」は TRUE となります。 よってサブクエリの SQL では3件の商品が対象となります。
4件の値が返るのが少し気持ち悪いのであれば、サブクエリに DISTINCT で重複を抑えることもできます。
今回の ANY を使ったサブクエリですが「= ANY」の部分を「IN」に替えても同様の結果が得られます。
(こちらの方が一般的かも知れません。)SELECT product.id, product.name FROM product WHERE product.id IN ( SELECT id_prd FROM sales WHERE volume >= 10 ) ORDER BY product.id;
■[複数の値を返すサブクエリで ALL を使う]の例
ANY と同様なのですが、ALL を使う場合は以下の様な書き方で行います。 ALL は意味合いとして「このサブクエリが返すカラム内の値の "すべて" に対して比較が TRUE である場合は TRUE を返す」ことを示します。
-- ALL を使う場合のSQLの書き方 「条件カラム(式)」「比較演算子」ALL (サブクエリ) -- 条件カラム(式) : 条件式の左側(テーブルカラムや計算式) -- 比較演算子 : = > < >= <= <> !=
それでは実際の例で見てみます。-- 売上件数が5未満の商品 SELECT product.id, product.name FROM product WHERE product.id >= ALL ( SELECT id_prd FROM sales WHERE volume < 5 ) ORDER BY product.id;
この SQL の結果は以下の様になります。
+----+----------+ | id | name | +----+----------+ | 4 | 蛍光ペン | | 5 | 消しゴム | +----+----------+ 2 rows in set (0.00 sec)
サブクエリの中身だけ実行すると以下の様になります。
SELECT id_prd FROM sales WHERE volume < 5;
この SQL の結果は以下の様になります。
+--------+ | id_prd | +--------+ | 3 | | 3 | | 3 | | 4 | | 4 | +--------+ 5 rows in set (0.00 sec)
売上数量テーブルには該当するレコードが5件ありますが、商品テーブルの「id」から見て比較した場合、 「3,3,3,4,4」以上に該当する場合に条件が TRUE になるので商品テーブルの「id:4」「id:5」は TRUE となります。 よってサブクエリの SQL では2件の商品が対象となります。
ここで比較演算子を「>=」から「!=」にしてみます。これはサブクエリの結果以外のものを TRUE に判定します。-- 売上件数が5未満以外の商品 SELECT product.id, product.name FROM product WHERE product.id != ALL ( SELECT id_prd FROM sales WHERE volume < 5 ) ORDER BY product.id;
サブクエリの中身は「商品id」が「3,4」を返すので、「!=」の演算子であればそれ以外の商品を取得します。
よって以下の様な結果になります。+----+----------+ | id | name | +----+----------+ | 1 | 鉛筆A | | 2 | 鉛筆B | | 5 | 消しゴム | +----+----------+ 3 rows in set (0.00 sec)
上記の ALL を使ったサブクエリですが「!= ALL」の部分を「NOT IN」に替えても同様の結果が得られます。
(こちらの方が一般的かも知れません。)SELECT product.id, product.name FROM product WHERE product.id NOT IN ( SELECT id_prd FROM sales WHERE volume < 5 ) ORDER BY product.id;
-
MySQL では2個のテーブルを結合してデータ取得(SELECT)を行うのですが、この時テーブルのカラムを用いて2個のテーブルを連結します。 両方のテーブルにデータが存在するもののみを取得することを 内部結合 といいます。 また、片一方のテーブルにしかデータが存在しないものを取得することを 外部結合 といいます。
テーブルの構造を考える時に、データ項目において性質が同じものはコード化を行い別テーブルにその項目の内容を持つ様にします。 このことをテーブルの正規化をと言いますが、なるべく生のデータを持たない様にします。
例として「個人」のデータ(個人テーブル)を持つ場合、情報として名前、性別、結婚状態、生年月日などがあります。 名前や生年月日は個人ごとに異なるので個人テーブルにそのままデータを持ちます。
ただし、「性別」としては「男性」「女性」「その他」「未回答」などがあると思いますが、これらをそのままの文字列で持つと無駄だったりしますので、 「性別」テーブルを別にコード化して持ちます。
また、「結婚状態」では「独身」「既婚」「離婚・死別」などががありますので、「結婚状態」テーブルを持ちます。
とにかく、テストを示すテーブルが無いことには始まりませんので、以下の3個のテーブル「個人」「性別」「結婚状態」を以下の SQL で作成します。-- 個人テーブル CREATE TABLE person ( id INT AUTO_INCREMENT NOT NULL PRIMARY KEY, name VARCHAR(32), id_mrg INT, id_gnd INT ); -- 既婚状態テーブル CREATE TABLE marriage ( id INT NOT NULL PRIMARY KEY, val VARCHAR(32) ); -- 性別状態テーブル CREATE TABLE gender ( id INT NOT NULL PRIMARY KEY, val VARCHAR(32) );それでは、上記のテーブルに以下の SQL でテストデータを登録します。
-- 既婚状態テーブル INSERT INTO marriage(id, val) VALUES(1, '独身') , (2, '既婚'), (3, '離婚'), (4, '死別'); -- 性別状態テーブル INSERT INTO gender(id, val) VALUES(1, '男性') , (2, '女性'), (3, 'その他'), (4, '未回答'); -- 個人テーブル INSERT INTO person(name, id_mrg, id_gnd) VALUES ('田中 太郎', 1, 1), ('山田 一郎', 2, 1), ('青木 花子', 2, 2), ('加藤 清正', 3, 1), ('斎藤 優子', 4, 2), ('石田 次郎', 2, 3), ('伊藤 啓治', NULL, 4), ('山下 幸一', 1, NULL);登録されたデータの確認を行います。
MariaDB [test]> SELECT * FROM marriage; +----+------+ | id | val | +----+------+ | 1 | 独身 | | 2 | 既婚 | | 3 | 離婚 | | 4 | 死別 | +----+------+ 4 rows in set (0.00 sec) MariaDB [test]> SELECT * FROM gender; +----+--------+ | id | val | +----+--------+ | 1 | 男性 | | 2 | 女性 | | 3 | その他 | | 4 | 未回答 | +----+--------+ 4 rows in set (0.00 sec) MariaDB [test]> SELECT * FROM person; +----+------------+--------+--------+ | id | name | id_mrg | id_gnd | +----+------------+--------+--------+ | 1 | 田中 太郎 | 1 | 1 | | 2 | 山田 一郎 | 2 | 1 | | 3 | 青木 花子 | 2 | 2 | | 4 | 加藤 清正 | 3 | 1 | | 5 | 斎藤 優子 | 4 | 2 | | 6 | 石田 次郎 | 2 | 3 | | 7 | 伊藤 啓治 | NULL | 4 | | 8 | 山下 幸一 | 1 | NULL | +----+------------+--------+--------+ 8 rows in set (0.00 sec)
■[内部結合(INNER JOIN)]の例
内部結合(INNER JOIN)の使い方ですが、以下の様な感じで行います。
-- [table1]と[table2]の結合 SELECT table1.カラムリスト [,table2.カラムリスト] FROM table1 INNER JOIN table2 ON table1.[table2のidと紐づくカラム] = table2.id;
内部結合(INNER JOIN)を使って「個人」テーブルの一覧を取得します。SELECT person.id, person.name, marriage.val AS '婚姻情報', gender.val AS '性別情報' FROM person INNER JOIN marriage ON person.id_mrg = marriage.id INNER JOIN gender ON person.id_gnd = gender.id;
この SQL の結果は以下の様になります。+----+------------+----------+----------+ | id | name | 婚姻情報 | 性別情報 | +----+------------+----------+----------+ | 1 | 田中 太郎 | 独身 | 男性 | | 2 | 山田 一郎 | 既婚 | 男性 | | 4 | 加藤 清正 | 離婚 | 男性 | | 3 | 青木 花子 | 既婚 | 女性 | | 5 | 斎藤 優子 | 死別 | 女性 | | 6 | 石田 次郎 | 既婚 | その他 | +----+------------+----------+----------+ 6 rows in set (0.00 sec)
内部結合(INNER JOIN) では結合に合致するものしか取得できないので、person の id「7、8」は一覧に表れてきません。
id「7」の「id_mrg」および、id「8」の「id_gnd」は NULL のため結合できません。
それでは、person の id「7、8」について一覧に表示できる様に、外部結合(LEFT JOIN) で行ってみます。
■[外部結合(LEFT JOIN)]の例
内部結合(INNER JOIN) の SQL の INNER を LEFT に替えます。
SELECT person.id, person.name, marriage.val AS '婚姻情報', gender.val AS '性別情報' FROM person LEFT JOIN marriage ON person.id_mrg = marriage.id LEFT JOIN gender ON person.id_gnd = gender.id;
この SQL の結果は以下の様になります。
+----+------------+----------+----------+ | id | name | 婚姻情報 | 性別情報 | +----+------------+----------+----------+ | 1 | 田中 太郎 | 独身 | 男性 | | 2 | 山田 一郎 | 既婚 | 男性 | | 3 | 青木 花子 | 既婚 | 女性 | | 4 | 加藤 清正 | 離婚 | 男性 | | 5 | 斎藤 優子 | 死別 | 女性 | | 6 | 石田 次郎 | 既婚 | その他 | | 7 | 伊藤 啓治 | NULL | 未回答 | | 8 | 山下 幸一 | 独身 | NULL | +----+------------+----------+----------+ 8 rows in set (0.00 sec)
「person.id = 7」の「person.id_mrg」が NULL で、 「person.id = 8」の「person.id_gnd」が NULL のため、 それぞれ連結しているテーブルに該当するレコードがないので、一覧では NULL と表示されます。
今回は結合の例なので JOIN を使いましたが、以下の様な SQL で内部結合と同様な結果が得られます。
SELECT person.id, person.name, marriage.val AS '婚姻情報', gender.val AS '性別情報' FROM person, marriage, gender WHERE person.id_mrg = marriage.id AND person.id_gnd = gender.id;
また、外部結合と同様な結果を取得できる以下の様な SQL もあります。 SELECT 句の中で直接それぞれに対応するテーブルと結合する方式です。
外部結合が嫌いであればこちらの方式でもよいと思います。SELECT person.id, person.name, ( SELECT val FROM marriage WHERE person.id_mrg = marriage.id ) AS '婚姻情報', ( SELECT val FROM gender WHERE person.id_gnd = gender.id ) AS '性別情報' FROM person;