[2024/01/26] MySQL 異なるデータ取得(SELECT)結果を結合して取得する方法について(UNION) (No.350)
[2024/01/25] MySQL データ取得(SELECT)のWHRE句に サブクエリ条件で ANY ALL の使い方について(ANY、SOME、ALL) (No.349)
[2024/01/24] MySQL データ取得(SELECT)の内部結合・外部結合について(INNER JOIN、LEFT JOIN) (No.348)
[2024/01/23] MySQL 「負」「ゼロ」「正」の判定を行う数学関数について(SIGN) (No.347)
[2024/01/23] MySQL 比較的使用する数値丸めを行う数学関数について(CEILING、FLOOR、ROUND、TRUNCATE) (No.346)
-
×
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
-
似た様な複数の 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行の結果セットになっています。
PR -
サブクエリとは 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;
-
MySQL では数値が「負」「ゼロ」「正」の判定を行う数学関数である SIGN 関数があります。
SIGN 関数は以下の様な引数をとります。SIGN(X) -- X : 判定する数値(X がマイナス値、ゼロ、プラス値に応じて、結果を「-1」「0」「1」として返します。)
それでは、SIGN 関数の使用例を示します。
■[SIGN関数]の例
MariaDB [(none)]> SELECT SIGN(-12), SIGN(0), SIGN(10); +-----------+---------+----------+ | SIGN(-12) | SIGN(0) | SIGN(10) | +-----------+---------+----------+ | -1 | 0 | 1 | +-----------+---------+----------+ 1 row in set (0.02 sec)
SIGN 関数の応用例として以下の例を示します。■[SIGN関数]の例
最初にテストテーブル「t_sign」を以下の様に作成します。 単に「id」と数値データ「val」を持つテーブル構造としました。
MariaDB [test]> CREATE TABLE t_sign ( -> id INT AUTO_INCREMENT NOT NULL PRIMARY KEY, -> val NUMERIC -> ); Query OK, 0 rows affected (0.19 sec) MariaDB [test]> INSERT INTO t_sign(val) VALUES(110) , (500), (300), (120), (250); Query OK, 5 rows affected (0.03 sec) Records: 5 Duplicates: 0 Warnings: 0 MariaDB [test]> SELECT * FROM t_sign; +----+------+ | id | val | +----+------+ | 1 | 110 | | 2 | 500 | | 3 | 300 | | 4 | 120 | | 5 | 250 | +----+------+ 5 rows in set (0.00 sec)
このテストテーブルを使って SIGN 関数の応用ですが、 「val」の値が指定値より小さいのか大きいのかの判定を行ってみます。
MariaDB [test]> SELECT -> CASE SIGN(val - 250) -> WHEN 1 THEN '250より大きい' -> WHEN 0 THEN '250と等しい' -> ELSE '250より小さい' -> END AS '250との比較' -> FROM t_sign; +---------------+ | 250との比較 | +---------------+ | 250より小さい | | 250より大きい | | 250より大きい | | 250より小さい | | 250と等しい | +---------------+ 5 rows in set (0.00 sec)
SIGN 関数の応用の一例でしたが、上手く使えば使い道はあると思います。
-
MySQL ではいろんな数学関数があります。三角関数(SIN, COSIN, TAN, LOG, ...)や対数に関する(LOG, ...)等がありますが、 これらは通常であればほぼ使う機会が無いと思います。 私自身も三角関数や対数は使ったことがありません。(科学計算を行うシステムであれば必要かもしれませんが)
数学関数のなかで使うとすれば、小数点以下の丸めなどを行う CEILING、FLOOR、ROUND、TRUNCATE 関数だと思います。■CEILING(最小整数値)
CEILING 関数は、与えられた数値以上で最小の整数値を返します。
CEILING 関数は以下の様な引数をとります。CEILING(X) -- X : 数値データ(小数点以下ありの正確値、浮動小数点型データなど) -- CEIL(X) はシノニム
それでは、CEILING 関数の使用例を示します。MariaDB [(none)]> SELECT -> CEILING(1.25) AS '小数以下ありの正の値' -> ,CEILING(-1.25) AS '小数以下ありの負の値' -> ,CEILING('1.25') AS '文字列の小数以下ありの正の値' -> ,CEILING('-1.25') AS '文字列の小数以下ありの負の値' -> ; +----------------------+----------------------+------------------------------+------------------------------+ | 小数以下ありの正の値 | 小数以下ありの負の値 | 文字列の小数以下ありの正の値 | 文字列の小数以下ありの負の値 | +----------------------+----------------------+------------------------------+------------------------------+ | 2 | -1 | 2 | -1 | +----------------------+----------------------+------------------------------+------------------------------+ 1 row in set (0.00 sec)
確かに与えられた数値以上で最小の整数値を返してのが分かります。
また、上の例の様に与えるデータは文字列でも同様の結果が得られます。
■FLOOR(最大整数値)
FLOOR 関数は、与えられた数値以下で最大の整数値を返します。
FLOOR 関数は以下の様な引数をとります。FLOOR(X) -- X : 数値データ(小数点以下ありの正確値、浮動小数点型データなど)
それでは、FLOOR 関数の使用例を示します。MariaDB [(none)]> SELECT -> FLOOR(1.25) AS '小数以下ありの正の値' -> ,FLOOR(-1.25) AS '小数以下ありの負の値' -> ,FLOOR('1.25') AS '文字列の小数以下ありの正の値' -> ,FLOOR('-1.25') AS '文字列の小数以下ありの負の値' -> ; +----------------------+----------------------+------------------------------+------------------------------+ | 小数以下ありの正の値 | 小数以下ありの負の値 | 文字列の小数以下ありの正の値 | 文字列の小数以下ありの負の値 | +----------------------+----------------------+------------------------------+------------------------------+ | 1 | -2 | 1 | -2 | +----------------------+----------------------+------------------------------+------------------------------+ 1 row in set (0.00 sec)
確かに与えられた数値以下で最大の整数値を返してのが分かります。 (「-1.25」の場合は「-1」の1個更にマイナスで一番近い値は「-2」なので結果「-2」となります。)
また、上の例の様に与えるデータは文字列でも同様の結果が得られます。
■ROUND(丸め処理)
ROUND 関数は、与えられた数値の指定小数点位置で丸め処理を行い値を返します。
ROUND 関数は以下の様な引数をとります。ROUND(X, DP) ROUND(X) -- X : 数値データ(小数点以下ありの正確値、浮動小数点型データなど) -- DP: 小数点位置(ROUND(X) は ROUND(X, 0) と同様) (DP がマイナスの場合は小数点から左側の位の大きい方を「0」にする)
それでは、ROUND 関数の使用例を示します。MariaDB [(none)]> SELECT -> ROUND(-1.25) -> ,ROUND(-1.51) -> ,ROUND(1.25) -> ,ROUND(1.51) -> ,ROUND(-1.251, 2) -> ,ROUND(-1.515, 2) -> ,ROUND(1.251, 2) -> ,ROUND(1.515, 2) -> ; +--------------+--------------+-------------+-------------+------------------+------------------+-----------------+-----------------+ | ROUND(-1.25) | ROUND(-1.51) | ROUND(1.25) | ROUND(1.51) | ROUND(-1.251, 2) | ROUND(-1.515, 2) | ROUND(1.251, 2) | ROUND(1.515, 2) | +--------------+--------------+-------------+-------------+------------------+------------------+-----------------+-----------------+ | -1 | -2 | 1 | 2 | -1.25 | -1.52 | 1.25 | 1.52 | +--------------+--------------+-------------+-------------+------------------+------------------+-----------------+-----------------+ 1 row in set (0.00 sec)
確かに与えられた数値を指定小数点位置で丸め処理がされた値を返してのが分かります。 (数値がマイナスの場合は絶対値で丸め処理を行い、その値にマイナスをしている様な感じです。)
また、上の例の様に与えるデータは文字列でも同様の結果が得られます。MariaDB [(none)]> SELECT -> ROUND('-1.25') -> ,ROUND('-1.51') -> ,ROUND('1.25') -> ,ROUND('1.51') -> ,ROUND('-1.251', 2) -> ,ROUND('-1.515', 2) -> ,ROUND('1.251', 2) -> ,ROUND('1.515', 2) -> ; +----------------+----------------+---------------+---------------+--------------------+--------------------+-------------------+-------------------+ | ROUND('-1.25') | ROUND('-1.51') | ROUND('1.25') | ROUND('1.51') | ROUND('-1.251', 2) | ROUND('-1.515', 2) | ROUND('1.251', 2) | ROUND('1.515', 2) | +----------------+----------------+---------------+---------------+--------------------+--------------------+-------------------+-------------------+ | -1 | -2 | 1 | 2 | -1.25 | -1.52 | 1.25 | 1.52 | +----------------+----------------+---------------+---------------+--------------------+--------------------+-------------------+-------------------+ 1 row in set (0.00 sec)
小数点位置がマイナスの場合の例を示します。
MariaDB [(none)]> SELECT -> ROUND(1251.23, -2) -> ,ROUND(1515.45, -2) -> ; +--------------------+--------------------+ | ROUND(1251.23, -2) | ROUND(1515.45, -2) | +--------------------+--------------------+ | 1300 | 1500 | +--------------------+--------------------+ 1 row in set (0.00 sec)
■TRUNCATE(切り捨て)
TRUNCATE 関数は、与えられた数値の指定小数点位置以下の切り捨て処理を行い値を返します。
TRUNCATE 関数は以下の様な引数をとります。TRUNCATE(X, DP) -- X : 数値データ(小数点以下ありの正確値、浮動小数点型データなど) -- DP: 小数点位置(DP がマイナスの場合は小数点から左側の位の大きい方を「0」にする)
それでは、TRUNCATE 関数の使用例を示します。MariaDB [(none)]> SELECT -> TRUNCATE(1.234, 1) -> ,TRUNCATE(1.987, 1) -> ,TRUNCATE(1.999, 0) -> ,TRUNCATE(-1.999, 1) -> ,TRUNCATE(1234, -2) -> ; +--------------------+--------------------+--------------------+---------------------+--------------------+ | TRUNCATE(1.234, 1) | TRUNCATE(1.987, 1) | TRUNCATE(1.999, 0) | TRUNCATE(-1.999, 1) | TRUNCATE(1234, -2) | +--------------------+--------------------+--------------------+---------------------+--------------------+ | 1.2 | 1.9 | 1 | -1.9 | 1200 | +--------------------+--------------------+--------------------+---------------------+--------------------+ 1 row in set (0.00 sec)
確かに与えられた数値を指定小数点位置以下で切り捨て処理がされた値を返してのが分かります。 (指定小数点位置がマイナスの場合は、小数点位置から上の位を「0」にしているのがわかります。)
また、上の例の様に与えるデータは文字列でも同様の結果が得られます。MariaDB [(none)]> SELECT -> TRUNCATE('1.234', 1) -> ,TRUNCATE('1.987', 1) -> ,TRUNCATE('1.999', 0) -> ,TRUNCATE('-1.999', 1) -> ,TRUNCATE('1234', -2) -> ; +----------------------+----------------------+----------------------+-----------------------+----------------------+ | TRUNCATE('1.234', 1) | TRUNCATE('1.987', 1) | TRUNCATE('1.999', 0) | TRUNCATE('-1.999', 1) | TRUNCATE('1234', -2) | +----------------------+----------------------+----------------------+-----------------------+----------------------+ | 1.2 | 1.9 | 1 | -1.9 | 1200 | +----------------------+----------------------+----------------------+-----------------------+----------------------+ 1 row in set (0.01 sec)